From ed3beae3041a8d6dd819f51bd9579824f68fffd5 Mon Sep 17 00:00:00 2001 From: Alias Letterman Date: Mon, 11 Feb 2019 18:45:45 -0600 Subject: [PATCH] win32: Add a WaveOut driver. --- win32/CWaveOut.cpp | 176 ++++++++++++++++++++++++++++++++++ win32/CWaveOut.h | 43 +++++++++ win32/snes9xw.vcxproj | 2 + win32/snes9xw.vcxproj.filters | 6 ++ 4 files changed, 227 insertions(+) create mode 100644 win32/CWaveOut.cpp create mode 100644 win32/CWaveOut.h diff --git a/win32/CWaveOut.cpp b/win32/CWaveOut.cpp new file mode 100644 index 00000000..a2bc386a --- /dev/null +++ b/win32/CWaveOut.cpp @@ -0,0 +1,176 @@ +/*****************************************************************************\ + Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. + This file is licensed under the Snes9x License. + For further information, consult the LICENSE file in the root directory. +\*****************************************************************************/ + +#include "CWaveOut.h" +#include "../snes9x.h" +#include "../apu/apu.h" +#include "wsnes9x.h" + +CWaveOut::CWaveOut(void) +{ + hWaveOut = NULL; + initDone = false; +} + +CWaveOut::~CWaveOut(void) +{ + DeInitSoundOutput(); +} + +void CALLBACK WaveCallback(HWAVEOUT hWave, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) +{ + if (uMsg == WOM_DONE) + { + InterlockedDecrement(((volatile LONG *)dwUser)); + SetEvent(GUI.SoundSyncEvent); + } +} + +bool CWaveOut::SetupSound() +{ + WAVEFORMATEX wfx; + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 2; + wfx.nSamplesPerSec = Settings.SoundPlaybackRate; + wfx.nBlockAlign = 2 * 2; + wfx.wBitsPerSample = 16; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + wfx.cbSize = 0; + + waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, (DWORD_PTR)WaveCallback, (DWORD_PTR)&bufferCount, CALLBACK_FUNCTION); + + UINT32 blockTime = GUI.SoundBufferSize / blockCount; + singleBufferSamples = (Settings.SoundPlaybackRate * blockTime) / 1000; + singleBufferSamples *= 2; + singleBufferBytes = singleBufferSamples * 2; + sumBufferSize = singleBufferBytes * blockCount; + writeOffset = 0; + partialOffset = 0; + + waveHeaders.resize(blockCount); + for (auto &w : waveHeaders) + { + w.lpData = (LPSTR)LocalAlloc(LMEM_FIXED, singleBufferBytes); + w.dwBufferLength = singleBufferBytes; + w.dwBytesRecorded = 0; + w.dwUser = 0; + w.dwFlags = 0; + w.dwLoops = 0; + w.lpNext = 0; + w.reserved = 0; + waveOutPrepareHeader(hWaveOut, &w, sizeof(WAVEHDR)); + } + initDone = true; + + return true; +} + +void CWaveOut::SetVolume(double volume) +{ + waveOutSetVolume(hWaveOut, 0xffffffff); +} + +void CWaveOut::BeginPlayback() +{ + waveOutRestart(hWaveOut); +} + +bool CWaveOut::InitSoundOutput() +{ + return true; +} + +void CWaveOut::DeInitSoundOutput() +{ + if (!initDone) + return; + + waveOutReset(hWaveOut); + + if (!waveHeaders.empty()) + { + for (auto &w : waveHeaders) + { + waveOutUnprepareHeader(hWaveOut, &w, sizeof(WAVEHDR)); + LocalFree(w.lpData); + } + } + waveHeaders.clear(); + + waveOutClose(hWaveOut); + + initDone = false; +} + +void CWaveOut::StopPlayback() +{ + waveOutPause(hWaveOut); +} + +void CWaveOut::ProcessSound() +{ + int freeBytes = ((blockCount - bufferCount) * singleBufferBytes) - partialOffset; + + if (Settings.DynamicRateControl) + { + S9xUpdateDynamicRate(freeBytes, sumBufferSize); + } + + UINT32 availableSamples; + + availableSamples = S9xGetSampleCount(); + + if (Settings.DynamicRateControl) + { + // Using rate control, we should always keep the emulator's sound buffers empty to + // maintain an accurate measurement. + if (availableSamples > (freeBytes >> 1)) + { + S9xClearSamples(); + return; + } + } + + if (!initDone) + return; + + if (partialOffset != 0) { + UINT32 samplesleftinblock = (singleBufferBytes - partialOffset) >> 1; + BYTE *offsetBuffer = (BYTE *)waveHeaders[writeOffset].lpData + partialOffset; + + if (availableSamples <= samplesleftinblock) + { + S9xMixSamples(offsetBuffer, availableSamples); + partialOffset += availableSamples << 1; + availableSamples = 0; + } + else + { + S9xMixSamples(offsetBuffer, samplesleftinblock); + partialOffset = 0; + availableSamples -= samplesleftinblock; + waveOutWrite(hWaveOut, &waveHeaders[writeOffset], sizeof(WAVEHDR)); + InterlockedIncrement(&bufferCount); + writeOffset++; + writeOffset %= bufferCount; + } + } + + while (availableSamples >= singleBufferSamples && bufferCount < blockCount) { + BYTE *curBuffer = (BYTE *)waveHeaders[writeOffset].lpData; + S9xMixSamples(curBuffer, singleBufferSamples); + waveOutWrite(hWaveOut, &waveHeaders[writeOffset], sizeof(WAVEHDR)); + InterlockedIncrement(&bufferCount); + writeOffset++; + writeOffset %= bufferCount; + availableSamples -= singleBufferSamples; + } + + if (availableSamples > 0 && bufferCount < blockCount) { + S9xMixSamples((BYTE *)waveHeaders[writeOffset].lpData, availableSamples); + partialOffset = availableSamples << 1; + } +} \ No newline at end of file diff --git a/win32/CWaveOut.h b/win32/CWaveOut.h new file mode 100644 index 00000000..d2c7c26c --- /dev/null +++ b/win32/CWaveOut.h @@ -0,0 +1,43 @@ +/*****************************************************************************\ + Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. + This file is licensed under the Snes9x License. + For further information, consult the LICENSE file in the root directory. +\*****************************************************************************/ + +#pragma once + +#include "../snes9x.h" +#include +#include "IS9xSoundOutput.h" +#include +#include + +class CWaveOut : public IS9xSoundOutput +{ + private: + void BeginPlayback(void); + void StopPlayback(void); + void ProcessSound(void); + + HWAVEOUT hWaveOut; + bool initDone; + + volatile LONG bufferCount; + UINT32 sumBufferSize; + UINT32 singleBufferSamples; + UINT32 singleBufferBytes; + const UINT32 blockCount = 8; + UINT32 writeOffset; + UINT32 partialOffset; + std::vector waveHeaders; + + public: + CWaveOut(void); + ~CWaveOut(void); + + // Inherited from IS9xSoundOutput + bool InitSoundOutput(void); + void DeInitSoundOutput(void); + bool SetupSound(void); + void SetVolume(double volume); +}; \ No newline at end of file diff --git a/win32/snes9xw.vcxproj b/win32/snes9xw.vcxproj index d8f873d8..3ed98cbc 100644 --- a/win32/snes9xw.vcxproj +++ b/win32/snes9xw.vcxproj @@ -439,6 +439,7 @@ + @@ -582,6 +583,7 @@ + diff --git a/win32/snes9xw.vcxproj.filters b/win32/snes9xw.vcxproj.filters index a88f1cf7..4a9d124b 100644 --- a/win32/snes9xw.vcxproj.filters +++ b/win32/snes9xw.vcxproj.filters @@ -291,6 +291,9 @@ Shaders + + GUI\SoundDriver + @@ -623,6 +626,9 @@ Shaders + + GUI\SoundDriver +