Mercurial > games > semicongine
view src/semicongine/resources/audio.nim @ 681:f20965f1d7fa
add: loading *.au audio files
author | Sam <sam@basx.dev> |
---|---|
date | Sat, 13 May 2023 19:31:48 +0700 |
parents | 28e704ca739b |
children | ddfc54036e00 |
line wrap: on
line source
import std/streams import std/endians import std/math 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 func changeEndian(value: int16): int16 = var bytes: array[2, uint8] = cast[array[2, uint8]](value) swap(bytes[0], bytes[1]) result = cast[int16](bytes) 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 # Returns sound data and samplerate 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 48000 Hz but got " & $header.sampleRate & " Hz, please resample (e.g. ffmpeg -i {infile} -ar 48000 {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))