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)