diff --git a/Doc/FuncStats.xlsx b/Doc/FuncStats.xlsx index 880365d..78ee3f0 100644 Binary files a/Doc/FuncStats.xlsx and b/Doc/FuncStats.xlsx differ diff --git a/SpaceCadetPinball/Sound.cpp b/SpaceCadetPinball/Sound.cpp index 6097d31..ae1456f 100644 --- a/SpaceCadetPinball/Sound.cpp +++ b/SpaceCadetPinball/Sound.cpp @@ -143,16 +143,6 @@ void Sound::Close() } } -int Sound::SubFactor(int a1, int a2) -{ - return a1 - a2; -} - -int Sound::AddFactor(int a1, int a2) -{ - return a1 + a2; -} - void Sound::PlaySound(MIXWAVE* wavePtr, int minChannel, int maxChannel, unsigned int dwFlags, __int16 loops) { MIXPLAYPARAMS mixParams{}; diff --git a/SpaceCadetPinball/Sound.h b/SpaceCadetPinball/Sound.h index 5eea4dd..ec427ff 100644 --- a/SpaceCadetPinball/Sound.h +++ b/SpaceCadetPinball/Sound.h @@ -10,8 +10,6 @@ public: static void Activate(); static void Deactivate(); static void Close(); - static int SubFactor(int a1, int a2); - static int AddFactor(int a1, int a2); static void PlaySound(MIXWAVE* wavePtr, int minChannel, int maxChannel, unsigned int dwFlags, __int16 loops); static MIXWAVE* LoadWaveFile(LPCSTR lpName); static void FreeSound(MIXWAVE* wave); diff --git a/SpaceCadetPinball/WaveMix.cpp b/SpaceCadetPinball/WaveMix.cpp index 2c3f53c..c32ce8d 100644 --- a/SpaceCadetPinball/WaveMix.cpp +++ b/SpaceCadetPinball/WaveMix.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "WaveMix.h" +#include "pinball.h" + int WaveMix::initialized_flag; char WaveMix::FileName[276]; CHANNELNODE WaveMix::channel_nodes[MAXQUEUEDWAVES]; @@ -163,6 +165,42 @@ int WaveMix::FreeWave(HANDLE hMixSession, MIXWAVE* lpMixWave) int WaveMix::Activate(HANDLE hMixSession, bool fActivate) { + GLOBALS* globals = SessionToGlobalDataPtr(hMixSession); + Globals = globals; + if (!globals) + return 5; + if (fActivate) + { + if (GlobalsActive) + return GlobalsActive != globals ? 4 : 0; + if (globals->SettingsDialogActiveFlag) + return 12; + GlobalsActive = globals; + sndPlaySoundA(nullptr, 0); + auto result = GetWaveDevice(); + if (result) + { + GlobalsActive = nullptr; + return result; + } + Globals->fActive = 1; + SetWaveOutPosition(Globals->dwCurrentSample); + XWAVEHDR* xHDR; + do + xHDR = GetWaveBlock(); + while (MixerPlay(xHDR, 1)); + } + else + { + if (globals->fActive) + { + Globals->dwCurrentSample = MyWaveOutGetPosition(globals->hWaveOut, globals->fGoodGetPos); + } + ReleaseWaveDevice(globals); + Globals->fActive = 0; + if (Globals == GlobalsActive) + GlobalsActive = nullptr; + } return 0; } @@ -324,6 +362,108 @@ int WaveMix::ReadConfigSettings(MIXCONFIG* lpConfig) return 1; } + UINT deviceId; + if (static_cast(lpConfig->dwFlags) >= 0) + { + deviceId = GetPrivateProfileIntA("general", "WaveOutDevice", 0, FileName); + } + else + { + deviceId = lpConfig->wDeviceID; + } + Globals->wDeviceID = deviceId; + if (Globals->wDeviceID >= waveDeviceCount) + Globals->wDeviceID = 0; + + if (waveOutGetDevCapsA(Globals->wDeviceID, &Globals->WaveoutCaps, 0x34u) + || !RemoveInvalidIniNameCharacters(Globals->WaveoutCaps.szPname)) + { + lstrcpyA(Globals->WaveoutCaps.szPname, "Unkown Device"); + } + + if (!ReadRegistryToGetMachineSpecificInfSection(Globals->wDeviceID, Globals->szDevicePName, 96)) + { + lstrcpyA(Globals->szDevicePName, GetOperatingSystemPrefix()); + lstrcatA(Globals->szDevicePName, Globals->WaveoutCaps.szPname); + } + + auto remix = GetPrivateProfileIntA("default", "Remix", 1, FileName); + auto goodWavePos = GetPrivateProfileIntA("default", "GoodWavePos", DefaultGoodWavePos(Globals->wDeviceID), + FileName); + auto waveBlocks = GetPrivateProfileIntA("default", "WaveBlocks", 3, FileName); + auto samplesPerSec = GetPrivateProfileIntA("default", "SamplesPerSec", 11, FileName); + if ((Globals->WaveoutCaps.dwSupport & 0x10) != 0) + { + if (!ShowDebugDialogs) + return 0; + wsprintfA(string_buffer, + "%s is a syncronous (blocking) wave output device. This will not permit audio to play while other applications are running.", + Globals->WaveoutCaps.szPname); + MessageBoxA(nullptr, string_buffer, "WavMix32", 0x40u); + return 0; + } + if (GetPrivateProfileIntA("not compatible", Globals->szDevicePName, 0, FileName)) + { + if (!ShowDebugDialogs) + return 0; + wsprintfA(string_buffer, "%s is not compatible with the realtime wavemixer.", Globals->szDevicePName); + MessageBoxA(nullptr, string_buffer, "WavMix32", 0x40u); + return 0; + } + + Globals->pfnRemix = GetPrivateProfileIntA(Globals->szDevicePName, "Remix", remix, FileName) != 2 + ? ResetRemix + : NoResetRemix; + Globals->fGoodGetPos = GetPrivateProfileIntA(Globals->szDevicePName, "GoodWavePos", goodWavePos, FileName) != 0; + Globals->WaveBlockCount = GetPrivateProfileIntA(Globals->szDevicePName, "WaveBlocks", waveBlocks, FileName); + if (Globals->WaveBlockCount < 2) + Globals->WaveBlockCount = 2; + else if (Globals->WaveBlockCount > 10) + Globals->WaveBlockCount = 10; + + Globals->PauseBlocks = GetPrivateProfileIntA(Globals->szDevicePName, "PauseBlocks", + DefaultPauseBlocks(Globals->WaveBlockCount), FileName); + if (Globals->PauseBlocks >= 0) + { + if (Globals->PauseBlocks > Globals->WaveBlockCount) + Globals->PauseBlocks = Globals->WaveBlockCount; + } + else + { + Globals->PauseBlocks = 0; + } + + auto samplesPerSec2 = GetPrivateProfileIntA(Globals->szDevicePName, "SamplesPerSec", samplesPerSec, FileName); + auto channels = GetPrivateProfileIntA(Globals->szDevicePName, "Channels", 1, FileName); + Globals->PCM.wf.nChannels = channels; + if (channels) + { + if (channels > 2u) + Globals->PCM.wf.nChannels = 2; + } + else + { + Globals->PCM.wf.nChannels = 1; + } + DWORD nAvgBytesPerSec; + if (samplesPerSec2 == 22) + { + Globals->PCM.wf.nSamplesPerSec = 22050; + nAvgBytesPerSec = 22050 * Globals->PCM.wf.nChannels; + } + else if (samplesPerSec2 == 44) + { + Globals->PCM.wf.nSamplesPerSec = 44100; + nAvgBytesPerSec = 44100 * Globals->PCM.wf.nChannels; + } + else + { + nAvgBytesPerSec = 11025 * Globals->PCM.wf.nChannels; + } + Globals->PCM.wf.nAvgBytesPerSec = nAvgBytesPerSec; + auto waveBlockLen2 = GetPrivateProfileIntA("default", "WaveBlockLen", 0, FileName); + Globals->dwWaveBlockLen = FigureOutDMABufferSize(waveBlockLen2, &Globals->PCM); + Configure(Globals, nullptr, lpConfig, nullptr, 0); return 1; } @@ -421,20 +561,18 @@ int WaveMix::DefaultPauseBlocks(int waveBlocks) int WaveMix::Configure(GLOBALS* hMixSession, HWND hWndParent, MIXCONFIG* lpConfig, int* flag1Ptr, int saveConfigFlag) { - MIXCONFIG mixConfigLocal; + MIXCONFIG mixConfigLocal{}; + tagWAVEOUTCAPSA pwoc{}; - auto hMixSession2 = hMixSession; auto mixConfig = lpConfig; - auto hMixSession3 = hMixSession; - auto flag1Ptr_2 = flag1Ptr; auto someFlag1 = 0; - auto globals1 = SessionToGlobalDataPtr(hMixSession); - Globals = globals1; - if (!globals1) + auto globals = SessionToGlobalDataPtr(hMixSession); + Globals = globals; + if (!globals) return 5; - if (globals1->fActive) + if (globals->fActive) return 4; - if (globals1->SettingsDialogActiveFlag) + if (globals->SettingsDialogActiveFlag) return 12; FlushChannel(hMixSession, -1, 1u); @@ -443,9 +581,169 @@ int WaveMix::Configure(GLOBALS* hMixSession, HWND hWndParent, MIXCONFIG* lpConfi mixConfigLocal.wSize = 30; mixConfigLocal.dwFlags = 1023; GetConfig(static_cast(hMixSession), &mixConfigLocal); + auto dialog = MakeSettingsDlgTemplate(); + if (!dialog) + return 1; + Globals->SettingsDialogActiveFlag = 1; + auto dlgResult = DialogBoxIndirectParamA(HModule, &dialog->Dialog, hWndParent, SettingsDlgProc, + (LPARAM)&mixConfigLocal); + DestroySettingsDlgTemplate(dialog); + Globals->SettingsDialogActiveFlag = 0; + if (dlgResult != 1) + return 1; + if (Globals->dwWaveBlockLen == mixConfigLocal.WaveBlockLen) + mixConfigLocal.dwFlags &= 0xFFFFFFF7; + + mixConfig = &mixConfigLocal; + } + if (!mixConfig->dwFlags) + return 1; + + globals = SessionToGlobalDataPtr(hMixSession); + Globals = globals; + if (!globals) + return 5; + + auto dwFlags = mixConfig->dwFlags; + if ((dwFlags & 0x100) != 0) + ShowDebugDialogs = mixConfig->ShowDebugDialogs != 0; + if ((dwFlags & 0x80u) != 0) + { + if (mixConfig->wDeviceID != globals->wDeviceID) + { + auto result = waveOutGetDevCapsA(mixConfig->wDeviceID, &pwoc, 0x34u); + if (result) + return result; + memcpy(&Globals->WaveoutCaps, &pwoc, sizeof Globals->WaveoutCaps); + Globals->wDeviceID = mixConfig->wDeviceID; + if (Globals->WaveoutCaps.wChannels == 1 && Globals->PCM.wf.nChannels == 2) + { + Globals->PCM.wf.nChannels = 1; + someFlag1 = 1; + Globals->PCM.wf.nBlockAlign = 1; + Globals->PCM.wf.nAvgBytesPerSec = Globals->PCM.wf.nSamplesPerSec; + } + lstrcpyA(globals->szDevicePName, pwoc.szPname); + if (!RemoveInvalidIniNameCharacters(Globals->szDevicePName)) + lstrcpyA(Globals->szDevicePName, "Unkown Device"); + if (!ReadRegistryToGetMachineSpecificInfSection(Globals->wDeviceID, Globals->szDevicePName, 96)) + { + lstrcpyA(Globals->szDevicePName, GetOperatingSystemPrefix()); + lstrcatA(Globals->szDevicePName, Globals->WaveoutCaps.szPname); + } + } } - return 1; + if ((dwFlags & 1) != 0) + { + if (mixConfig->wChannels <= 1u) + { + if (globals->PCM.wf.nChannels == 2) + someFlag1 = 1; + globals->PCM.wf.nChannels = 1; + globals->PCM.wf.nBlockAlign = 1; + globals->PCM.wf.nAvgBytesPerSec = globals->PCM.wf.nSamplesPerSec; + } + if (globals->WaveoutCaps.wChannels > 1u) + { + if (globals->PCM.wf.nChannels == 1) + someFlag1 = 1; + globals->PCM.wf.nChannels = 2; + globals->PCM.wf.nBlockAlign = 2; + globals->PCM.wf.nAvgBytesPerSec = 2 * globals->PCM.wf.nSamplesPerSec; + } + } + + if ((dwFlags & 2) != 0) + { + auto nAvgBytesPerSec = 0u; + if (mixConfig->wSamplingRate == 22) + { + if (globals->PCM.wf.nSamplesPerSec != 22050) + someFlag1 = 1; + globals->PCM.wf.nSamplesPerSec = 22050; + nAvgBytesPerSec = 22050 * globals->PCM.wf.nChannels; + } + else + { + if (mixConfig->wSamplingRate == 44) + { + if (globals->PCM.wf.nSamplesPerSec != 44100) + someFlag1 = 1; + globals->PCM.wf.nSamplesPerSec = 44100; + nAvgBytesPerSec = 44100 * globals->PCM.wf.nChannels; + } + else + { + if (globals->PCM.wf.nSamplesPerSec != 11025) + someFlag1 = 1; + globals->PCM.wf.nSamplesPerSec = 11025; + nAvgBytesPerSec = 11025 * globals->PCM.wf.nChannels; + } + } + globals->PCM.wf.nAvgBytesPerSec = nAvgBytesPerSec; + } + + if ((dwFlags & 4) != 0) + { + auto v24 = 2; + globals->WaveBlockCount = mixConfig->WaveBlockCount; + if (mixConfig->WaveBlockCount < 2) + globals->WaveBlockCount = 2; + else if (mixConfig->WaveBlockCount > 10) + globals->WaveBlockCount = 10; + if (globals->PauseBlocks >= 0) + { + if (globals->PauseBlocks > globals->WaveBlockCount) + globals->PauseBlocks = globals->WaveBlockCount; + } + else + { + globals->PauseBlocks = 0; + } + } + + if (someFlag1) + globals->dwWaveBlockLen = FigureOutDMABufferSize(0, &globals->PCM); + + if ((dwFlags & 8) != 0) + { + if (mixConfig->WaveBlockLen) + globals->dwWaveBlockLen = mixConfig->WaveBlockLen; + } + + if (globals->dwWaveBlockLen < 344) + globals->dwWaveBlockLen = 344; + else if (globals->dwWaveBlockLen > 11025) + globals->dwWaveBlockLen = 11025; + + auto dwFlags2 = dwFlags; + if ((dwFlags & 0x10) != 0) + globals->CmixPtr = cmixit; + + if ((dwFlags2 & 0x20) != 0) + { + globals->pfnRemix = !mixConfig->ResetMixDefaultFlag ? NoResetRemix : ResetRemix; + } + + if ((dwFlags2 & 0x40) != 0) + globals->fGoodGetPos = mixConfig->GoodWavePos != 0; + + if ((dwFlags2 & 0x200) != 0) + { + globals->PauseBlocks = mixConfig->PauseBlocks; + if (mixConfig->PauseBlocks > globals->WaveBlockCount) + globals->PauseBlocks = globals->WaveBlockCount; + } + + GetConfig(hMixSession, mixConfig); + if (flag1Ptr) + *flag1Ptr = someFlag1; + if (saveConfigFlag) + SaveConfigSettings(dwFlags); + if (ShowDebugDialogs) + ShowCurrentSettings(); + return 0; } int WaveMix::GetConfig(HANDLE hMixSession, MIXCONFIG* lpConfig) @@ -461,11 +759,11 @@ int WaveMix::GetConfig(HANDLE hMixSession, MIXCONFIG* lpConfig) if ((dwFlags & 1) != 0) lpConfig->wChannels = globals->PCM.wf.nChannels; if ((dwFlags & 2) != 0) - lpConfig->wSamplingRate = 11 * (globals->PCM.wf.nSamplesPerSec / 0x2B11); + lpConfig->wSamplingRate = static_cast(11 * (globals->PCM.wf.nSamplesPerSec / 0x2B11)); if ((dwFlags & 4) != 0) lpConfig->WaveBlockCount = globals->WaveBlockCount; if ((dwFlags & 8) != 0) - lpConfig->WaveBlockLen = globals->WaveBlockLen; + lpConfig->WaveBlockLen = static_cast(globals->dwWaveBlockLen); if ((dwFlags & 0x10) != 0) lpConfig->CmixPtrDefaultFlag = globals->CmixPtr == cmixit; if ((dwFlags & 0x20) != 0) @@ -481,14 +779,14 @@ int WaveMix::GetConfig(HANDLE hMixSession, MIXCONFIG* lpConfig) return 0; } -unsigned WaveMix::MyWaveOutGetPosition(HWAVEOUT hwo, int fGoodGetPos) +unsigned WaveMix::MyWaveOutGetPosition(HWAVEOUT hWaveOut, int fGoodGetPos) { mmtime_tag pmmt{}; if (!fGoodGetPos) return (timeGetTime() - Globals->dwBaseTime) * Globals->PCM.wf.nAvgBytesPerSec / 0x3E8 & 0xFFFFFFF8; - pmmt.wType = 4; - waveOutGetPosition(hwo, &pmmt, 0xCu); + pmmt.wType = TIME_BYTES; + waveOutGetPosition(hWaveOut, &pmmt, 0xCu); return Globals->pfnSampleAdjust(pmmt.u.ms, Globals->dwWaveOutPos); } @@ -520,6 +818,37 @@ int WaveMix::ResetRemix(DWORD dwRemixSamplePos, CHANNELNODE* channel) AddToPlayingQueue(block); } + auto dwCurrentSamplePrev = Globals->dwCurrentSample; + MyWaveOutReset(Globals->hWaveOut); + Globals->dwCurrentSample = dwCurrentSamplePrev; + auto pauseFlag = 0; + if (Globals->PauseBlocks > 0) + { + waveOutPause(Globals->hWaveOut); + pauseFlag = 1; + } + + auto pauseCount = 0; + for (auto xHDR = play_queue.first; xHDR; xHDR = xHDR->QNext) + { + if (waveOutWrite(Globals->hWaveOut, &xHDR->wh, sizeof(WAVEHDR))) + { + if (ShowDebugDialogs) + MessageBoxA(nullptr, "Failed to write block to device", "WavMix32", 0x30u); + xHDR->fAvailable = 1; + RemoveFromPlayingQueue(xHDR); + } + if (pauseFlag) + { + if (++pauseCount >= Globals->PauseBlocks) + { + waveOutRestart(Globals->hWaveOut); + pauseFlag = 0; + } + } + } + if (pauseFlag) + waveOutRestart(Globals->hWaveOut); return 1; } @@ -585,7 +914,7 @@ XWAVEHDR* WaveMix::GetWaveBlock() XWAVEHDR* result = Globals->WaveBlockArray[index]; result->fAvailable = 0; - result->wh.dwBufferLength = Globals->WaveBlockLen; + result->wh.dwBufferLength = Globals->dwWaveBlockLen; result->wh.lpData = reinterpret_cast(&result[1]); result->g = GlobalsActive; return result; @@ -629,7 +958,7 @@ int WaveMix::MixerPlay(XWAVEHDR* lpXWH, int fWriteBlocks) auto currentSample = Globals->dwCurrentSample; auto dataPtr = reinterpret_cast(lpXWH->wh.lpData); - auto waveBlockLen = Globals->WaveBlockLen; + auto waveBlockLen = Globals->dwWaveBlockLen; while (waveBlockLen) { if (currentSample >= minStartPos) @@ -666,7 +995,7 @@ int WaveMix::MixerPlay(XWAVEHDR* lpXWH, int fWriteBlocks) if (waveCount) { auto dataLength = endBlockPosition - currentSample; - Globals->CmixPtr(dataPtr, play_data, play_volume, waveCount, dataLength); + Globals->CmixPtr(dataPtr, play_data, play_volume, waveCount, static_cast(dataLength)); dataPtr += dataLength; waveBlockLen -= dataLength; @@ -713,11 +1042,11 @@ int WaveMix::MixerPlay(XWAVEHDR* lpXWH, int fWriteBlocks) } lpXWH->dwWavePos = Globals->dwCurrentSample; - Globals->dwCurrentSample += Globals->WaveBlockLen; + Globals->dwCurrentSample += Globals->dwWaveBlockLen; if (fWriteBlocks) { AddToPlayingQueue(lpXWH); - if (waveOutWrite(Globals->hWaveOut, &lpXWH->wh, 0x20u)) + if (waveOutWrite(Globals->hWaveOut, &lpXWH->wh, sizeof(WAVEHDR))) { if (ShowDebugDialogs) MessageBoxA(nullptr, "Failed to write block to device", "WavMix32", 0x30u); @@ -743,6 +1072,508 @@ XWAVEHDR* WaveMix::AddToPlayingQueue(XWAVEHDR* lpXWH) return play_queue.first; } +void WaveMix::MyWaveOutReset(HWAVEOUT hWaveOut) +{ + auto position = MyWaveOutGetPosition(hWaveOut, Globals->fGoodGetPos); + waveOutReset(hWaveOut); + SetWaveOutPosition(position); +} + +void WaveMix::SetWaveOutPosition(unsigned newPosition) +{ + DWORD position; + mmtime_tag pmmt{}; + + pmmt.wType = TIME_BYTES; + if (Globals->hWaveOut) + { + waveOutGetPosition(Globals->hWaveOut, &pmmt, 0xCu); + position = pmmt.u.ms; /*Todo: check union ms vs sample with TIME_BYTES*/ + } + else + { + position = 0; + } + + if (position < newPosition) + { + Globals->dwWaveOutPos = newPosition - position; + Globals->pfnSampleAdjust = AddFactor; + } + else + { + Globals->dwWaveOutPos = position - newPosition; + Globals->pfnSampleAdjust = SubFactor; + } + Globals->dwCurrentSample = newPosition; + Globals->dwBaseTime = timeGetTime() - 1000 * newPosition / Globals->PCM.wf.nAvgBytesPerSec; +} + +DWORD WaveMix::SubFactor(DWORD a1, DWORD a2) +{ + return a1 - a2; +} + +DWORD WaveMix::AddFactor(DWORD a1, DWORD a2) +{ + return a1 + a2; +} + +dialog_template* WaveMix::MakeSettingsDlgTemplate() +{ + auto size = 0u; + dialog_template* temp = MakeDlgTemplate(&size, 0x80C80080, 0, 0, 212, 132, L"WavMix32 Settings (Ver %X.%X Static)"); + temp = AddDlgControl(&size, temp, 128, 65537, 1, 155, 5, 50, 14, L"&Ok"); + temp = AddDlgControl(&size, temp, 128, 0x10000, 2, 155, 25, 50, 14, L"&Cancel"); + temp = AddDlgControl(&size, temp, 130, 0, 1012, 5, 5, 115, 8, L"Number of WaveBlocks (%d-%d):"); + temp = AddDlgControl(&size, temp, 130, 0, 1013, 5, 20, 115, 8, L"Size of WaveBlocks (%d-%d):"); + temp = AddDlgControl(&size, temp, 130, 0, 0xFFFF, 5, 35, 102, 8, L"Playback Frequency (11,22,44):"); + temp = AddDlgControl(&size, temp, 130, 0, 0xFFFF, 5, 80, 57, 8, L"Playback Device:"); + temp = AddDlgControl(&size, temp, 130, 0, 1014, 5, 50, 115, 8, L"Number of Pause Blocks (%d-%d):"); + temp = AddDlgControl(&size, temp, 130, 0, 1015, 5, 65, 63, 8, L"Max Channels = %d"); + temp = AddDlgControl(&size, temp, 129, 8454272, 1003, 125, 5, 25, 12, L"-1"); + temp = AddDlgControl(&size, temp, 129, 8454272, 1007, 125, 20, 25, 12, L"-1"); + temp = AddDlgControl(&size, temp, 129, 8454272, 1008, 125, 35, 25, 12, L"-1"); + temp = AddDlgControl(&size, temp, 129, 8454272, 1011, 125, 50, 25, 12, L"-1"); + temp = AddDlgControl(&size, temp, 133, 2162946, 1009, 65, 80, 140, 60, L"No Devices"); + temp = AddDlgControl(&size, temp, 128, 65539, 1000, 5, 101, 40, 10, L"Stereo"); + temp = AddDlgControl(&size, temp, 128, 65539, 1001, 55, 101, 60, 10, L"Reset Remix"); + temp = AddDlgControl(&size, temp, 128, 65539, 1010, 125, 101, 78, 10, L"Show Debug Dialogs"); + temp = AddDlgControl(&size, temp, 128, 65539, 1005, 5, 117, 40, 10, L"CMixit"); + return AddDlgControl(&size, temp, 128, 65539, 1004, 55, 117, 75, 10, L"Good Get Position"); +} + +dialog_template* WaveMix::MakeDlgTemplate(unsigned* totalSize, unsigned style, short x, short y, short cx, short cy, + const wchar_t* String) +{ + auto dlgSize = 2 * wcslen(String) + 24; + *totalSize = dlgSize; + if ((dlgSize & 3) != 0) + *totalSize = dlgSize - (dlgSize & 3) + 4; + + auto hGlobal = GlobalAlloc(GHND, *totalSize); + auto dlgTemplate = static_cast(GlobalLock(hGlobal)); + if (dlgTemplate) + { + dlgTemplate->Dialog.dwExtendedStyle = 0; + dlgTemplate->Dialog.cdit = 0; + dlgTemplate->Dialog.style = style | 0x10000000; + dlgTemplate->Dialog.x = x; + dlgTemplate->Dialog.y = y; + dlgTemplate->Dialog.cx = cx; + dlgTemplate->Dialog.cy = cy; + memcpy(dlgTemplate->Header, String, 2 * wcslen(String) + 2); + } + return dlgTemplate; +} + +dialog_template* WaveMix::AddDlgControl(unsigned* totalSize, dialog_template* dlgTemplate, short idClass, + unsigned style, WORD id, short x, short y, short cx, short cy, + const wchar_t* String) +{ + dialog_template* dlgTemplate2 = dlgTemplate; + if (dlgTemplate) + { + auto prevSize = *totalSize; + *totalSize += 2 * wcslen(String) + 25; + if ((*totalSize & 3) != 0) + *totalSize = *totalSize - (*totalSize & 3) + 4; + + GlobalUnlock(GlobalHandle(dlgTemplate2)); + unsigned int newSize = *totalSize; + HGLOBAL hGlobal = GlobalReAlloc(GlobalHandle(dlgTemplate2), newSize, 0x42u); + dlgTemplate2 = static_cast(GlobalLock(hGlobal)); + if (dlgTemplate2) + { + auto dlgItem = (dialog_item_template*)((char*)dlgTemplate2 + prevSize); + dlgItem->Item.dwExtendedStyle = 0; + dlgItem->Item.style = style | 0x50000000; + dlgItem->Item.x = x; + dlgItem->Item.y = y; + dlgItem->Item.cx = cx; + dlgItem->Item.cy = cy; + dlgItem->Item.id = id; + dlgItem->sysClass = 0xFFFF; + dlgItem->idClass = idClass; + wcscpy_s(dlgItem->Header, wcslen(String) + 1, String); + ++dlgTemplate2->Dialog.cdit; + } + } + return dlgTemplate2; +} + +void WaveMix::DestroySettingsDlgTemplate(LPCVOID pMem) +{ + if (pMem) + { + GlobalUnlock(GlobalHandle(pMem)); + GlobalFree(GlobalHandle(pMem)); + } +} + +int WaveMix::Settings_OnInitDialog(HWND hWnd, int wParam, MIXCONFIG* lpMixconfig) +{ + tagWAVEOUTCAPSA pwoc{}; + CHAR String[256]; + + GetWindowTextA(hWnd, String, 256); + wsprintfA(string_buffer, String, 2, 81); + SetWindowTextA(hWnd, string_buffer); + SetWindowLongA(hWnd, -21, (LONG)lpMixconfig); + SendMessageA(GetDlgItem(hWnd, 1000), 0xF1u, lpMixconfig->wChannels > 1u, 0); + SendMessageA(GetDlgItem(hWnd, 1001), 0xF1u, lpMixconfig->ResetMixDefaultFlag != 0, 0); + SendMessageA(GetDlgItem(hWnd, 1004), 0xF1u, lpMixconfig->GoodWavePos != 0, 0); + SendMessageA(GetDlgItem(hWnd, 1005), 0xF1u, lpMixconfig->CmixPtrDefaultFlag != 0, 0); + SendMessageA(GetDlgItem(hWnd, 1010), 0xF1u, lpMixconfig->ShowDebugDialogs != 0, 0); + EnableWindow(GetDlgItem(hWnd, 1005), 0); + SendMessageA(GetDlgItem(hWnd, 1003), 0xC5u, 2u, 0); + SendMessageA(GetDlgItem(hWnd, 1007), 0xC5u, 4u, 0); + SendMessageA(GetDlgItem(hWnd, 1008), 0xC5u, 2u, 0); + SendMessageA(GetDlgItem(hWnd, 1011), 0xC5u, 2u, 0); + GetWindowTextA(GetDlgItem(hWnd, 1012), String, 100); + wsprintfA(string_buffer, String, 2, 10); + SetWindowTextA(GetDlgItem(hWnd, 1012), string_buffer); + GetWindowTextA(GetDlgItem(hWnd, 1013), String, 100); + wsprintfA(string_buffer, String, 344, 11025); + SetWindowTextA(GetDlgItem(hWnd, 1013), string_buffer); + GetWindowTextA(GetDlgItem(hWnd, 1014), String, 100); + wsprintfA(string_buffer, String, 0, 10); + SetWindowTextA(GetDlgItem(hWnd, 1014), string_buffer); + GetWindowTextA(GetDlgItem(hWnd, 1015), String, 100); + wsprintfA(string_buffer, String, 16); + SetWindowTextA(GetDlgItem(hWnd, 1015), string_buffer); + wsprintfA(string_buffer, "%d", lpMixconfig->WaveBlockCount); + SetWindowTextA(GetDlgItem(hWnd, 1003), string_buffer); + wsprintfA(string_buffer, "%d", lpMixconfig->WaveBlockLen); + SetWindowTextA(GetDlgItem(hWnd, 1007), string_buffer); + wsprintfA(string_buffer, "%d", lpMixconfig->wSamplingRate); + SetWindowTextA(GetDlgItem(hWnd, 1008), string_buffer); + wsprintfA(string_buffer, "%d", lpMixconfig->PauseBlocks); + SetWindowTextA(GetDlgItem(hWnd, 1011), string_buffer); + + signed int uDeviceID = 0; + signed int deviceCount = waveOutGetNumDevs(); + if (deviceCount > 0) + { + do + { + MMRESULT getResult = waveOutGetDevCapsA(uDeviceID, &pwoc, 0x34u); + if (getResult) + { + wsprintfA(string_buffer, "waveOutGetDevCaps failed (err %u) for device %d", getResult, uDeviceID); + MessageBoxA(nullptr, string_buffer, "WavMix32", 0x40u); + } + else + { + wsprintfA(string_buffer, "%d: %s", uDeviceID, pwoc.szPname); + SendMessageA(GetDlgItem(hWnd, 1009), 0x143u, 0, (LPARAM)string_buffer); + } + ++uDeviceID; + } + while (uDeviceID < deviceCount); + } + SendMessageA(GetDlgItem(hWnd, 1009), 0x14Eu, lpMixconfig->wDeviceID, 0); + return 1; +} + +int WaveMix::Settings_OnCommand(HWND hWnd, int command, int lParam, int wParam) +{ + auto userData = (MIXCONFIG*)GetWindowLongA(hWnd, -21); + if (command == 1) + { + if (userData) + { + userData->wChannels = (SendMessageA(GetDlgItem(hWnd, 1000), 0xF0u, 0, 0) != 0) + 1; + userData->ResetMixDefaultFlag = SendMessageA(GetDlgItem(hWnd, 1001), 0xF0u, 0, 0) != 0; + userData->GoodWavePos = SendMessageA(GetDlgItem(hWnd, 1004), 0xF0u, 0, 0) != 0; + userData->ShowDebugDialogs = SendMessageA(GetDlgItem(hWnd, 1010), 0xF0u, 0, 0) != 0; + userData->CmixPtrDefaultFlag = SendMessageA(GetDlgItem(hWnd, 1005), 0xF0u, 0, 0) != 0; + GetWindowTextA(GetDlgItem(hWnd, 1003), string_buffer, 10); + userData->WaveBlockCount = atoi(string_buffer); + GetWindowTextA(GetDlgItem(hWnd, 1007), string_buffer, 10); + userData->WaveBlockLen = atoi(string_buffer); + GetWindowTextA(GetDlgItem(hWnd, 1008), string_buffer, 10); + userData->wSamplingRate = atoi(string_buffer); + GetWindowTextA(GetDlgItem(hWnd, 1011), string_buffer, 10); + userData->PauseBlocks = atoi(string_buffer); + GetWindowTextA(GetDlgItem(hWnd, 1009), string_buffer, 10); + userData->wDeviceID = isdigit(string_buffer[0]) ? atoi(string_buffer) : 0; + EndDialog(hWnd, 1); + } + } + else + { + if (command != 2) + return 0; + EndDialog(hWnd, 0); + } + return 1; +} + +int WaveMix::ReadRegistryToGetMachineSpecificInfSection(unsigned wDeviceId, LPSTR lpString1, int maxLength) +{ + HKEY phkResult; + CHAR SubKey[52]; + + int result = 0; + int osPrefixLength = lstrlenA(GetOperatingSystemPrefix()); + if (maxLength < osPrefixLength + 10) + return 0; + wsprintfA(SubKey, "Device%u", wDeviceId); + if (RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\WaveMix", &phkResult)) + return 0; + lstrcpyA(lpString1, GetOperatingSystemPrefix()); + LONG cbData = maxLength - osPrefixLength; + if (!RegQueryValueA(phkResult, SubKey, &lpString1[osPrefixLength], &cbData) && cbData > 0) + result = 1; + RegCloseKey(phkResult); + return result; +} + +#pragma warning (disable : 4996)/*Original uses GetVersion*/ +const char* WaveMix::GetOperatingSystemPrefix() +{ + if (GetVersion() < 0x80000000) + return "WinNT:"; + if (GetVersion() >= 0x80000000 && static_cast(GetVersion()) >= 4u) + return "Win95:"; + if (GetVersion() >= 0x80000000 && static_cast(GetVersion()) < 4u) + return "Win31:"; + return "OS_X"; /*The next big thing: waveOut on OSX*/ +} +#pragma warning (default : 4996)/*Original uses GetVersion*/ + +unsigned WaveMix::FigureOutDMABufferSize(unsigned waveBlockLen, PCMWAVEFORMAT* pcm) +{ + unsigned int result = waveBlockLen; + if (!waveBlockLen) + result = pcm->wf.nSamplesPerSec * pcm->wf.nChannels * (pcm->wBitsPerSample >> 3) >> 4; + if (result < 344) + result = 344; + if (result > 11025) + result = 11025; + return result; +} + +int WaveMix::NoResetRemix(DWORD dwRemixSamplePos, CHANNELNODE* channel) +{ + Pump(); + return 1; +} + +void WaveMix::SaveConfigSettings(unsigned dwFlags) +{ + if ((dwFlags & 0x80u) != 0) + { + wsprintfA(string_buffer, "%d", Globals->wDeviceID); + WritePrivateProfileStringA("general", "WaveOutDevice", string_buffer, FileName); + } + if ((dwFlags & 1) != 0) + { + wsprintfA(string_buffer, "%d", Globals->PCM.wf.nChannels); + WritePrivateProfileStringA(Globals->szDevicePName, "Channels", string_buffer, FileName); + } + if ((dwFlags & 2) != 0) + { + wsprintfA(string_buffer, "%d", static_cast(11 * (Globals->PCM.wf.nSamplesPerSec / 0x2B11))); + WritePrivateProfileStringA(Globals->szDevicePName, "SamplesPerSec", string_buffer, FileName); + } + if ((dwFlags & 4) != 0) + { + wsprintfA(string_buffer, "%d", Globals->WaveBlockCount); + WritePrivateProfileStringA(Globals->szDevicePName, "WaveBlocks", string_buffer, FileName); + } + if ((dwFlags & 8) != 0) + { + wsprintfA(string_buffer, "%d", LOWORD(WaveMix::Globals->dwWaveBlockLen)); + WritePrivateProfileStringA(Globals->szDevicePName, "WaveBlockLen", string_buffer, FileName); + } + if ((dwFlags & 0x10) != 0) + { + wsprintfA(string_buffer, "%d", Globals->CmixPtr == cmixit); + WritePrivateProfileStringA(Globals->szDevicePName, "CMixit", string_buffer, FileName); + } + if ((dwFlags & 0x20) != 0) + { + wsprintfA(string_buffer, "%d", (Globals->pfnRemix != ResetRemix) + 1); + WritePrivateProfileStringA(Globals->szDevicePName, "Remix", string_buffer, FileName); + } + if ((dwFlags & 0x40) != 0) + { + wsprintfA(string_buffer, "%d", Globals->fGoodGetPos == 0); + WritePrivateProfileStringA(Globals->szDevicePName, "GoodWavePos", string_buffer, FileName); + } + if ((dwFlags & 0x100) != 0) + { + wsprintfA(string_buffer, "%d", ShowDebugDialogs != 0); + WritePrivateProfileStringA("general", "ShowDebugDialogs", string_buffer, FileName); + } + if ((dwFlags & 0x200) != 0) + { + wsprintfA(string_buffer, "%d", Globals->PauseBlocks); + WritePrivateProfileStringA(Globals->szDevicePName, "PauseBlocks", string_buffer, FileName); + } +} + +void WaveMix::ShowCurrentSettings() +{ + tagWAVEOUTCAPSA pwoc{}; + + if (waveOutGetDevCapsA(Globals->wDeviceID, &pwoc, 0x34u) || !RemoveInvalidIniNameCharacters(pwoc.szPname)) + lstrcpyA(pwoc.szPname, "Unknown Device"); + auto cmixitType = "cmixit"; + if (Globals->CmixPtr != cmixit) + cmixitType = "386 mixit"; + auto getGood = "TRUE"; + if (!Globals->fGoodGetPos) + getGood = "FALSE"; + auto remixType = "Reset"; + if (Globals->pfnRemix != ResetRemix) + remixType = "NoReset"; + wsprintfA( + string_buffer, + "Using:\t%s.\n" + "\tRemix = %s\n" + "\tGoodGetPos=%s\n" + "\t%d ping pong wave blocks\n" + "\tWave block len = %lu bytes\n" + "\tpfnMixit=%s\n" + "\tSamplesPerSec=%lu,\n" + "\tChannels=%d\n" + "\tPauseBlocks=%d", + pwoc.szPname, + remixType, + getGood, + Globals->WaveBlockCount, + Globals->dwWaveBlockLen, + cmixitType, + Globals->PCM.wf.nSamplesPerSec, + Globals->PCM.wf.nChannels, + Globals->PauseBlocks); + MessageBoxA(nullptr, string_buffer, "WavMix32", 0x40u); +} + +unsigned WaveMix::GetWaveDevice() +{ + WAVEFORMATEX pwfx{}; + + if (Globals->hWaveOut) + return 0; + HWND window = CreateWindowExA(0, "WavMix32", pinball::WindowName, 0x8000000u, 0, 0, 0, 0, nullptr, nullptr, HModule, + nullptr); + GLOBALS* globals = Globals; + Globals->hWndApp = window; + if (!window) + { + if (ShowDebugDialogs) + MessageBoxA(nullptr, "Failed to create callback window.", "WavMix32", 0x30u); + return 1; + } + pwfx.wFormatTag = globals->PCM.wf.wFormatTag; + pwfx.nChannels = globals->PCM.wf.nChannels; + pwfx.nSamplesPerSec = globals->PCM.wf.nSamplesPerSec; + pwfx.nAvgBytesPerSec = globals->PCM.wf.nAvgBytesPerSec; + pwfx.nBlockAlign = globals->PCM.wf.nBlockAlign; + pwfx.wBitsPerSample = globals->PCM.wBitsPerSample; + pwfx.cbSize = 0; + unsigned int openResult = waveOutOpen(&globals->hWaveOut, 0xFFFFFFFF, &pwfx, + reinterpret_cast(globals->hWndApp), 0, + 0x10000u); + if (openResult) + { + DestroyWindow(Globals->hWndApp); + Globals->hWndApp = nullptr; + return openResult; + } + if (AllocWaveBlocks(Globals->hWaveOut, block_array1) + && AllocWaveBlocks(Globals->hWaveOut, block_array2)) + { + Globals->WaveBlockArray = block_array1; + return 0; + } + FreeWaveBlocks(Globals->hWaveOut, block_array1); + FreeWaveBlocks(Globals->hWaveOut, block_array2); + waveOutClose(Globals->hWaveOut); + Globals->hWaveOut = nullptr; + DestroyWindow(Globals->hWndApp); + return 1; +} + +void WaveMix::FreeWaveBlocks(HWAVEOUT hwo, XWAVEHDR** waveBlocks) +{ + for (int i = 0; i < 10; ++i) + { + auto blockPtr = &waveBlocks[i]; + if (*blockPtr) + { + waveOutUnprepareHeader(hwo, &(*blockPtr)->wh, sizeof(WAVEHDR)); + GlobalUnlock(GlobalHandle(*blockPtr)); + GlobalFree(GlobalHandle(*blockPtr)); + *blockPtr = nullptr; + } + } +} + +int WaveMix::AllocWaveBlocks(HWAVEOUT hwo, XWAVEHDR** waveBlocks) +{ + for (int i = 0; i < 10; ++i) + { + auto xHDR = static_cast(GlobalLock(GlobalAlloc(GMEM_SHARE, 0x2B41u))); + waveBlocks[i] = xHDR; + if (!xHDR) + { + if (ShowDebugDialogs) + MessageBoxA( + nullptr, + "Unable to allocate memory for waveform data. Try making more memory available by closing other applications.", + "WavMix32", + 0x30u); + for (int j = i - 1; j >= 0; --j) + { + GlobalUnlock(GlobalHandle(waveBlocks[j])); + GlobalFree(GlobalHandle(waveBlocks[j])); + waveBlocks[j] = nullptr; + } + return 0; + } + xHDR->wh.lpData = (LPSTR)&xHDR[1]; + xHDR->wh.dwBufferLength = Globals->dwWaveBlockLen; + xHDR->wh.dwFlags = 0; + xHDR->wh.dwLoops = 0; + xHDR->fAvailable = 1; + xHDR->dwWavePos = 0; + } + int index = 0; + while (!waveOutPrepareHeader(hwo, &waveBlocks[index]->wh, sizeof(WAVEHDR))) + { + waveBlocks[index++]->wh.dwFlags |= 1u; + if (index >= 10) + return 1; + } + if (ShowDebugDialogs) + MessageBoxA(nullptr, "Unable to prepare wave header.", "WavMix32", 0x30u); + FreeWaveBlocks(hwo, waveBlocks); + return 0; +} + +void WaveMix::ReleaseWaveDevice(GLOBALS* globals) +{ + if (globals->fActive) + { + if (globals->hWaveOut) + { + MyWaveOutReset(globals->hWaveOut); + DestroyPlayQueue(); + FreeWaveBlocks(globals->hWaveOut, block_array1); + FreeWaveBlocks(globals->hWaveOut, block_array2); + waveOutClose(globals->hWaveOut); + globals->hWaveOut = nullptr; + DestroyWindow(globals->hWndApp); + globals->hWndApp = nullptr; + } + } +} + void WaveMix::cmixit(unsigned __int8* lpDest, unsigned __int8** rgWaveSrc, volume_struct* volume, int iNumWaves, unsigned __int16 length) { @@ -755,3 +1586,13 @@ LRESULT WaveMix::WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) Pump(); return 0; } + +INT_PTR WaveMix::SettingsDlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + if (Msg == 272) + return Settings_OnInitDialog(hWnd, wParam, (MIXCONFIG*)lParam); + if (Msg != 273) + return 0; + Settings_OnCommand(hWnd, static_cast(wParam), lParam, HIWORD(wParam)); + return 1; +} diff --git a/SpaceCadetPinball/WaveMix.h b/SpaceCadetPinball/WaveMix.h index 6abfea4..6f24469 100644 --- a/SpaceCadetPinball/WaveMix.h +++ b/SpaceCadetPinball/WaveMix.h @@ -58,13 +58,13 @@ struct MIXCONFIG DWORD dwFlags; WORD wChannels; WORD wSamplingRate; - __int16 WaveBlockCount; - __int16 WaveBlockLen; + unsigned __int16 WaveBlockCount; + unsigned __int16 WaveBlockLen; __int16 CmixPtrDefaultFlag; unsigned __int16 ResetMixDefaultFlag; unsigned __int16 GoodWavePos; unsigned __int16 wDeviceID; - __int16 PauseBlocks; + unsigned __int16 PauseBlocks; __int16 ShowDebugDialogs; HKEY RegistryKey; }; @@ -88,20 +88,13 @@ struct GLOBALS { WORD wMagic1; __int16 unknown0; - int unknown1; + HWND hWndApp; int unknown2; HWAVEOUT hWaveOut; int fActive; int SettingsDialogActiveFlag; unsigned int wDeviceID; - int unknown7; - int unknown8; - int unknown9; - int unknown10; - int unknown11; - int unknown12; - int unknown13; - int unknown14; + char szDevicePName[32]; int unknown15; int unknown16; int unknown17; @@ -156,23 +149,39 @@ struct GLOBALS int unknown93; int unknown94; PCMWAVEFORMAT PCM; - int WaveBlockLen; + DWORD dwWaveBlockLen; int WaveBlockCount; int PauseBlocks; XWAVEHDR** WaveBlockArray; DWORD dwCurrentSample; DWORD dwBaseTime; int fGoodGetPos; - int dwWaveOutPos; + DWORD dwWaveOutPos; void (*CmixPtr)(unsigned __int8* lpDest, unsigned __int8** rgWaveSrc, volume_struct* volume, int iNumWaves, unsigned __int16 length); int (* pfnRemix)(DWORD, CHANNELNODE*); - int (* pfnSampleAdjust)(int, int); + DWORD (* pfnSampleAdjust)(DWORD, DWORD); int unknown110; __int16 wMagic2; __int16 unknown112; }; +struct dialog_template +{ + DLGTEMPLATE Dialog; + WORD menu; + WORD windowClass; + WCHAR Header[1]; +}; + +struct dialog_item_template +{ + DLGITEMTEMPLATE Item; + WORD sysClass; + WORD idClass; + WCHAR Header[1]; +}; + class WaveMix { @@ -204,7 +213,7 @@ private: static int DefaultPauseBlocks(int waveBlocks); static int Configure(GLOBALS* hMixSession, HWND hWndParent, MIXCONFIG* lpConfig, int* flag1Ptr, int saveConfigFlag); static int GetConfig(HANDLE hMixSession, MIXCONFIG* lpConfig); - static unsigned MyWaveOutGetPosition(HWAVEOUT hwo, int fGoodGetPos); + static unsigned MyWaveOutGetPosition(HWAVEOUT hWaveOut, int fGoodGetPos); static void FreeChannelNode(CHANNELNODE* channel); static int ResetRemix(DWORD dwRemixSamplePos, CHANNELNODE* channel); static XWAVEHDR* RemoveFromPlayingQueue(XWAVEHDR* lpXWH); @@ -213,9 +222,35 @@ private: static XWAVEHDR* GetWaveBlock(); static int MixerPlay(XWAVEHDR* lpXWH, int fWriteBlocks); static XWAVEHDR* AddToPlayingQueue(XWAVEHDR* lpXWH); + static void MyWaveOutReset(HWAVEOUT hWaveOut); + static void SetWaveOutPosition(unsigned int newPosition); + static DWORD SubFactor(DWORD a1, DWORD a2); + static DWORD AddFactor(DWORD a1, DWORD a2); + static dialog_template* MakeSettingsDlgTemplate(); + static dialog_template* MakeDlgTemplate(unsigned* totalSize, unsigned style, __int16 x, __int16 y, __int16 cx, + __int16 cy, + const wchar_t* String); + static dialog_template* AddDlgControl(unsigned int* totalSize, dialog_template* dlgTemplate, __int16 idClass, + unsigned style, + WORD id, __int16 x, __int16 y, __int16 cx, __int16 cy, + const wchar_t* String); + static void DestroySettingsDlgTemplate(LPCVOID pMem); + static int Settings_OnInitDialog(HWND hWnd, int wParam, MIXCONFIG* lpMixconfig); + static int Settings_OnCommand(HWND hWnd, int command, int lParam, int wParam); + static int ReadRegistryToGetMachineSpecificInfSection(unsigned wDeviceId, LPSTR lpString1, int maxLength); + static const char* GetOperatingSystemPrefix(); + static unsigned int FigureOutDMABufferSize(unsigned int waveBlockLen, PCMWAVEFORMAT* pcm); + static int NoResetRemix(DWORD dwRemixSamplePos, CHANNELNODE* channel); + static void SaveConfigSettings(unsigned dwFlags); + static void ShowCurrentSettings(); + static unsigned int GetWaveDevice(); + static void FreeWaveBlocks(HWAVEOUT hwo, XWAVEHDR** waveBlocks); + static int AllocWaveBlocks(HWAVEOUT hwo, XWAVEHDR** waveBlocks); + static void ReleaseWaveDevice(GLOBALS* globals); static void cmixit(unsigned __int8* lpDest, unsigned __int8** rgWaveSrc, volume_struct* volume, int iNumWaves, unsigned __int16 length); static LRESULT __stdcall WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + static INT_PTR __stdcall SettingsDlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); static int initialized_flag; static char FileName[276];