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