From 23dc374917acd0d7100c6e9db7ae5333338cee28 Mon Sep 17 00:00:00 2001 From: Wohlstand Date: Thu, 22 Aug 2024 10:19:38 +0300 Subject: WinMM: Added chan-alloc and audio output options --- utils/winmm_drv/src/MidiSynth.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'utils/winmm_drv/src') diff --git a/utils/winmm_drv/src/MidiSynth.cpp b/utils/winmm_drv/src/MidiSynth.cpp index 29cbb78..b1baa13 100644 --- a/utils/winmm_drv/src/MidiSynth.cpp +++ b/utils/winmm_drv/src/MidiSynth.cpp @@ -133,7 +133,7 @@ private: bool stopProcessing; public: - int Init(Bit16s *buffer, unsigned int bufferSize, unsigned int chunkSize, bool useRingBuffer, unsigned int sampleRate) + int Init(Bit16s *buffer, unsigned int bufferSize, unsigned int chunkSize, bool useRingBuffer, unsigned int sampleRate, UINT outDevice) { DWORD callbackType = CALLBACK_NULL; DWORD_PTR callback = (DWORD_PTR)NULL; @@ -148,7 +148,7 @@ public: PCMWAVEFORMAT wFormat = {WAVE_FORMAT_PCM, 2, sampleRate, sampleRate * 4, 4, 16}; // Open waveout device - int wResult = waveOutOpen(&hWaveOut, WAVE_MAPPER, (LPWAVEFORMATEX)&wFormat, callback, (DWORD_PTR)&midiSynth, callbackType); + int wResult = waveOutOpen(&hWaveOut, outDevice, (LPWAVEFORMATEX)&wFormat, callback, (DWORD_PTR)&midiSynth, callbackType); if(wResult != MMSYSERR_NOERROR) { MessageBoxW(NULL, L"Failed to open waveform output device", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION); @@ -308,17 +308,21 @@ void WaveOutWin32::RenderingThread(void *) while(!s_waveOut.stopProcessing) { bool allBuffersRendered = true; + for(UINT i = 0; i < s_waveOut.chunks; i++) { if(s_waveOut.WaveHdr[i].dwFlags & WHDR_DONE) { allBuffersRendered = false; midiSynth.Render((Bit16s *)s_waveOut.WaveHdr[i].lpData, s_waveOut.WaveHdr[i].dwBufferLength / 4); + if(waveOutWrite(s_waveOut.hWaveOut, &s_waveOut.WaveHdr[i], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) MessageBoxW(NULL, L"Failed to write block to device", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION); + midiSynth.CheckForSignals(); } } + if(allBuffersRendered) WaitForSingleObject(s_waveOut.hEvent, INFINITE); } @@ -518,8 +522,12 @@ int MidiSynth::Init() m_setupInit = false; LoadSynthSetup(); - UINT wResult = s_waveOut.Init(buffer, bufferSize, chunkSize, useRingBuffer, sampleRate); - if(wResult) return wResult; + UINT wResult = s_waveOut.Init(buffer, bufferSize, chunkSize, useRingBuffer, sampleRate, m_setup.outputDevice); + + if(wResult) + return wResult; + + m_setupCurrent.outputDevice = m_setup.outputDevice; // Start playing stream adl_generate(synth, bufferSize * 2, buffer); @@ -664,6 +672,12 @@ void MidiSynth::LoadSynthSetup() m_setupCurrent.volumeModel = m_setup.volumeModel; } + if(!m_setupInit || m_setupCurrent.chanAlloc != m_setup.chanAlloc) + { + adl_setChannelAllocMode(synth, m_setup.chanAlloc); + m_setupCurrent.chanAlloc = m_setup.chanAlloc; + } + if(!m_setupInit || m_setupCurrent.numChips != m_setup.numChips) { adl_setNumChips(synth, m_setup.numChips); -- cgit v1.2.3 From c68dc6970d5b4abab13e138ac977f7212ef32e4a Mon Sep 17 00:00:00 2001 From: Wohlstand Date: Thu, 22 Aug 2024 11:26:10 +0300 Subject: WinMM: Use the Float32 for WaveOut --- utils/winmm_drv/src/MidiSynth.cpp | 53 +++++++++++++++++++++++++++++---------- utils/winmm_drv/src/MidiSynth.h | 5 ++-- 2 files changed, 43 insertions(+), 15 deletions(-) (limited to 'utils/winmm_drv/src') diff --git a/utils/winmm_drv/src/MidiSynth.cpp b/utils/winmm_drv/src/MidiSynth.cpp index b1baa13..a03ee42 100644 --- a/utils/winmm_drv/src/MidiSynth.cpp +++ b/utils/winmm_drv/src/MidiSynth.cpp @@ -23,6 +23,8 @@ namespace OPL3Emu static MidiSynth &midiSynth = MidiSynth::getInstance(); +static const unsigned int s_audioChannels = 2; + static class MidiStream { private: @@ -133,10 +135,13 @@ private: bool stopProcessing; public: - int Init(Bit16s *buffer, unsigned int bufferSize, unsigned int chunkSize, bool useRingBuffer, unsigned int sampleRate, UINT outDevice) + int Init(float *buffer, unsigned int bufferSize, + unsigned int chunkSize, bool useRingBuffer, + unsigned int sampleRate, UINT outDevice) { DWORD callbackType = CALLBACK_NULL; DWORD_PTR callback = (DWORD_PTR)NULL; + hEvent = NULL; if(!useRingBuffer) { @@ -145,10 +150,18 @@ public: callbackType = CALLBACK_EVENT; } - PCMWAVEFORMAT wFormat = {WAVE_FORMAT_PCM, 2, sampleRate, sampleRate * 4, 4, 16}; + PCMWAVEFORMAT wFormat = + { + WAVE_FORMAT_IEEE_FLOAT, s_audioChannels, + sampleRate, + (DWORD)(sampleRate * sizeof(float) * s_audioChannels), + s_audioChannels * sizeof(float), + 8 * sizeof(float) + }; // Open waveout device - int wResult = waveOutOpen(&hWaveOut, outDevice, (LPWAVEFORMATEX)&wFormat, callback, (DWORD_PTR)&midiSynth, callbackType); + int wResult = waveOutOpen(&hWaveOut, outDevice, (LPWAVEFORMATEX)&wFormat, + callback, (DWORD_PTR)&midiSynth, callbackType); if(wResult != MMSYSERR_NOERROR) { MessageBoxW(NULL, L"Failed to open waveform output device", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION); @@ -158,13 +171,15 @@ public: // Prepare headers chunks = useRingBuffer ? 1 : bufferSize / chunkSize; WaveHdr = new WAVEHDR[chunks]; + LPSTR chunkStart = (LPSTR)buffer; - DWORD chunkBytes = 4 * chunkSize; + DWORD chunkBytes = s_audioChannels * sizeof(float) * chunkSize; + for(UINT i = 0; i < chunks; i++) { if(useRingBuffer) { - WaveHdr[i].dwBufferLength = 4 * bufferSize; + WaveHdr[i].dwBufferLength = s_audioChannels * sizeof(float) * bufferSize; WaveHdr[i].lpData = chunkStart; WaveHdr[i].dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP; WaveHdr[i].dwLoops = -1L; @@ -237,7 +252,7 @@ public: return 4; } } - _beginthread(RenderingThread, 16384, this); + _beginthread(RenderingThread, 16384 * 2, this); return 0; } @@ -288,6 +303,7 @@ public: std::cout << "OPL3: GetPos() wrap: " << delta << "\n"; ++getPosWraps; } + prevPlayPos = mmTime.u.sample; return mmTime.u.sample + getPosWraps * (1 << 27); } @@ -314,7 +330,8 @@ void WaveOutWin32::RenderingThread(void *) if(s_waveOut.WaveHdr[i].dwFlags & WHDR_DONE) { allBuffersRendered = false; - midiSynth.Render((Bit16s *)s_waveOut.WaveHdr[i].lpData, s_waveOut.WaveHdr[i].dwBufferLength / 4); + midiSynth.Render((float *)s_waveOut.WaveHdr[i].lpData, + s_waveOut.WaveHdr[i].dwBufferLength / (sizeof(float) * 2)); if(waveOutWrite(s_waveOut.hWaveOut, &s_waveOut.WaveHdr[i], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) MessageBoxW(NULL, L"Failed to write block to device", L"libADLMIDI", MB_OK | MB_ICONEXCLAMATION); @@ -373,12 +390,12 @@ void MidiSynth::RenderAvailableSpace() return; } } - midiSynth.Render(buffer + 2 * framesRendered, framesToRender); + midiSynth.Render(buffer + sizeof(float) * framesRendered, framesToRender); } // Renders totalFrames frames starting from bufpos // The number of frames rendered is added to the global counter framesRendered -void MidiSynth::Render(Bit16s *bufpos, DWORD totalFrames) +void MidiSynth::Render(float *bufpos, DWORD totalFrames) { while(totalFrames > 0) { @@ -444,10 +461,12 @@ void MidiSynth::Render(Bit16s *bufpos, DWORD totalFrames) } synthEvent.Wait(); - adl_generate(synth, framesToRender * 2, bufpos); + adl_generateFormat(synth, framesToRender * s_audioChannels, + (ADL_UInt8*)bufpos, (ADL_UInt8*)bufpos + synthAudioFormat.containerSize, + &synthAudioFormat); synthEvent.Release(); framesRendered += framesToRender; - bufpos += 2 * framesToRender; // each frame consists of two samples for both the Left and Right channels + bufpos += s_audioChannels * framesToRender; // each frame consists of two samples for both the Left and Right channels totalFrames -= framesToRender; } @@ -494,6 +513,7 @@ void MidiSynth::LoadSettings() chunkSize = MillisToFrames(10); midiLatency = MillisToFrames(0); useRingBuffer = false; + if(!useRingBuffer) { // Number of chunks should be ceil(bufferSize / chunkSize) @@ -506,7 +526,7 @@ void MidiSynth::LoadSettings() int MidiSynth::Init() { LoadSettings(); - buffer = new Bit16s[2 * bufferSize]; // each frame consists of two samples for both the Left and Right channels + buffer = new float[s_audioChannels * bufferSize]; // each frame consists of two samples for both the Left and Right channels // Init synth if(synthEvent.Init()) @@ -519,6 +539,10 @@ int MidiSynth::Init() return 1; } + synthAudioFormat.type = ADLMIDI_SampleType_F32; + synthAudioFormat.sampleOffset = s_audioChannels * sizeof(float); + synthAudioFormat.containerSize = sizeof(float); + m_setupInit = false; LoadSynthSetup(); @@ -530,7 +554,10 @@ int MidiSynth::Init() m_setupCurrent.outputDevice = m_setup.outputDevice; // Start playing stream - adl_generate(synth, bufferSize * 2, buffer); + adl_generateFormat(synth, bufferSize * s_audioChannels, + (ADL_UInt8*)buffer, + (ADL_UInt8*)buffer + synthAudioFormat.containerSize, + &synthAudioFormat); framesRendered = 0; wResult = s_waveOut.Start(); diff --git a/utils/winmm_drv/src/MidiSynth.h b/utils/winmm_drv/src/MidiSynth.h index bf2da02..a590a77 100644 --- a/utils/winmm_drv/src/MidiSynth.h +++ b/utils/winmm_drv/src/MidiSynth.h @@ -53,10 +53,11 @@ private: Bit8u reverbTime; Bit8u reverbLevel; - Bit16s *buffer; + float *buffer; DWORD framesRendered; ADL_MIDIPlayer *synth; + ADLMIDI_AudioFormat synthAudioFormat; bool m_setupInit; DriverSettings m_setup; @@ -76,7 +77,7 @@ public: void ResetSynth(); void PanicSynth(); void RenderAvailableSpace(); - void Render(Bit16s *bufpos, DWORD totalFrames); + void Render(float *bufpos, DWORD totalFrames); void CheckForSignals(); void PushMIDI(DWORD msg); void PlaySysex(Bit8u *bufpos, DWORD len); -- cgit v1.2.3 From b00d3f8d933fc7de26b954bedf841cb484139562 Mon Sep 17 00:00:00 2001 From: Wohlstand Date: Thu, 22 Aug 2024 18:26:45 +0300 Subject: WinMM: Added gaining function --- utils/winmm_drv/src/MidiSynth.cpp | 39 +++++++++++++++++++++++++++++++++++++-- utils/winmm_drv/src/MidiSynth.h | 7 +++++++ utils/winmm_drv/src/winmm_drv.cpp | 21 ++++++++++++++++----- 3 files changed, 60 insertions(+), 7 deletions(-) (limited to 'utils/winmm_drv/src') diff --git a/utils/winmm_drv/src/MidiSynth.cpp b/utils/winmm_drv/src/MidiSynth.cpp index a03ee42..e559a4f 100644 --- a/utils/winmm_drv/src/MidiSynth.cpp +++ b/utils/winmm_drv/src/MidiSynth.cpp @@ -464,6 +464,16 @@ void MidiSynth::Render(float *bufpos, DWORD totalFrames) adl_generateFormat(synth, framesToRender * s_audioChannels, (ADL_UInt8*)bufpos, (ADL_UInt8*)bufpos + synthAudioFormat.containerSize, &synthAudioFormat); + + // Apply the volume + float g_l = volumeFactorL * gain; + float g_r = volumeFactorR * gain; + for(size_t i = 0; i < framesToRender * s_audioChannels; i += s_audioChannels) + { + bufpos[i + 0] *= g_l; + bufpos[i + 1] *= g_r; + } + synthEvent.Release(); framesRendered += framesToRender; bufpos += s_audioChannels * framesToRender; // each frame consists of two samples for both the Left and Right channels @@ -484,15 +494,19 @@ void MidiSynth::CheckForSignals() switch(cmd) { - case 1: // Reload settings on the fly + case DRV_SIGNAL_RELOAD_SETUP: // Reload settings on the fly this->loadSetup(); LoadSynthSetup(); break; - case 2: + case DRV_SIGNAL_RESET_SYNTH: adl_reset(synth); break; + case DRV_SIGNAL_UPDATE_GAIN: + this->loadGain(); + break; + default: break; } @@ -513,6 +527,9 @@ void MidiSynth::LoadSettings() chunkSize = MillisToFrames(10); midiLatency = MillisToFrames(0); useRingBuffer = false; + volumeFactorL = 1.0f; + volumeFactorR = 1.0f; + gain = 1.0f; if(!useRingBuffer) { @@ -621,9 +638,27 @@ void MidiSynth::PlaySysex(Bit8u *bufpos, DWORD len) synthEvent.Release(); } +void MidiSynth::SetVolume(DWORD vol) +{ + volumeFactorR = (float)0xFFFF / HIWORD(vol); + volumeFactorL = (float)0xFFFF / LOWORD(vol); +} + +DWORD MidiSynth::GetVolume() +{ + return MAKELONG((DWORD)(0xFFFF * volumeFactorL), (DWORD)(0xFFFF * volumeFactorR)); +} + void MidiSynth::loadSetup() { ::loadSetup(&m_setup); + gain = (float)m_setup.gain100 / 100.f; +} + +void MidiSynth::loadGain() +{ + ::getGain(&m_setup); + gain = (float)m_setup.gain100 / 100.f; } void MidiSynth::LoadSynthSetup() diff --git a/utils/winmm_drv/src/MidiSynth.h b/utils/winmm_drv/src/MidiSynth.h index a590a77..0442f7b 100644 --- a/utils/winmm_drv/src/MidiSynth.h +++ b/utils/winmm_drv/src/MidiSynth.h @@ -52,6 +52,9 @@ private: Bit8u reverbMode; Bit8u reverbTime; Bit8u reverbLevel; + float volumeFactorL; + float volumeFactorR; + float gain; float *buffer; DWORD framesRendered; @@ -82,7 +85,11 @@ public: void PushMIDI(DWORD msg); void PlaySysex(Bit8u *bufpos, DWORD len); + void SetVolume(DWORD vol); + DWORD GetVolume(); + void loadSetup(); + void loadGain(); void LoadSynthSetup(); }; diff --git a/utils/winmm_drv/src/winmm_drv.cpp b/utils/winmm_drv/src/winmm_drv.cpp index 7f65dda..9baa56a 100644 --- a/utils/winmm_drv/src/winmm_drv.cpp +++ b/utils/winmm_drv/src/winmm_drv.cpp @@ -201,6 +201,7 @@ EXTERN_C HRESULT modGetCaps(PVOID capsPtr, DWORD capsSize) CHAR synthName[] = "libADLMIDI synth\0"; WCHAR synthNameW[] = L"libADLMIDI synth\0"; + DWORD support = MIDICAPS_VOLUME | MIDICAPS_LRVOLUME; switch(capsSize) { @@ -214,7 +215,7 @@ EXTERN_C HRESULT modGetCaps(PVOID capsPtr, DWORD capsSize) myCapsA->wVoices = 0; myCapsA->wNotes = 0; myCapsA->wChannelMask = 0xffff; - myCapsA->dwSupport = 0; + myCapsA->dwSupport = support; return MMSYSERR_NOERROR; case(sizeof(MIDIOUTCAPSW)): @@ -227,7 +228,7 @@ EXTERN_C HRESULT modGetCaps(PVOID capsPtr, DWORD capsSize) myCapsW->wVoices = 0; myCapsW->wNotes = 0; myCapsW->wChannelMask = 0xffff; - myCapsW->dwSupport = 0; + myCapsW->dwSupport = support; return MMSYSERR_NOERROR; case(sizeof(MIDIOUTCAPS2A)): @@ -240,7 +241,7 @@ EXTERN_C HRESULT modGetCaps(PVOID capsPtr, DWORD capsSize) myCaps2A->wVoices = 0; myCaps2A->wNotes = 0; myCaps2A->wChannelMask = 0xffff; - myCaps2A->dwSupport = 0; + myCaps2A->dwSupport = support; return MMSYSERR_NOERROR; case(sizeof(MIDIOUTCAPS2W)): @@ -253,7 +254,7 @@ EXTERN_C HRESULT modGetCaps(PVOID capsPtr, DWORD capsSize) myCaps2W->wVoices = 0; myCaps2W->wNotes = 0; myCaps2W->wChannelMask = 0xffff; - myCaps2W->dwSupport = 0; + myCaps2W->dwSupport = support; return MMSYSERR_NOERROR; default: @@ -316,7 +317,8 @@ LONG CloseDriver(Driver *driver, UINT uDeviceID, UINT uMsg, DWORD_PTR dwUser, DW return MMSYSERR_NOERROR; } -EXTERN_C DWORD __declspec(dllexport) __stdcall modMessage(DWORD uDeviceID, DWORD uMsg, DWORD_PTR dwUser, DWORD_PTR dwParam1, DWORD_PTR dwParam2) +EXTERN_C DWORD __declspec(dllexport) __stdcall +modMessage(DWORD uDeviceID, DWORD uMsg, DWORD_PTR dwUser, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { MIDIHDR *midiHdr; Driver *driver = &drivers[uDeviceID]; @@ -377,6 +379,15 @@ EXTERN_C DWORD __declspec(dllexport) __stdcall modMessage(DWORD uDeviceID, DWORD DoCallback(uDeviceID, dwUser, MOM_DONE, dwParam1, (DWORD_PTR)NULL); return MMSYSERR_NOERROR; + case MODM_GETVOLUME: + if(dwParam1) + *(DWORD *)dwParam1 = midiSynth.GetVolume(); + return MMSYSERR_NOERROR; + + case MODM_SETVOLUME: + midiSynth.SetVolume(dwParam1); + return MMSYSERR_NOERROR; + case MODM_GETNUMDEVS: return 0x1; -- cgit v1.2.3 From 0a95f6178f4cf1e3f27ef215865455266d43483d Mon Sep 17 00:00:00 2001 From: Wohlstand Date: Thu, 22 Aug 2024 20:25:00 +0300 Subject: WinMM: Fixed the MinGW workaround --- utils/winmm_drv/src/winmm_drv.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'utils/winmm_drv/src') diff --git a/utils/winmm_drv/src/winmm_drv.cpp b/utils/winmm_drv/src/winmm_drv.cpp index 9baa56a..09f38d2 100644 --- a/utils/winmm_drv/src/winmm_drv.cpp +++ b/utils/winmm_drv/src/winmm_drv.cpp @@ -266,8 +266,12 @@ void DoCallback(int driverNum, DWORD_PTR clientNum, DWORD msg, DWORD_PTR param1, { Driver::Client *client = &drivers[driverNum].clients[clientNum]; #ifdef __MINGW32__ - if(s_DriverCallback) + if(!s_DriverCallback) + { initWorkarounds(); + if(!s_DriverCallback) + return; // Ouch! + } #endif DriverCallback(client->callback, client->flags, drivers[driverNum].hdrvr, msg, client->instance, param1, param2); } -- cgit v1.2.3