From 30b35981d3209f9d2949a55eb603664d51b0d3f4 Mon Sep 17 00:00:00 2001 From: Nathan M Gilbert Date: Thu, 18 Jan 2024 13:23:13 -0500 Subject: [PATCH] finish MusicManager (#453) * finish MusicManager * Fixes/improvements --------- Co-authored-by: Christian Semmler --- .../legoomni/src/video/legometerpresenter.cpp | 2 +- LEGO1/omni/include/mxmusicmanager.h | 35 +++-- LEGO1/omni/include/mxstreamcontroller.h | 3 + .../omni/src/audio/mxloopingmidipresenter.cpp | 2 +- LEGO1/omni/src/audio/mxmidipresenter.cpp | 2 +- LEGO1/omni/src/audio/mxmusicmanager.cpp | 142 ++++++++++++++++-- util/compat.h | 5 + 7 files changed, 168 insertions(+), 23 deletions(-) diff --git a/LEGO1/lego/legoomni/src/video/legometerpresenter.cpp b/LEGO1/lego/legoomni/src/video/legometerpresenter.cpp index 5be9d0d6..f347545d 100644 --- a/LEGO1/lego/legoomni/src/video/legometerpresenter.cpp +++ b/LEGO1/lego/legoomni/src/video/legometerpresenter.cpp @@ -8,7 +8,7 @@ DECOMP_SIZE_ASSERT(LegoMeterPresenter, 0x94) // GLOBAL: LEGO1 0x1010207c // STRING: LEGO1 0x10101fb4 -const char* g_filterIndex = "FILTER_INDEX"; +const char* g_filterIndex = "FILLER_INDEX"; // GLOBAL: LEGO1 0x10102094 // STRING: LEGO1 0x10101f70 diff --git a/LEGO1/omni/include/mxmusicmanager.h b/LEGO1/omni/include/mxmusicmanager.h index f2afe437..48c2df69 100644 --- a/LEGO1/omni/include/mxmusicmanager.h +++ b/LEGO1/omni/include/mxmusicmanager.h @@ -4,6 +4,8 @@ #include "decomp.h" #include "mxaudiomanager.h" +#include + // VTABLE: LEGO1 0x100dc930 // SIZE 0x58 class MxMusicManager : public MxAudioManager { @@ -16,9 +18,17 @@ class MxMusicManager : public MxAudioManager { virtual MxResult Create(MxU32 p_frequencyMS, MxBool p_createThread); // vtable+30 inline MxBool GetMIDIInitialized() { return m_midiInitialized; } + inline void GetMIDIVolume(DWORD& p_volume) + { + if (midiOutGetVolume((HMIDIOUT) m_midiStreamH, &p_volume)) { + p_volume = CalculateVolume(100); + } + } + MxResult ResetStream(); + void ResetBuffer(); + undefined4 InitializeMIDI(MxU8* p_data, MxS32 p_loopCount); void DeinitializeMIDI(); - undefined4 FUN_100c09c0(MxU8* p_data, MxS32 p_loopCount); void SetMultiplier(MxS32 p_multiplier); private: @@ -27,16 +37,19 @@ class MxMusicManager : public MxAudioManager { MxS32 CalculateVolume(MxS32 p_volume); void SetMIDIVolume(); - HMIDISTRM m_midiStreamH; // 0x30 - MxBool m_midiInitialized; // 0x34 - undefined4 m_unk0x38; // 0x38 - undefined4 m_unk0x3c; // 0x3c - undefined4 m_unk0x40; // 0x40 - undefined4 m_unk0x44; // 0x44 - undefined4 m_unk0x48; // 0x48 - MIDIHDR* m_midiHdrP; // 0x4c - MxS32 m_multiplier; // 0x50 - DWORD m_midiVolume; // 0x54 + static void CALLBACK + MidiCallbackProc(HMIDIOUT p_hmo, UINT p_wMsg, DWORD_PTR p_dwInstance, DWORD_PTR p_dwParam1, DWORD_PTR p_dwParam2); + + HMIDISTRM m_midiStreamH; // 0x30 + MxBool m_midiInitialized; // 0x34 + MxU32 m_bufferSize; // 0x38 + MxU32 m_bufferCurrentSize; // 0x3c + MxU8* m_bufferOffset; // 0x40 + MxU8* m_bufferCurrentOffset; // 0x44 + MxU32 m_loopCount; // 0x48 + MIDIHDR* m_midiHdrP; // 0x4c + MxS32 m_multiplier; // 0x50 + DWORD m_midiVolume; // 0x54 // SYNTHETIC: LEGO1 0x100c0610 // MxMusicManager::`scalar deleting destructor' diff --git a/LEGO1/omni/include/mxstreamcontroller.h b/LEGO1/omni/include/mxstreamcontroller.h index 790856c1..57caae8e 100644 --- a/LEGO1/omni/include/mxstreamcontroller.h +++ b/LEGO1/omni/include/mxstreamcontroller.h @@ -118,4 +118,7 @@ class MxStreamController : public MxCore { // TEMPLATE: LEGO1 0x100c1240 // List::~List +// TEMPLATE: LEGO1 0x100c1bc0 +// list >::insert + #endif // MXSTREAMCONTROLLER_H diff --git a/LEGO1/omni/src/audio/mxloopingmidipresenter.cpp b/LEGO1/omni/src/audio/mxloopingmidipresenter.cpp index 2ad286e1..77b32506 100644 --- a/LEGO1/omni/src/audio/mxloopingmidipresenter.cpp +++ b/LEGO1/omni/src/audio/mxloopingmidipresenter.cpp @@ -40,7 +40,7 @@ MxResult MxLoopingMIDIPresenter::PutData() if (m_currentTickleState == e_streaming && m_chunk && !MusicManager()->GetMIDIInitialized()) { SetVolume(((MxDSSound*) m_action)->GetVolume()); - MusicManager()->FUN_100c09c0(m_chunk->GetData(), !m_action->GetLoopCount() ? -1 : m_action->GetLoopCount()); + MusicManager()->InitializeMIDI(m_chunk->GetData(), !m_action->GetLoopCount() ? -1 : m_action->GetLoopCount()); } m_criticalSection.Leave(); diff --git a/LEGO1/omni/src/audio/mxmidipresenter.cpp b/LEGO1/omni/src/audio/mxmidipresenter.cpp index 8757e6af..db65574a 100644 --- a/LEGO1/omni/src/audio/mxmidipresenter.cpp +++ b/LEGO1/omni/src/audio/mxmidipresenter.cpp @@ -95,7 +95,7 @@ MxResult MxMIDIPresenter::PutData() if (m_currentTickleState == e_streaming && m_chunk && !MusicManager()->GetMIDIInitialized()) { SetVolume(((MxDSSound*) m_action)->GetVolume()); - if (MusicManager()->FUN_100c09c0(m_chunk->GetData(), 1)) + if (MusicManager()->InitializeMIDI(m_chunk->GetData(), 1)) EndAction(); } diff --git a/LEGO1/omni/src/audio/mxmusicmanager.cpp b/LEGO1/omni/src/audio/mxmusicmanager.cpp index 244e3861..b936ac30 100644 --- a/LEGO1/omni/src/audio/mxmusicmanager.cpp +++ b/LEGO1/omni/src/audio/mxmusicmanager.cpp @@ -31,11 +31,11 @@ void MxMusicManager::InitData() { m_midiStreamH = 0; m_midiInitialized = FALSE; - m_unk0x38 = 0; - m_unk0x3c = 0; - m_unk0x40 = 0; - m_unk0x44 = 0; - m_unk0x48 = 0; + m_bufferSize = 0; + m_bufferCurrentSize = 0; + m_bufferOffset = 0; + m_bufferCurrentOffset = 0; + m_loopCount = 0; m_midiHdrP = NULL; } @@ -62,6 +62,60 @@ void MxMusicManager::Destroy(MxBool p_fromDestructor) } } +// FUNCTION: LEGO1 0x100c0720 +MxResult MxMusicManager::ResetStream() +{ + MxResult result = FAILURE; + + if (m_midiInitialized) { + if (m_bufferCurrentSize == 0) { + if (m_loopCount != -1) { + m_loopCount += -1; + + if (!m_loopCount) { + DeinitializeMIDI(); + goto done; + } + } + + ResetBuffer(); + } + + if (m_midiHdrP->dwFlags & (MHDR_DONE | MHDR_PREPARED)) { + if (midiOutUnprepareHeader((HMIDIOUT) m_midiStreamH, m_midiHdrP, sizeof(MIDIHDR)) != MMSYSERR_NOERROR) + goto done; + + memset(m_midiHdrP, 0, sizeof(MIDIHDR)); + } + + m_bufferCurrentOffset += 4; + DWORD length = *((DWORD*) m_bufferCurrentOffset); + m_bufferCurrentOffset += sizeof(DWORD); + + m_midiHdrP->lpData = (LPSTR) m_bufferCurrentOffset; + m_midiHdrP->dwBufferLength = length; + m_midiHdrP->dwBytesRecorded = length; + + if (!midiOutPrepareHeader((HMIDIOUT) m_midiStreamH, m_midiHdrP, sizeof(MIDIHDR))) { + if (!midiStreamOut(m_midiStreamH, m_midiHdrP, sizeof(MIDIHDR))) { + result = SUCCESS; + m_bufferCurrentOffset += length; + m_bufferCurrentSize--; + } + } + } + +done: + return result; +} + +// FUNCTION: LEGO1 0x100c07e0 +void MxMusicManager::ResetBuffer() +{ + m_bufferCurrentOffset = m_bufferOffset; + m_bufferCurrentSize = m_bufferSize; +} + // FUNCTION: LEGO1 0x100c07f0 void MxMusicManager::SetMIDIVolume() { @@ -74,6 +128,19 @@ void MxMusicManager::SetMIDIVolume() } } +// FUNCTION: LEGO1 0x100c0820 +void CALLBACK MxMusicManager::MidiCallbackProc( + HMIDIOUT p_hmo, + UINT p_wMsg, + DWORD_PTR p_dwInstance, + DWORD_PTR p_dwParam1, + DWORD_PTR p_dwParam2 +) +{ + if (p_wMsg == MOM_DONE) + ((MxMusicManager*) p_dwInstance)->ResetStream(); +} + // FUNCTION: LEGO1 0x100c0840 MxResult MxMusicManager::Create(MxU32 p_frequencyMS, MxBool p_createThread) { @@ -136,11 +203,68 @@ MxS32 MxMusicManager::CalculateVolume(MxS32 p_volume) return (result << 0x10) | result; } -// STUB: LEGO1 0x100c09c0 -undefined4 MxMusicManager::FUN_100c09c0(MxU8* p_data, MxS32 p_loopCount) +// FUNCTION: LEGO1 0x100c09c0 +undefined4 MxMusicManager::InitializeMIDI(MxU8* p_data, MxS32 p_loopCount) { - // TODO - return 0; + MxResult result = FAILURE; + + m_criticalSection.Enter(); + + if (!m_midiInitialized) { + MxU32 total = midiOutGetNumDevs(); + MxU32 device = 0; + + for (; device < total; device++) { + MIDIOUTCAPSA caps; + midiOutGetDevCapsA(device, &caps, sizeof(MIDIOUTCAPSA)); + if (caps.wTechnology == MOD_FMSYNTH) + break; + } + + if (device >= total) + device = -1; + + if (midiStreamOpen(&m_midiStreamH, &device, 1, (DWORD) MidiCallbackProc, (DWORD) this, CALLBACK_FUNCTION) != + MMSYSERR_NOERROR) + goto done; + + GetMIDIVolume(m_midiVolume); + + m_midiHdrP = new MIDIHDR(); + if (!m_midiHdrP) + goto done; + + memset(m_midiHdrP, 0, sizeof(MIDIHDR)); + + MIDIPROPTIMEDIV timediv; + timediv.cbStruct = 8; + m_bufferOffset = p_data; + m_bufferOffset += 0x14; + timediv.dwTimeDiv = *((DWORD*) m_bufferOffset); + + if (midiStreamProperty(m_midiStreamH, (LPBYTE) &timediv, MIDIPROP_SET | MIDIPROP_TIMEDIV) != MMSYSERR_NOERROR) + goto done; + + m_bufferOffset += 0x14; + m_bufferSize = *((MxU32*) m_bufferOffset); + m_bufferOffset += sizeof(MxU32); + m_loopCount = p_loopCount; + m_midiInitialized = TRUE; + + ResetBuffer(); + if (ResetStream() != SUCCESS) + goto done; + + SetMIDIVolume(); + if (midiStreamRestart(m_midiStreamH) != MMSYSERR_NOERROR) + goto done; + + result = SUCCESS; + } + +done: + m_criticalSection.Leave(); + return result; } // FUNCTION: LEGO1 0x100c0b20 diff --git a/util/compat.h b/util/compat.h index b38a1c7c..a5b7a487 100644 --- a/util/compat.h +++ b/util/compat.h @@ -15,6 +15,11 @@ #define COMPAT_CONST #endif +// DWORD_PTR didn't exist in older Windows SDKs +#if (defined(_MSC_VER) && _MSC_VER < 1100) +typedef unsigned long DWORD_PTR, *PDWORD_PTR; +#endif + // Disable "identifier was truncated to '255' characters" warning. // Impossible to avoid this if using STL map or set. // This removes most (but not all) occurrences of the warning.