Mercurial > games > semicongine
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/semiconginev2/old/resources/audio.nim Wed Jul 17 21:01:37 2024 +0700 @@ -0,0 +1,93 @@ +import std/os +import std/streams +import std/strformat +import std/endians + +import ../core/audiotypes + +type + Encoding {.size: sizeof(uint32).} = enum + # Unspecified = 0 + # Uint8Ulaw = 1 + # Int8 = 2 + Int16 = 3 + # Int24 = 4 + # Int32 = 5 + # Float32 = 6 + # Float64 = 7 + + AuHeader = object + magicNumber: uint32 + dataOffset: uint32 + dataSize: uint32 + encoding: Encoding + sampleRate: uint32 + channels: uint32 + +proc readSample(stream: Stream, encoding: Encoding, channels: int): Sample = + result[0] = stream.readint16() + swapEndian16(addr result[0], addr result[0]) + + if channels == 2: + result[1] = stream.readint16() + swapEndian16(addr result[1], addr result[1]) + else: + result[1] = result[0] + +# https://en.wikipedia.org/wiki/Au_file_format +proc ReadAU*(stream: Stream): Sound = + var header: AuHeader + + for name, value in fieldPairs(header): + var bytes: array[4, uint8] + stream.read(bytes) + swap(bytes[0], bytes[3]) + swap(bytes[1], bytes[2]) + value = cast[typeof(value)](bytes) + + assert header.magicNumber == 0x2e736e64 + if header.sampleRate != AUDIO_SAMPLE_RATE: + 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>)") + if not (header.channels in [1'u32, 2'u32]): + raise newException(Exception, "Only support mono and stereo audio at the moment (1 or 2 channels), but found " & $header.channels) + + var annotation: string + stream.read(annotation) + + result = new Sound + stream.setPosition(int(header.dataOffset)) + while not stream.atEnd(): + result[].add stream.readSample(header.encoding, int(header.channels)) + +{.compile: currentSourcePath.parentDir() & "/stb_vorbis.c".} + +proc stb_vorbis_decode_memory(mem: pointer, len: cint, channels: ptr cint, sample_rate: ptr cint, output: ptr ptr cshort): cint {.importc.} +proc free(p: pointer) {.importc.} + +proc ReadVorbis*(stream: Stream): Sound = + var + data = stream.readAll() + channels: cint + sampleRate: cint + output: ptr cshort + + var nSamples = stb_vorbis_decode_memory(addr data[0], cint(data.len), addr channels, addr sampleRate, addr output) + + if nSamples < 0: + raise newException(Exception, &"Unable to read ogg/vorbis sound file, error code: {nSamples}") + if sampleRate != AUDIO_SAMPLE_RATE: + 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>)") + + result = new Sound + if channels == 2: + result[].setLen(int(nSamples)) + copyMem(addr result[][0], output, nSamples * sizeof(Sample)) + free(output) + elif channels == 1: + for i in 0 ..< nSamples: + let value = cast[ptr UncheckedArray[int16]](output)[i] + result[].add [value, value] + free(output) + else: + free(output) + raise newException(Exception, "Only support mono and stereo audio at the moment (1 or 2 channels), but found " & $channels)