diff --git a/Doc/FuncStats.xlsx b/Doc/FuncStats.xlsx index 032240b..880365d 100644 Binary files a/Doc/FuncStats.xlsx and b/Doc/FuncStats.xlsx differ diff --git a/SpaceCadetPinball/WaveMix.cpp b/SpaceCadetPinball/WaveMix.cpp index 37cefeb..2c3f53c 100644 --- a/SpaceCadetPinball/WaveMix.cpp +++ b/SpaceCadetPinball/WaveMix.cpp @@ -7,12 +7,19 @@ CHANNELNODE WaveMix::channel_nodes[MAXQUEUEDWAVES]; CHANNELNODE* WaveMix::free_channel_nodes; char WaveMix::volume_table[256 * 11]; int WaveMix::debug_flag; -int WaveMix::cmixit_ptr; +void (*WaveMix::cmixit_ptr)(unsigned __int8* lpDest, unsigned __int8** rgWaveSrc, volume_struct* volume, int iNumWaves, + unsigned __int16 length); HMODULE WaveMix::HModule; PCMWAVEFORMAT WaveMix::gpFormat = {{1u, 1u, 11025u, 11025u, 1u}, 8u}; char WaveMix::string_buffer[256] = "WaveMix V 2.3 by Angel M. Diaz, Jr. (c) Microsoft 1993-1995"; -GLOBALS* WaveMix::Globals; +GLOBALS *WaveMix::Globals, *WaveMix::GlobalsActive; int WaveMix::ShowDebugDialogs; +PLAYQUEUE WaveMix::play_queue; +CHANNELNODE* WaveMix::play_channel_array[MAXCHANNELS]; +XWAVEHDR* WaveMix::block_array1[10]; +XWAVEHDR* WaveMix::block_array2[10]; +unsigned char* WaveMix::play_data[MAXCHANNELS]; +volume_struct WaveMix::play_volume[MAXCHANNELS]; HANDLE WaveMix::Init() { @@ -60,8 +67,8 @@ HANDLE WaveMix::ConfigureInit(MIXCONFIG* lpConfig) globals->CmixPtr = cmixit_ptr; globals->wMagic2 = 21554; globals->wMagic1 = 21554; - globals->unknown102 = 0; - globals->unknown5 = 0; + globals->WaveBlockArray = nullptr; + globals->SettingsDialogActiveFlag = 0; globals->unknown44 = 655370; memset(globals->aChannel, 0xFFu, sizeof globals->aChannel); memmove(&globals->PCM, &gpFormat, 0x10u); @@ -189,7 +196,7 @@ int WaveMix::Startup(HMODULE hModule) return 0; InitVolumeTable(); debug_flag = GetPrivateProfileIntA("general", "debug", 0, FileName); - cmixit_ptr = (int)cmixit; + cmixit_ptr = cmixit; HModule = hModule; WndClass.hCursor = LoadCursorA(nullptr, IDC_ARROW); @@ -344,7 +351,7 @@ int WaveMix::ReadRegistryForAppSpecificConfigs(MIXCONFIG* lpConfig) if ((dwFlags & 2) == 0) lpConfig->wSamplingRate = ReadRegistryInt(phkResult, "SamplesPerSec", 11); if ((dwFlags & 4) == 0) - lpConfig->WaveBlocks = static_cast(ReadRegistryInt(phkResult, "WaveBlocks", 3)); + lpConfig->WaveBlockCount = static_cast(ReadRegistryInt(phkResult, "WaveBlocks", 3)); if ((dwFlags & 8) == 0) lpConfig->WaveBlockLen = static_cast(ReadRegistryInt(phkResult, "WaveBlockLen", 0)); lpConfig->CmixPtrDefaultFlag = 1; @@ -360,7 +367,7 @@ int WaveMix::ReadRegistryForAppSpecificConfigs(MIXCONFIG* lpConfig) lpConfig->ShowDebugDialogs = static_cast(ReadRegistryInt(phkResult, "ShowDebugDialogs", 0)); if ((dwFlags & 0x200) == 0) { - int defaultPauseBlocks = DefaultPauseBlocks(static_cast(lpConfig->WaveBlocks)); + int defaultPauseBlocks = DefaultPauseBlocks(static_cast(lpConfig->WaveBlockCount)); lpConfig->PauseBlocks = static_cast(ReadRegistryInt(phkResult, "PauseBlocks", defaultPauseBlocks)); } lpConfig->dwFlags = 1023; @@ -395,7 +402,7 @@ int WaveMix::DefaultGoodWavePos(unsigned uDeviceID) } else { - result = (LOBYTE(pwoc.dwSupport) >> 5) & 1; + result = LOBYTE(pwoc.dwSupport) >> 5 & 1; } return result; } @@ -414,15 +421,72 @@ int WaveMix::DefaultPauseBlocks(int waveBlocks) int WaveMix::Configure(GLOBALS* hMixSession, HWND hWndParent, MIXCONFIG* lpConfig, int* flag1Ptr, int saveConfigFlag) { + MIXCONFIG mixConfigLocal; + + auto hMixSession2 = hMixSession; + auto mixConfig = lpConfig; + auto hMixSession3 = hMixSession; + auto flag1Ptr_2 = flag1Ptr; + auto someFlag1 = 0; + auto globals1 = SessionToGlobalDataPtr(hMixSession); + Globals = globals1; + if (!globals1) + return 5; + if (globals1->fActive) + return 4; + if (globals1->SettingsDialogActiveFlag) + return 12; + FlushChannel(hMixSession, -1, 1u); + + if (!mixConfig) + { + mixConfigLocal.wSize = 30; + mixConfigLocal.dwFlags = 1023; + GetConfig(static_cast(hMixSession), &mixConfigLocal); + } + return 1; } +int WaveMix::GetConfig(HANDLE hMixSession, MIXCONFIG* lpConfig) +{ + GLOBALS* globals = SessionToGlobalDataPtr(static_cast(hMixSession)); + Globals = globals; + if (!globals) + return 5; + if (!lpConfig) + return 11; + + DWORD dwFlags = lpConfig->dwFlags; + if ((dwFlags & 1) != 0) + lpConfig->wChannels = globals->PCM.wf.nChannels; + if ((dwFlags & 2) != 0) + lpConfig->wSamplingRate = 11 * (globals->PCM.wf.nSamplesPerSec / 0x2B11); + if ((dwFlags & 4) != 0) + lpConfig->WaveBlockCount = globals->WaveBlockCount; + if ((dwFlags & 8) != 0) + lpConfig->WaveBlockLen = globals->WaveBlockLen; + if ((dwFlags & 0x10) != 0) + lpConfig->CmixPtrDefaultFlag = globals->CmixPtr == cmixit; + if ((dwFlags & 0x20) != 0) + lpConfig->ResetMixDefaultFlag = globals->pfnRemix == ResetRemix; + if ((dwFlags & 0x40) != 0) + lpConfig->GoodWavePos = globals->fGoodGetPos; + if ((dwFlags & 0x80u) != 0) + lpConfig->wDeviceID = globals->wDeviceID; + if ((dwFlags & 0x100) != 0) + lpConfig->ShowDebugDialogs = ShowDebugDialogs != 0; + if ((dwFlags & 0x200) != 0) + lpConfig->PauseBlocks = globals->PauseBlocks; + return 0; +} + unsigned WaveMix::MyWaveOutGetPosition(HWAVEOUT hwo, int fGoodGetPos) { mmtime_tag pmmt{}; if (!fGoodGetPos) - return ((timeGetTime() - Globals->dwBaseTime) * Globals->PCM.wf.nAvgBytesPerSec / 0x3E8) & 0xFFFFFFF8; + return (timeGetTime() - Globals->dwBaseTime) * Globals->PCM.wf.nAvgBytesPerSec / 0x3E8 & 0xFFFFFFF8; pmmt.wType = 4; waveOutGetPosition(hwo, &pmmt, 0xCu); return Globals->pfnSampleAdjust(pmmt.u.ms, Globals->dwWaveOutPos); @@ -437,9 +501,251 @@ void WaveMix::FreeChannelNode(CHANNELNODE* channel) } } -short WaveMix::cmixit(BYTE* a1, char* a2, char* a3, int a4, unsigned short a5) +int WaveMix::ResetRemix(DWORD dwRemixSamplePos, CHANNELNODE* channel) +{ + Globals->dwCurrentSample = dwRemixSamplePos; + DestroyPlayQueue(); + SwapWaveBlocks(); + + while (true) + { + auto block = GetWaveBlock(); + if (!block) + break; + if (!MixerPlay(block, 0)) + { + block->fAvailable = 1; + break; + } + AddToPlayingQueue(block); + } + + return 1; +} + +XWAVEHDR* WaveMix::RemoveFromPlayingQueue(XWAVEHDR* lpXWH) +{ + if (!play_queue.first) + return nullptr; + + if (lpXWH != play_queue.first) + { + XWAVEHDR* prev = play_queue.first; + XWAVEHDR* current = play_queue.first->QNext; + while (current) + { + if (current == lpXWH) + break; + prev = current; + current = current->QNext; + } + if (!current) + return nullptr; + + prev->QNext = current->QNext; + if (current == play_queue.last) + play_queue.last = prev; + } + else + { + play_queue.first = lpXWH->QNext; + if (!play_queue.first) + play_queue.last = nullptr; + } + + lpXWH->QNext = nullptr; + return lpXWH; +} + +void WaveMix::DestroyPlayQueue() +{ + while (play_queue.first) + { + play_queue.first->fAvailable = 1; + RemoveFromPlayingQueue(play_queue.first); + } +} + +void WaveMix::SwapWaveBlocks() +{ + if (Globals->WaveBlockArray == block_array1) + Globals->WaveBlockArray = block_array2; + else + Globals->WaveBlockArray = block_array1; +} + +XWAVEHDR* WaveMix::GetWaveBlock() +{ + int index = 0; + for (; index < Globals->WaveBlockCount; index++) + if (Globals->WaveBlockArray[index]->fAvailable) + break; + if (index >= Globals->WaveBlockCount) + return nullptr; + + XWAVEHDR* result = Globals->WaveBlockArray[index]; + result->fAvailable = 0; + result->wh.dwBufferLength = Globals->WaveBlockLen; + result->wh.lpData = reinterpret_cast(&result[1]); + result->g = GlobalsActive; + return result; +} + +int WaveMix::MixerPlay(XWAVEHDR* lpXWH, int fWriteBlocks) +{ + if (!lpXWH) + return 0; + + unsigned minStartPos = -1; + auto playChannelCount = 0; + auto channelPtr = Globals->aChannel; + for (auto channelIndex = 16; channelIndex; --channelIndex) + { + auto channel = *channelPtr; + if (channel != reinterpret_cast(-1) && channel) + { + do + { + if (channel->dwEndPos > Globals->dwCurrentSample) + break; + channel = channel->next; + } + while (channel); + if (channel) + { + if (channel->dwStartPos < minStartPos) + minStartPos = channel->dwStartPos; + play_channel_array[playChannelCount++] = channel; + } + } + ++channelPtr; + } + if (!playChannelCount) + { + if (fWriteBlocks) + lpXWH->fAvailable = 1; + return 0; + } + + auto currentSample = Globals->dwCurrentSample; + auto dataPtr = reinterpret_cast(lpXWH->wh.lpData); + auto waveBlockLen = Globals->WaveBlockLen; + while (waveBlockLen) + { + if (currentSample >= minStartPos) + { + auto waveCount = 0; + auto endBlockPosition = currentSample + waveBlockLen; + + for (auto channelIndex = 0; channelIndex < playChannelCount; ++channelIndex) + { + auto channel = play_channel_array[channelIndex]; + if (channel->dwStartPos <= currentSample) + { + if (channel->dwEndPos < endBlockPosition) + endBlockPosition = channel->dwEndPos; + auto dataOffset = currentSample - channel->dwStartPos; + if (channel->PlayParams.wLoops) + { + dataOffset %= channel->dwNumSamples; + auto endBlockPosition2 = currentSample + (channel->dwNumSamples - dataOffset); + if (endBlockPosition2 < endBlockPosition) + endBlockPosition = endBlockPosition2; + } + play_data[waveCount] = &channel->lpPos[dataOffset]; + play_volume[waveCount].L = channel->Volume.L; + play_volume[waveCount].R = channel->Volume.R; + waveCount++; + } + else if (channel->dwStartPos < endBlockPosition) + { + endBlockPosition = channel->dwStartPos; + } + } + + if (waveCount) + { + auto dataLength = endBlockPosition - currentSample; + Globals->CmixPtr(dataPtr, play_data, play_volume, waveCount, dataLength); + + dataPtr += dataLength; + waveBlockLen -= dataLength; + minStartPos = -1; + currentSample += dataLength; + + + auto playChPtr = play_channel_array; + for (auto channelIndex = 0; channelIndex < playChannelCount;) + { + while (*playChPtr) + { + if ((*playChPtr)->dwEndPos > currentSample) + break; + *playChPtr = (*playChPtr)->next; + } + if (*playChPtr) + { + if ((*playChPtr)->dwStartPos < minStartPos) + minStartPos = (*playChPtr)->dwStartPos; + ++channelIndex; + ++playChPtr; + } + else + { + playChannelCount--; + *playChPtr = play_channel_array[playChannelCount]; + if (!playChannelCount) + break; + } + } + } + } + else + { + auto length = waveBlockLen; + if (waveBlockLen + currentSample >= minStartPos) + length = minStartPos - currentSample; + memset(dataPtr, 0x80u, length); + dataPtr += length; + currentSample += length; + waveBlockLen -= length; + } + } + + lpXWH->dwWavePos = Globals->dwCurrentSample; + Globals->dwCurrentSample += Globals->WaveBlockLen; + if (fWriteBlocks) + { + AddToPlayingQueue(lpXWH); + if (waveOutWrite(Globals->hWaveOut, &lpXWH->wh, 0x20u)) + { + if (ShowDebugDialogs) + MessageBoxA(nullptr, "Failed to write block to device", "WavMix32", 0x30u); + lpXWH->fAvailable = 1; + RemoveFromPlayingQueue(lpXWH); + } + } + return 1; +} + +XWAVEHDR* WaveMix::AddToPlayingQueue(XWAVEHDR* lpXWH) +{ + lpXWH->QNext = nullptr; + if (play_queue.first) + { + play_queue.last->QNext = lpXWH; + } + else + { + play_queue.first = lpXWH; + } + play_queue.last = lpXWH; + return play_queue.first; +} + +void WaveMix::cmixit(unsigned __int8* lpDest, unsigned __int8** rgWaveSrc, volume_struct* volume, int iNumWaves, + unsigned __int16 length) { - return 0; } LRESULT WaveMix::WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) diff --git a/SpaceCadetPinball/WaveMix.h b/SpaceCadetPinball/WaveMix.h index c6b7e2b..6abfea4 100644 --- a/SpaceCadetPinball/WaveMix.h +++ b/SpaceCadetPinball/WaveMix.h @@ -10,6 +10,15 @@ #define MAXCHANNELS 16 #define MAXQUEUEDWAVES 100 +struct GLOBALS; + +struct volume_struct +{ + unsigned __int16 L; + unsigned __int16 R; +}; + + struct MIXWAVE { PCMWAVEFORMAT pcm; @@ -27,6 +36,7 @@ struct MIXPLAYPARAMS HWND hWndNotify; DWORD dwFlags; WORD wLoops; + int Unknown0; }; struct CHANNELNODE @@ -37,10 +47,9 @@ struct CHANNELNODE DWORD dwNumSamples; DWORD dwStartPos; DWORD dwEndPos; - char* lpPos; - char* lpEnd; - int Unknown0; - int Unknown1; + unsigned char* lpPos; + unsigned char* lpEnd; + volume_struct Volume; }; struct MIXCONFIG @@ -49,7 +58,7 @@ struct MIXCONFIG DWORD dwFlags; WORD wChannels; WORD wSamplingRate; - __int16 WaveBlocks; + __int16 WaveBlockCount; __int16 WaveBlockLen; __int16 CmixPtrDefaultFlag; unsigned __int16 ResetMixDefaultFlag; @@ -60,6 +69,21 @@ struct MIXCONFIG HKEY RegistryKey; }; +struct XWAVEHDR +{ + WAVEHDR wh; + BOOL fAvailable; + DWORD dwWavePos; + GLOBALS* g; + struct XWAVEHDR* QNext; +}; + +struct PLAYQUEUE +{ + XWAVEHDR* first; + XWAVEHDR* last; +}; + struct GLOBALS { WORD wMagic1; @@ -68,7 +92,7 @@ struct GLOBALS int unknown2; HWAVEOUT hWaveOut; int fActive; - int unknown5; + int SettingsDialogActiveFlag; unsigned int wDeviceID; int unknown7; int unknown8; @@ -112,7 +136,7 @@ struct GLOBALS int unknown58; int unknown59; int unknown60; - CHANNELNODE* aChannel[16]; + CHANNELNODE* aChannel[MAXCHANNELS]; int unknown77; int unknown78; int unknown79; @@ -135,14 +159,15 @@ struct GLOBALS int WaveBlockLen; int WaveBlockCount; int PauseBlocks; - int unknown102; - int unknown103; + XWAVEHDR** WaveBlockArray; + DWORD dwCurrentSample; DWORD dwBaseTime; int fGoodGetPos; int dwWaveOutPos; - int CmixPtr; - void(__stdcall* pfnRemix)(DWORD, CHANNELNODE*); - int (__stdcall* pfnSampleAdjust)(int, int); + void (*CmixPtr)(unsigned __int8* lpDest, unsigned __int8** rgWaveSrc, volume_struct* volume, int iNumWaves, + unsigned __int16 length); + int (* pfnRemix)(DWORD, CHANNELNODE*); + int (* pfnSampleAdjust)(int, int); int unknown110; __int16 wMagic2; __int16 unknown112; @@ -178,9 +203,18 @@ private: static int DefaultGoodWavePos(unsigned int uDeviceID); 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 void FreeChannelNode(CHANNELNODE* channel); - static __int16 cmixit(BYTE* a1, char* a2, char* a3, int a4, unsigned __int16 a5); + static int ResetRemix(DWORD dwRemixSamplePos, CHANNELNODE* channel); + static XWAVEHDR* RemoveFromPlayingQueue(XWAVEHDR* lpXWH); + static void DestroyPlayQueue(); + static void SwapWaveBlocks(); + static XWAVEHDR* GetWaveBlock(); + static int MixerPlay(XWAVEHDR* lpXWH, int fWriteBlocks); + static XWAVEHDR* AddToPlayingQueue(XWAVEHDR* lpXWH); + 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 initialized_flag; @@ -189,10 +223,17 @@ private: static CHANNELNODE* free_channel_nodes; static char volume_table[256 * 11]; static int debug_flag; - static int cmixit_ptr; + static void (*cmixit_ptr)(unsigned __int8* lpDest, unsigned __int8** rgWaveSrc, volume_struct* volume, + int iNumWaves, unsigned __int16 length); static HMODULE HModule; - static GLOBALS* Globals; + static GLOBALS *Globals, *GlobalsActive; static PCMWAVEFORMAT gpFormat; static int ShowDebugDialogs; static char string_buffer[256]; + static PLAYQUEUE play_queue; + static CHANNELNODE* play_channel_array[MAXCHANNELS]; + static XWAVEHDR* block_array1[10]; + static XWAVEHDR* block_array2[10]; + static unsigned char* play_data[MAXCHANNELS]; + static volume_struct play_volume[MAXCHANNELS]; };