Mercurial > games > semicongine
comparison semiconginev2/old/resources/audio.nim @ 1227:4d97cfc4888b
merge
| author | sam <sam@basx.dev> |
|---|---|
| date | Wed, 17 Jul 2024 23:45:43 +0700 |
| parents | 56781cc0fc7c |
| children |
comparison
equal
deleted
inserted
replaced
| 1170:2addc5f6804f | 1227:4d97cfc4888b |
|---|---|
| 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) |
