comparison semiconginev2/audio/resources.nim @ 1225:27cd1c21290e compiletime-tests

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