# HG changeset patch # User Sam # Date 1683049046 25200 # Node ID 886de16ffdc49ab59a8f0beb4eb033658c14a7e2 # Parent 977392dc055ff3e0a4ac67b421d4c0d8280d86dc# Parent 232290881c8775051303531285e12f00e63c337f Merge branch 'main' of github.com:saemideluxe/semicongine diff -r 232290881c87 -r 886de16ffdc4 src/semicongine/audio.nim --- a/src/semicongine/audio.nim Tue May 02 16:29:20 2023 +0700 +++ b/src/semicongine/audio.nim Tue May 02 10:37:26 2023 -0700 @@ -1,11 +1,19 @@ import std/tables import std/locks +when defined(windows): # used for setting audio thread priority on windows + import winim +when defined(linux): + import std/posix + import ./audiotypes import ./platform/audio export audiotypes +const NBUFFERS = 4 +const BUFFERSAMPLECOUNT = 2048 + type Playback = object sound: Sound @@ -23,7 +31,8 @@ level: Level device: NativeSoundDevice lock: Lock - buffer: SoundData + buffers: seq[SoundData] + currentBuffer: int proc loadSoundResource(resourcePath: string): Sound = assert false, "Not implemented yet" @@ -37,8 +46,12 @@ proc setupDevice(mixer: var Mixer) = # call this inside audio thread - mixer.buffer = newSeq[Sample](512) - mixer.device = openSoundDevice(44100, addr mixer.buffer) + var bufferaddresses: seq[ptr SoundData] + for i in 0 ..< NBUFFERS: + mixer.buffers.add newSeq[Sample](BUFFERSAMPLECOUNT) + for i in 0 ..< mixer.buffers.len: + bufferaddresses.add (addr mixer.buffers[i]) + mixer.device = openSoundDevice(44100, bufferaddresses) proc loadSound*(mixer: var Mixer, name: string, resource: string) = assert not (name in mixer.sounds) @@ -133,7 +146,7 @@ proc updateSoundBuffer(mixer: var Mixer) = # mix - for i in 0 ..< mixer.buffer.len: + for i in 0 ..< mixer.buffers[mixer.currentBuffer].len: var currentSample = (0'i16, 0'i16) mixer.lock.withLock(): for track in mixer.tracks.mvalues: @@ -153,9 +166,11 @@ stoppedSounds.add id for id in stoppedSounds: track.playing.del(id) - mixer.buffer[i] = currentSample + mixer.buffers[mixer.currentBuffer][i] = currentSample # send data to sound device - mixer.device.writeSoundData() + # mixer.device.writeSoundData((mixer.currentBuffer - 1) %% mixer.buffers.len) + mixer.device.writeSoundData(mixer.currentBuffer) + mixer.currentBuffer = (mixer.currentBuffer + 1) mod mixer.buffers.len proc destroy*(mixer: var Mixer) = @@ -167,6 +182,8 @@ var mixer* = createShared(Mixer) proc audioWorker() {.thread.} = + when defined(linux): + nice(-20) onThreadDestruction(proc() = mixer[].lock.withLock(mixer[].destroy()); freeShared(mixer)) mixer[].setupDevice() while true: @@ -176,3 +193,5 @@ mixer[] = initMixer() var audiothread: Thread[void] audiothread.createThread(audioWorker) + when defined(window): + SetThreadPriority(audiothread.handle(), THREAD_PRIORITY_TIME_CRITICAL) diff -r 232290881c87 -r 886de16ffdc4 src/semicongine/platform/windows/audio.nim --- a/src/semicongine/platform/windows/audio.nim Tue May 02 16:29:20 2023 +0700 +++ b/src/semicongine/platform/windows/audio.nim Tue May 02 10:37:26 2023 -0700 @@ -11,9 +11,9 @@ type NativeSoundDevice* = object handle: HWAVEOUT - buffer: WAVEHDR + buffers: seq[WAVEHDR] -proc openSoundDevice*(sampleRate: uint32, buffer: ptr SoundData): NativeSoundDevice = +proc openSoundDevice*(sampleRate: uint32, buffers: seq[ptr SoundData]): NativeSoundDevice = var format = WAVEFORMATEX( wFormatTag: WAVE_FORMAT_PCM, nChannels: 2, @@ -23,26 +23,24 @@ wBitsPerSample: 16, cbSize: 0, ) + checkWinMMResult waveOutOpen(addr result.handle, WAVE_MAPPER, addr format, DWORD_PTR(0), DWORD_PTR(0), CALLBACK_NULL) - checkWinMMResult waveOutOpen(addr result.handle, WAVE_MAPPER, addr format, DWORD_PTR(0), DWORD_PTR(0), CALLBACK_NULL) - result.buffer = WAVEHDR( - lpData: cast[cstring](addr buffer[][0]), - dwBufferLength: DWORD(buffer[].len * sizeof(Sample)), - dwBytesRecorded: 0, - dwUser: DWORD_PTR(0), - dwFlags: 0, - dwLoops: 1, - lpNext: nil, - reserved: DWORD_PTR(0) - ) - checkWinMMResult waveOutPrepareHeader(result.handle, addr result.buffer, UINT(sizeof(WAVEHDR))) + for i in 0 ..< buffers.len: + result.buffers.add WAVEHDR( + lpData: cast[cstring](addr buffers[i][][0]), + dwBufferLength: DWORD(buffers[i][].len * sizeof(Sample)), + dwLoops: 1, + ) + for i in 0 ..< result.buffers.len: + checkWinMMResult waveOutPrepareHeader(result.handle, addr result.buffers[i], UINT(sizeof(WAVEHDR))) + checkWinMMResult waveOutWrite(result.handle, addr result.buffers[i], UINT(sizeof(WAVEHDR))) -# add double buffering: https://stackoverflow.com/questions/49605552/double-buffered-waveoutwrite-stuttering-like-hell -proc writeSoundData*(soundDevice: var NativeSoundDevice) = - checkWinMMResult waveOutWrite(soundDevice.handle, addr soundDevice.buffer, UINT(sizeof(WAVEHDR))) - while (soundDevice.buffer.dwFlags and WHDR_DONE) != 1: +proc writeSoundData*(soundDevice: var NativeSoundDevice, buffer: int) = + while (soundDevice.buffers[buffer].dwFlags and WHDR_DONE) == 0: discard + checkWinMMResult waveOutWrite(soundDevice.handle, addr soundDevice.buffers[buffer], UINT(sizeof(WAVEHDR))) proc closeSoundDevice*(soundDevice: var NativeSoundDevice) = - discard waveOutUnprepareHeader(soundDevice.handle, addr soundDevice.buffer, UINT(sizeof(WAVEHDR))) + for i in 0 ..< soundDevice.buffers.len: + discard waveOutUnprepareHeader(soundDevice.handle, addr soundDevice.buffers[i], UINT(sizeof(WAVEHDR))) waveOutClose(soundDevice.handle) diff -r 232290881c87 -r 886de16ffdc4 tests/test_audio.nim --- a/tests/test_audio.nim Tue May 02 16:29:20 2023 +0700 +++ b/tests/test_audio.nim Tue May 02 10:37:26 2023 -0700 @@ -85,5 +85,6 @@ mixer[].stop() test2() mixer[].stop() - test3() - mixer[].stop() + while true: + test3() + mixer[].stop()