Isle
Loading...
Searching...
No Matches
mxmusicmanager.cpp
Go to the documentation of this file.
1#include "mxmusicmanager.h"
2
3#include "mxmisc.h"
4#include "mxticklemanager.h"
5#include "mxticklethread.h"
6
7#include <windows.h>
8
10
11// FUNCTION: LEGO1 0x100c05a0
13{
14 Init();
15}
16
17// FUNCTION: LEGO1 0x100c0630
19{
21}
22
23// FUNCTION: LEGO1 0x100c0690
25{
26 m_multiplier = 100;
27 InitData();
28}
29
30// FUNCTION: LEGO1 0x100c06a0
32{
33 m_midiStreamH = 0;
34 m_midiInitialized = FALSE;
35 m_bufferSize = 0;
36 m_bufferCurrentSize = 0;
37 m_bufferOffset = 0;
38 m_bufferCurrentOffset = 0;
39 m_loopCount = 0;
40 m_midiHdrP = NULL;
41}
42
43// FUNCTION: LEGO1 0x100c06c0
44void MxMusicManager::Destroy(MxBool p_fromDestructor)
45{
46 if (m_thread) {
48 if (m_thread) {
49 delete m_thread;
50 }
51 }
52 else {
54 }
55
58 Init();
60
61 if (!p_fromDestructor) {
63 }
64}
65
66// FUNCTION: LEGO1 0x100c0720
68{
69 MxResult result = FAILURE;
70
71 if (m_midiInitialized) {
72 if (m_bufferCurrentSize == 0) {
73 if (m_loopCount != -1) {
74 m_loopCount += -1;
75
76 if (!m_loopCount) {
78 goto done;
79 }
80 }
81
83 }
84
85 if (m_midiHdrP->dwFlags & MHDR_DONE || m_midiHdrP->dwFlags & MHDR_PREPARED) {
86 if (midiOutUnprepareHeader((HMIDIOUT) m_midiStreamH, m_midiHdrP, sizeof(MIDIHDR)) != MMSYSERR_NOERROR) {
87 goto done;
88 }
89
90 memset(m_midiHdrP, 0, sizeof(MIDIHDR));
91 }
92
93 m_bufferCurrentOffset += 4;
94 DWORD length = *((DWORD*) m_bufferCurrentOffset);
95 m_bufferCurrentOffset += sizeof(DWORD);
96
97 m_midiHdrP->lpData = (LPSTR) m_bufferCurrentOffset;
98 m_midiHdrP->dwBufferLength = length;
99 m_midiHdrP->dwBytesRecorded = length;
100
101 if (!midiOutPrepareHeader((HMIDIOUT) m_midiStreamH, m_midiHdrP, sizeof(MIDIHDR))) {
102 if (!midiStreamOut(m_midiStreamH, m_midiHdrP, sizeof(MIDIHDR))) {
103 result = SUCCESS;
104 m_bufferCurrentOffset += length;
105 m_bufferCurrentSize--;
106 }
107 }
108 }
109
110done:
111 return result;
112}
113
114// FUNCTION: LEGO1 0x100c07e0
116{
117 m_bufferCurrentOffset = m_bufferOffset;
118 m_bufferCurrentSize = m_bufferSize;
119}
120
121// FUNCTION: LEGO1 0x100c07f0
122void MxMusicManager::SetMIDIVolume()
123{
124 MxS32 result = (m_volume * m_multiplier) / 0x64;
125 HMIDISTRM streamHandle = m_midiStreamH;
126
127 if (streamHandle) {
128 MxS32 volume = CalculateVolume(result);
129 midiOutSetVolume((HMIDIOUT) streamHandle, volume);
130 }
131}
132
133// FUNCTION: LEGO1 0x100c0820
134void CALLBACK MxMusicManager::MidiCallbackProc(HDRVR p_hdrvr, UINT p_uMsg, DWORD p_dwUser, DWORD p_dw1, DWORD p_dw2)
135{
136 if (p_uMsg == MOM_DONE) {
137 ((MxMusicManager*) p_dwUser)->ResetStream();
138 }
139}
140
141// FUNCTION: LEGO1 0x100c0840
142MxResult MxMusicManager::Create(MxU32 p_frequencyMS, MxBool p_createThread)
143{
144 MxResult status = FAILURE;
145 MxBool locked = FALSE;
146
148 if (p_createThread) {
150 locked = TRUE;
151 m_thread = new MxTickleThread(this, p_frequencyMS);
152
153 if (!m_thread || m_thread->Start(0, 0) != SUCCESS) {
154 goto done;
155 }
156 }
157 else {
158 TickleManager()->RegisterClient(this, p_frequencyMS);
159 }
160
161 status = SUCCESS;
162 }
163
164done:
165 if (status != SUCCESS) {
166 Destroy();
167 }
168
169 if (locked) {
171 }
172
173 return status;
174}
175
176// FUNCTION: LEGO1 0x100c0930
178{
179 Destroy(FALSE);
180}
181
182// FUNCTION: LEGO1 0x100c0940
184{
187 SetMIDIVolume();
189}
190
191// FUNCTION: LEGO1 0x100c0970
193{
195 m_multiplier = p_multiplier;
196 SetMIDIVolume();
198}
199
200// FUNCTION: LEGO1 0x100c09a0
201MxS32 MxMusicManager::CalculateVolume(MxS32 p_volume)
202{
203 MxS32 result = (p_volume * 0xffff) / 100;
204 return (result << 0x10) | result;
205}
206
207// FUNCTION: LEGO1 0x100c09c0
209{
210 MxResult result = FAILURE;
211
213
214 if (!m_midiInitialized) {
215 MxU32 total = midiOutGetNumDevs();
216 MxU32 device = 0;
217
218 for (; device < total; device++) {
219 MIDIOUTCAPSA caps;
220 midiOutGetDevCapsA(device, &caps, sizeof(MIDIOUTCAPSA));
221 if (caps.wTechnology == MOD_FMSYNTH) {
222 break;
223 }
224 }
225
226 if (device >= total) {
227 device = -1;
228 }
229
230 if (midiStreamOpen(&m_midiStreamH, &device, 1, (DWORD) MidiCallbackProc, (DWORD) this, CALLBACK_FUNCTION) !=
231 MMSYSERR_NOERROR) {
232 goto done;
233 }
234
235 GetMIDIVolume(m_midiVolume);
236
237 m_midiHdrP = new MIDIHDR();
238 if (!m_midiHdrP) {
239 goto done;
240 }
241
242 memset(m_midiHdrP, 0, sizeof(MIDIHDR));
243
244 MIDIPROPTIMEDIV timediv;
245 timediv.cbStruct = 8;
246 m_bufferOffset = p_data;
247 m_bufferOffset += 0x14;
248 timediv.dwTimeDiv = *((DWORD*) m_bufferOffset);
249
250 if (midiStreamProperty(m_midiStreamH, (LPBYTE) &timediv, MIDIPROP_SET | MIDIPROP_TIMEDIV) != MMSYSERR_NOERROR) {
251 goto done;
252 }
253
254 m_bufferOffset += 0x14;
255 m_bufferSize = *((MxU32*) m_bufferOffset);
256 m_bufferOffset += sizeof(MxU32);
257 m_loopCount = p_loopCount;
258 m_midiInitialized = TRUE;
259
260 ResetBuffer();
261 if (ResetStream() != SUCCESS) {
262 goto done;
263 }
264
265 SetMIDIVolume();
266 if (midiStreamRestart(m_midiStreamH) != MMSYSERR_NOERROR) {
267 goto done;
268 }
269
270 result = SUCCESS;
271 }
272
273done:
275 return result;
276}
277
278// FUNCTION: LEGO1 0x100c0b20
280{
282
283 if (m_midiInitialized) {
284 m_midiInitialized = FALSE;
285 midiStreamStop(m_midiStreamH);
286 midiOutUnprepareHeader((HMIDIOUT) m_midiStreamH, m_midiHdrP, sizeof(MIDIHDR));
287 midiOutSetVolume((HMIDIOUT) m_midiStreamH, m_midiVolume);
288 midiStreamClose(m_midiStreamH);
289 delete m_midiHdrP;
290 InitData();
291 }
292
294}
MxResult Create() override
[AI] Initializes audio subsystem resources and registers an instance for global audio management.
virtual void SetVolume(MxS32 p_volume)
[AI] Sets the current global audio volume.
MxS32 m_volume
[AI] Holds the current global audio volume for the game.
void Destroy() override
[AI] Tears down the audio subsystem instance and unregisters it from global management.
void Enter()
[AI] Acquires/gains entry to the critical section or mutex, blocking if not available.
void Leave()
[AI] Releases/leaves the critical section or mutex.
MxCriticalSection m_criticalSection
[AI] Critical section object used for guarding access to the presenter list and internal members for ...
MxThread * m_thread
[AI] Optional pointer to a worker thread used for media dispatch/IO (if multi-threaded operation is u...
Manages MIDI music playback with Win32 MIDI streaming for the LEGO Island engine.
void InitData()
Initializes and zeros all associated MIDI-related member data.
MxMusicManager()
Constructs an instance and initializes member data.
void Destroy() override
Releases all resources and stops streaming.
void DeinitializeMIDI()
Stops playback and releases all MIDI streaming resources.
void SetMultiplier(MxS32 p_multiplier)
Sets the volume multiplier for scaling volume output.
MxResult ResetStream()
Schedules and streams the next chunk of MIDI data; handles buffer looping and completion.
void Init()
Initializes multipliers and buffer with default values; called from constructor/Destroy.
MxResult InitializeMIDI(MxU8 *p_data, MxS32 p_loopCount)
Initializes streaming for a MIDI data buffer and begins playback.
void GetMIDIVolume(DWORD &p_volume)
Retrieves the current hardware MIDI out volume.
void SetVolume(MxS32 p_volume) override
Sets the music playback volume, taking into account multiplier for overall gain adjustment.
void ResetBuffer()
Resets the MIDI buffer to the starting offset and sets the buffer size to the full stream.
~MxMusicManager() override
Destructor, ensures proper cleanup of resources and MIDI deinitialization.
void Terminate()
[AI] Signals the thread to terminate.
Definition: mxthread.cpp:50
MxResult Start(MxS32 p_stackSize, MxS32 p_flag)
[AI] Starts the thread with a given stack size and creation flags.
Definition: mxthread.cpp:28
virtual void UnregisterClient(MxCore *p_client)
[AI] Unregisters (marks for destruction) a previously registered client.
virtual void RegisterClient(MxCore *p_client, MxTime p_interval)
[AI] Registers an MxCore object to receive periodic tickles.
MxTickleThread periodically calls Tickle() on a target MxCore object in a separate thread.
#define TRUE
Definition: d3drmdef.h:28
#define FALSE
Definition: d3drmdef.h:27
typedef DWORD(FAR PASCAL *LPCLIPPERCALLBACK)(LPDIRECTDRAWCLIPPER lpDDClipper
#define DECOMP_SIZE_ASSERT(T, S)
Definition: decomp.h:19
#define NULL
[AI] Null pointer value (C/C++ semantics).
Definition: legotypes.h:26
#define FAILURE
[AI] Used to indicate a failed operation in result codes.
Definition: legotypes.h:34
#define SUCCESS
[AI] Used to indicate a successful operation in result codes.
Definition: legotypes.h:30
MxTickleManager * TickleManager()
[AI] Provides access to the global tickle manager.
Definition: mxmisc.cpp:25
MxU8 MxBool
[AI]
Definition: mxtypes.h:124
MxLong MxResult
[AI]
Definition: mxtypes.h:106
unsigned char MxU8
[AI]
Definition: mxtypes.h:8
signed int MxS32
[AI]
Definition: mxtypes.h:38
unsigned int MxU32
[AI]
Definition: mxtypes.h:32