Mercurial > games > semicongine
comparison semiconginev2/old/resources/audio.nim @ 1218:56781cc0fc7c compiletime-tests
did: renamge main package
author | sam <sam@basx.dev> |
---|---|
date | Wed, 17 Jul 2024 21:01:37 +0700 |
parents | semicongine/old/resources/audio.nim@a3eb305bcac2 |
children |
comparison
equal
deleted
inserted
replaced
1217:f819a874058f | 1218:56781cc0fc7c |
---|---|
1 import std/os | |
2 import std/streams | |
3 import std/strformat | |
4 import std/endians | |
5 | |
6 import ../core/audiotypes | |
7 | |
8 type | |
9 Encoding {.size: sizeof(uint32).} = enum | |
10 # Unspecified = 0 | |
11 # Uint8Ulaw = 1 | |
12 # Int8 = 2 | |
13 Int16 = 3 | |
14 # Int24 = 4 | |
15 # Int32 = 5 | |
16 # Float32 = 6 | |
17 # Float64 = 7 | |
18 | |
19 AuHeader = object | |
20 magicNumber: uint32 | |
21 dataOffset: uint32 | |
22 dataSize: uint32 | |
23 encoding: Encoding | |
24 sampleRate: uint32 | |
25 channels: uint32 | |
26 | |
27 proc readSample(stream: Stream, encoding: Encoding, channels: int): Sample = | |
28 result[0] = stream.readint16() | |
29 swapEndian16(addr result[0], addr result[0]) | |
30 | |
31 if channels == 2: | |
32 result[1] = stream.readint16() | |
33 swapEndian16(addr result[1], addr result[1]) | |
34 else: | |
35 result[1] = result[0] | |
36 | |
37 # https://en.wikipedia.org/wiki/Au_file_format | |
38 proc ReadAU*(stream: Stream): Sound = | |
39 var header: AuHeader | |
40 | |
41 for name, value in fieldPairs(header): | |
42 var bytes: array[4, uint8] | |
43 stream.read(bytes) | |
44 swap(bytes[0], bytes[3]) | |
45 swap(bytes[1], bytes[2]) | |
46 value = cast[typeof(value)](bytes) | |
47 | |
48 assert header.magicNumber == 0x2e736e64 | |
49 if header.sampleRate != AUDIO_SAMPLE_RATE: | |
50 raise newException(Exception, &"Only support sample rate of {AUDIO_SAMPLE_RATE} Hz but got {header.sampleRate} Hz, please resample (e.g. ffmpeg -i <infile> -ar {AUDIO_SAMPLE_RATE} <outfile>)") | |
51 if not (header.channels in [1'u32, 2'u32]): | |
52 raise newException(Exception, "Only support mono and stereo audio at the moment (1 or 2 channels), but found " & $header.channels) | |
53 | |
54 var annotation: string | |
55 stream.read(annotation) | |
56 | |
57 result = new Sound | |
58 stream.setPosition(int(header.dataOffset)) | |
59 while not stream.atEnd(): | |
60 result[].add stream.readSample(header.encoding, int(header.channels)) | |
61 | |
62 {.compile: currentSourcePath.parentDir() & "/stb_vorbis.c".} | |
63 | |
64 proc stb_vorbis_decode_memory(mem: pointer, len: cint, channels: ptr cint, sample_rate: ptr cint, output: ptr ptr cshort): cint {.importc.} | |
65 proc free(p: pointer) {.importc.} | |
66 | |
67 proc ReadVorbis*(stream: Stream): Sound = | |
68 var | |
69 data = stream.readAll() | |
70 channels: cint | |
71 sampleRate: cint | |
72 output: ptr cshort | |
73 | |
74 var nSamples = stb_vorbis_decode_memory(addr data[0], cint(data.len), addr channels, addr sampleRate, addr output) | |
75 | |
76 if nSamples < 0: | |
77 raise newException(Exception, &"Unable to read ogg/vorbis sound file, error code: {nSamples}") | |
78 if sampleRate != AUDIO_SAMPLE_RATE: | |
79 raise newException(Exception, &"Only support sample rate of {AUDIO_SAMPLE_RATE} Hz but got {sampleRate} Hz, please resample (e.g. ffmpeg -i <infile> -acodec libvorbis -ar {AUDIO_SAMPLE_RATE} <outfile>)") | |
80 | |
81 result = new Sound | |
82 if channels == 2: | |
83 result[].setLen(int(nSamples)) | |
84 copyMem(addr result[][0], output, nSamples * sizeof(Sample)) | |
85 free(output) | |
86 elif channels == 1: | |
87 for i in 0 ..< nSamples: | |
88 let value = cast[ptr UncheckedArray[int16]](output)[i] | |
89 result[].add [value, value] | |
90 free(output) | |
91 else: | |
92 free(output) | |
93 raise newException(Exception, "Only support mono and stereo audio at the moment (1 or 2 channels), but found " & $channels) |