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