- cleaned up the interdependencies between the MIDIStreamer and the WinMIDIDevice classes.

A major part of this device's implementation details about how to handle the callback were not encapsulated by the device class at all, they were #ifdef'd into the streamer class.
This puts everything into the device class which now exposes a clean interface to the rest of the game with no special handling aside from calling two additional virtual functions that are empty for the other devices
This commit is contained in:
Christoph Oelckers 2017-03-10 19:03:58 +01:00
parent 8d6fe24945
commit 5374eed06d
8 changed files with 215 additions and 316 deletions

View file

@ -74,7 +74,8 @@ public:
virtual int UnprepareHeader(MidiHeader *data); virtual int UnprepareHeader(MidiHeader *data);
virtual bool FakeVolume(); virtual bool FakeVolume();
virtual bool Pause(bool paused) = 0; virtual bool Pause(bool paused) = 0;
virtual bool NeedThreadedCallback(); virtual void InitPlayback();
virtual bool Update();
virtual void PrecacheInstruments(const uint16_t *instruments, int count); virtual void PrecacheInstruments(const uint16_t *instruments, int count);
virtual void TimidityVolumeChanged(); virtual void TimidityVolumeChanged();
virtual void FluidSettingInt(const char *setting, int value); virtual void FluidSettingInt(const char *setting, int value);
@ -357,21 +358,20 @@ public:
void FluidSettingStr(const char *setting, const char *value); void FluidSettingStr(const char *setting, const char *value);
void WildMidiSetOption(int opt, int set); void WildMidiSetOption(int opt, int set);
void CreateSMF(TArray<uint8_t> &file, int looplimit=0); void CreateSMF(TArray<uint8_t> &file, int looplimit=0);
int ServiceEvent();
protected: protected:
MIDIStreamer(const char *dumpname, EMidiDevice type); MIDIStreamer(const char *dumpname, EMidiDevice type);
bool CheckExitEvent();
void OutputVolume (uint32_t volume); void OutputVolume (uint32_t volume);
int FillBuffer(int buffer_num, int max_events, uint32_t max_time); int FillBuffer(int buffer_num, int max_events, uint32_t max_time);
int FillStopBuffer(int buffer_num); int FillStopBuffer(int buffer_num);
uint32_t *WriteStopNotes(uint32_t *events); uint32_t *WriteStopNotes(uint32_t *events);
int ServiceEvent();
int VolumeControllerChange(int channel, int volume); int VolumeControllerChange(int channel, int volume);
int ClampLoopCount(int loopcount); int ClampLoopCount(int loopcount);
void SetTempo(int new_tempo); void SetTempo(int new_tempo);
static EMidiDevice SelectMIDIDevice(EMidiDevice devtype); static EMidiDevice SelectMIDIDevice(EMidiDevice devtype);
MIDIDevice *CreateMIDIDevice(EMidiDevice devtype) const; MIDIDevice *CreateMIDIDevice(EMidiDevice devtype);
static void Callback(void *userdata); static void Callback(void *userdata);

View file

@ -122,6 +122,8 @@ CUSTOM_CVAR (Float, snd_sfxvolume, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOIN
} }
} }
class MIDIStreamer;
class NullSoundRenderer : public SoundRenderer class NullSoundRenderer : public SoundRenderer
{ {
public: public:

View file

@ -131,10 +131,6 @@ extern char MIDI_CommonLengths[15];
HMISong::HMISong (FileReader &reader, EMidiDevice type, const char *args) HMISong::HMISong (FileReader &reader, EMidiDevice type, const char *args)
: MIDIStreamer(type, args), MusHeader(0), Tracks(0) : MIDIStreamer(type, args), MusHeader(0), Tracks(0)
{ {
if (!CheckExitEvent())
{
return;
}
int len = reader.GetLength(); int len = reader.GetLength();
if (len < 0x100) if (len < 0x100)
{ // Way too small to be HMI. { // Way too small to be HMI.

View file

@ -36,15 +36,6 @@
#include "i_midi_win32.h" #include "i_midi_win32.h"
/*
#ifdef _WIN32
DWORD PlayerLoop();
HANDLE PlayerThread;
HANDLE ExitEvent;
HANDLE BufferDoneEvent;
#endif
*/
#include "i_musicinterns.h" #include "i_musicinterns.h"
#include "templates.h" #include "templates.h"
@ -73,8 +64,6 @@ EXTERN_CVAR(Float, snd_musicvolume)
EXTERN_CVAR(Int, snd_mididevice) EXTERN_CVAR(Int, snd_mididevice)
#ifdef _WIN32 #ifdef _WIN32
DWORD WINAPI PlayerProc(LPVOID lpParameter);
extern unsigned mididevice; extern unsigned mididevice;
#endif #endif
@ -105,27 +94,9 @@ static const uint8_t StaticMIDIhead[] =
MIDIStreamer::MIDIStreamer(EMidiDevice type, const char *args) MIDIStreamer::MIDIStreamer(EMidiDevice type, const char *args)
: :
#ifdef _WIN32
//PlayerThread(0), ExitEvent(0), BufferDoneEvent(0),
#endif
MIDI(0), Division(0), InitialTempo(500000), DeviceType(type), Args(args) MIDI(0), Division(0), InitialTempo(500000), DeviceType(type), Args(args)
{ {
memset(Buffer, 0, sizeof(Buffer)); memset(Buffer, 0, sizeof(Buffer));
/*
#ifdef _WIN32
BufferDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (BufferDoneEvent == NULL)
{
Printf(PRINT_BOLD, "Could not create buffer done event for MIDI playback\n");
}
ExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (ExitEvent == NULL)
{
Printf(PRINT_BOLD, "Could not create exit event for MIDI playback\n");
return;
}
#endif
*/
} }
//========================================================================== //==========================================================================
@ -136,18 +107,9 @@ MIDIStreamer::MIDIStreamer(EMidiDevice type, const char *args)
MIDIStreamer::MIDIStreamer(const char *dumpname, EMidiDevice type) MIDIStreamer::MIDIStreamer(const char *dumpname, EMidiDevice type)
: :
#ifdef _WIN32
//PlayerThread(0), ExitEvent(0), BufferDoneEvent(0),
#endif
MIDI(0), Division(0), InitialTempo(500000), DeviceType(type), DumpFilename(dumpname) MIDI(0), Division(0), InitialTempo(500000), DeviceType(type), DumpFilename(dumpname)
{ {
memset(Buffer, 0, sizeof(Buffer)); memset(Buffer, 0, sizeof(Buffer));
/*
#ifdef _WIN32
BufferDoneEvent = NULL;
ExitEvent = NULL;
#endif
*/
} }
//========================================================================== //==========================================================================
@ -159,18 +121,6 @@ MIDIStreamer::MIDIStreamer(const char *dumpname, EMidiDevice type)
MIDIStreamer::~MIDIStreamer() MIDIStreamer::~MIDIStreamer()
{ {
Stop(); Stop();
/*
#ifdef _WIN32
if (ExitEvent != NULL)
{
CloseHandle(ExitEvent);
}
if (BufferDoneEvent != NULL)
{
CloseHandle(BufferDoneEvent);
}
#endif
*/
if (MIDI != NULL) if (MIDI != NULL)
{ {
delete MIDI; delete MIDI;
@ -198,11 +148,7 @@ bool MIDIStreamer::IsMIDI() const
bool MIDIStreamer::IsValid() const bool MIDIStreamer::IsValid() const
{ {
#ifdef _WIN32
return /*ExitEvent != NULL &&*/ Division != 0;
#else
return Division != 0; return Division != 0;
#endif
} }
//========================================================================== //==========================================================================
@ -278,7 +224,7 @@ EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device)
// //
//========================================================================== //==========================================================================
MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype)
{ {
switch (devtype) switch (devtype)
{ {
@ -331,7 +277,6 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const
void MIDIStreamer::Play(bool looping, int subsong) void MIDIStreamer::Play(bool looping, int subsong)
{ {
DWORD tid;
EMidiDevice devtype; EMidiDevice devtype;
m_Status = STATE_Stopped; m_Status = STATE_Stopped;
@ -359,10 +304,6 @@ void MIDIStreamer::Play(bool looping, int subsong)
MIDI = CreateMIDIDevice(devtype); MIDI = CreateMIDIDevice(devtype);
} }
#ifndef _WIN32
assert(MIDI == NULL || MIDI->NeedThreadedCallback() == false);
#endif
if (MIDI == NULL || 0 != MIDI->Open(Callback, this)) if (MIDI == NULL || 0 != MIDI->Open(Callback, this))
{ {
Printf(PRINT_BOLD, "Could not open MIDI out device\n"); Printf(PRINT_BOLD, "Could not open MIDI out device\n");
@ -392,29 +333,9 @@ void MIDIStreamer::Play(bool looping, int subsong)
Stop(); Stop();
} }
else else
{
/*
#ifdef _WIN32
if (MIDI->NeedThreadedCallback())
{
PlayerThread = CreateThread(NULL, 0, PlayerProc, this, 0, &tid);
if (PlayerThread == NULL)
{
Printf ("Creating MIDI thread failed\n");
Stop();
}
else
{ {
m_Status = STATE_Playing; m_Status = STATE_Playing;
} }
}
else
#endif
*/
{
m_Status = STATE_Playing;
}
}
} }
//========================================================================== //==========================================================================
@ -440,12 +361,7 @@ void MIDIStreamer::StartPlayback()
MusicVolumeChanged(); // set volume to current music's properties MusicVolumeChanged(); // set volume to current music's properties
OutputVolume(Volume); OutputVolume(Volume);
/* MIDI->InitPlayback();
#ifdef _WIN32
ResetEvent(ExitEvent);
ResetEvent(BufferDoneEvent);
#endif
*/
// Fill the initial buffers for the song. // Fill the initial buffers for the song.
BufferNum = 0; BufferNum = 0;
@ -532,17 +448,6 @@ void MIDIStreamer::Stop()
{ {
EndQueued = 4; EndQueued = 4;
/*
#ifdef _WIN32
if (PlayerThread != NULL)
{
SetEvent(ExitEvent);
WaitForSingleObject(PlayerThread, INFINITE);
CloseHandle(PlayerThread);
PlayerThread = NULL;
}
#endif
*/
if (MIDI != NULL && MIDI->IsOpen()) if (MIDI != NULL && MIDI->IsOpen())
{ {
MIDI->Stop(); MIDI->Stop();
@ -714,10 +619,6 @@ int MIDIStreamer::VolumeControllerChange(int channel, int volume)
// //
// MIDIStreamer :: Callback Static // MIDIStreamer :: Callback Static
// //
// Signals the BufferDoneEvent to prepare the next buffer. The buffer is not
// prepared in the callback directly, because it's generally still in use by
// the MIDI streamer when this callback is executed.
//
//========================================================================== //==========================================================================
void MIDIStreamer::Callback(void *userdata) void MIDIStreamer::Callback(void *userdata)
@ -728,18 +629,7 @@ void MIDIStreamer::Callback(void *userdata)
{ {
return; return;
} }
/*
#ifdef _WIN32
if (self->PlayerThread != NULL)
{
SetEvent(self->BufferDoneEvent);
}
else
#endif
*/
{
self->ServiceEvent(); self->ServiceEvent();
}
} }
//========================================================================== //==========================================================================
@ -753,128 +643,9 @@ void MIDIStreamer::Callback(void *userdata)
void MIDIStreamer::Update() void MIDIStreamer::Update()
{ {
/* if (!MIDI->Update()) Stop();
#ifdef _WIN32
// If the PlayerThread is signalled, then it's dead.
if (PlayerThread != NULL &&
WaitForSingleObject(PlayerThread, 0) == WAIT_OBJECT_0)
{
static const char *const MMErrorCodes[] =
{
"No error",
"Unspecified error",
"Device ID out of range",
"Driver failed enable",
"Device already allocated",
"Device handle is invalid",
"No device driver present",
"Memory allocation error",
"Function isn't supported",
"Error value out of range",
"Invalid flag passed",
"Invalid parameter passed",
"Handle being used simultaneously on another thread",
"Specified alias not found",
"Bad registry database",
"Registry key not found",
"Registry read error",
"Registry write error",
"Registry delete error",
"Registry value not found",
"Driver does not call DriverCallback",
"More data to be returned",
};
static const char *const MidiErrorCodes[] =
{
"MIDI header not prepared",
"MIDI still playing something",
"MIDI no configured instruments",
"MIDI hardware is still busy",
"MIDI port no longer connected",
"MIDI invalid MIF",
"MIDI operation unsupported with open mode",
"MIDI through device 'eating' a message",
};
DWORD code = 0xABADCAFE;
GetExitCodeThread(PlayerThread, &code);
CloseHandle(PlayerThread);
PlayerThread = NULL;
Printf ("MIDI playback failure: ");
if (code < countof(MMErrorCodes))
{
Printf("%s\n", MMErrorCodes[code]);
}
else if (code >= MIDIERR_BASE && code < MIDIERR_BASE + countof(MidiErrorCodes))
{
Printf("%s\n", MidiErrorCodes[code - MIDIERR_BASE]);
}
else
{
Printf("%08x\n", code);
}
Stop();
}
#endif
*/
} }
//==========================================================================
//
// MIDIStreamer :: PlayerProc Static
//
// Entry point for the player thread.
//
//==========================================================================
#ifdef _WIN32
DWORD WINAPI PlayerProc(LPVOID lpParameter)
{
// return ((MIDIStreamer *)lpParameter)->PlayerLoop();
return 0;
}
#endif
//==========================================================================
//
// MIDIStreamer :: PlayerLoop
//
// Services MIDI playback events.
//
//==========================================================================
/*
#ifdef _WIN32
DWORD MIDIStreamer::PlayerLoop()
{
HANDLE events[2] = { BufferDoneEvent, ExitEvent };
int res;
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
for (;;)
{
switch (WaitForMultipleObjects(2, events, FALSE, INFINITE))
{
case WAIT_OBJECT_0:
if (0 != (res = ServiceEvent()))
{
return res;
}
break;
case WAIT_OBJECT_0 + 1:
return 0;
default:
// Should not happen.
return MMSYSERR_ERROR;
}
}
return 0;
}
#endif
*/
//========================================================================== //==========================================================================
// //
// MIDIStreamer :: ServiceEvent // MIDIStreamer :: ServiceEvent
@ -913,8 +684,8 @@ fill:
switch (res & 3) switch (res & 3)
{ {
case SONG_MORE: case SONG_MORE:
if ((MIDI->NeedThreadedCallback() && 0 != (res = MIDI->StreamOutSync(&Buffer[BufferNum]))) || res = MIDI->StreamOut(&Buffer[BufferNum]);
(!MIDI->NeedThreadedCallback() && 0 != (res = MIDI->StreamOut(&Buffer[BufferNum])))) if (res != 0)
{ {
return res; return res;
} }
@ -1433,27 +1204,6 @@ bool MIDIStreamer::SetMIDISubsong(int subsong)
return subsong == 0; return subsong == 0;
} }
//==========================================================================
//
// MIDIStreamer :: CheckExitEvent
//
//
//
//==========================================================================
bool MIDIStreamer::CheckExitEvent()
{
/*
#ifdef _WIN32
if (ExitEvent == NULL)
{
return false;
}
#endif
*/
return true;
}
//========================================================================== //==========================================================================
// //
// MIDIDevice stubs. // MIDIDevice stubs.
@ -1545,16 +1295,23 @@ bool MIDIDevice::FakeVolume()
//========================================================================== //==========================================================================
// //
// MIDIDevice :: NeedThreadedCallabck
// //
// Most implementations can service the callback directly rather than using
// a separate thread.
// //
//========================================================================== //==========================================================================
bool MIDIDevice::NeedThreadedCallback() void MIDIDevice::InitPlayback()
{ {
return false; }
//==========================================================================
//
//
//
//==========================================================================
bool MIDIDevice::Update()
{
return true;
} }
//========================================================================== //==========================================================================

View file

@ -96,11 +96,6 @@ static const uint8_t CtrlTranslate[15] =
MUSSong2::MUSSong2 (FileReader &reader, EMidiDevice type, const char *args) MUSSong2::MUSSong2 (FileReader &reader, EMidiDevice type, const char *args)
: MIDIStreamer(type, args), MusHeader(0), MusBuffer(0) : MIDIStreamer(type, args), MusHeader(0), MusBuffer(0)
{ {
if (!CheckExitEvent())
{
return;
}
uint8_t front[32]; uint8_t front[32];
int start; int start;

View file

@ -108,10 +108,6 @@ MIDISong2::MIDISong2 (FileReader &reader, EMidiDevice type, const char *args)
int p; int p;
int i; int i;
if (!CheckExitEvent())
{
return;
}
SongLen = reader.GetLength(); SongLen = reader.GetLength();
MusHeader = new uint8_t[SongLen]; MusHeader = new uint8_t[SongLen];
if (reader.Read(MusHeader, SongLen) != SongLen) if (reader.Read(MusHeader, SongLen) != SongLen)

View file

@ -34,19 +34,7 @@
#ifdef _WIN32 #ifdef _WIN32
#define WIN32_LEAN_AND_MEAN #include "i_midi_win32.h"
#define USE_WINDOWS_DWORD
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0400
#undef _WIN32_WINNT
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#endif
#ifndef USE_WINDOWS_DWORD
#define USE_WINDOWS_DWORD
#endif
#include <windows.h>
#include <mmsystem.h>
// HEADER FILES ------------------------------------------------------------ // HEADER FILES ------------------------------------------------------------
@ -92,13 +80,16 @@ public:
int PrepareHeader(MidiHeader *data); int PrepareHeader(MidiHeader *data);
int UnprepareHeader(MidiHeader *data); int UnprepareHeader(MidiHeader *data);
bool FakeVolume(); bool FakeVolume();
bool NeedThreadedCallback();
bool Pause(bool paused); bool Pause(bool paused);
void InitPlayback() override;
bool Update() override;
void PrecacheInstruments(const uint16_t *instruments, int count); void PrecacheInstruments(const uint16_t *instruments, int count);
DWORD PlayerLoop();
protected: //protected:
static void CALLBACK CallbackFunc(HMIDIOUT, UINT, DWORD_PTR, DWORD, DWORD); static void CALLBACK CallbackFunc(HMIDIOUT, UINT, DWORD_PTR, DWORD, DWORD);
MIDIStreamer *Streamer;
HMIDISTRM MidiOut; HMIDISTRM MidiOut;
UINT DeviceID; UINT DeviceID;
DWORD SavedVolume; DWORD SavedVolume;
@ -108,6 +99,12 @@ protected:
MidiCallback Callback; MidiCallback Callback;
void *CallbackData; void *CallbackData;
HANDLE BufferDoneEvent;
HANDLE ExitEvent;
HANDLE PlayerThread;
}; };
// PUBLIC DATA DEFINITIONS ------------------------------------------------- // PUBLIC DATA DEFINITIONS -------------------------------------------------
@ -128,6 +125,18 @@ WinMIDIDevice::WinMIDIDevice(int dev_id)
MidiOut = 0; MidiOut = 0;
HeaderIndex = 0; HeaderIndex = 0;
memset(WinMidiHeaders, 0, sizeof(WinMidiHeaders)); memset(WinMidiHeaders, 0, sizeof(WinMidiHeaders));
BufferDoneEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (BufferDoneEvent == nullptr)
{
Printf(PRINT_BOLD, "Could not create buffer done event for MIDI playback\n");
}
ExitEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (ExitEvent == nullptr)
{
Printf(PRINT_BOLD, "Could not create exit event for MIDI playback\n");
}
PlayerThread = nullptr;
} }
//========================================================================== //==========================================================================
@ -139,6 +148,15 @@ WinMIDIDevice::WinMIDIDevice(int dev_id)
WinMIDIDevice::~WinMIDIDevice() WinMIDIDevice::~WinMIDIDevice()
{ {
Close(); Close();
if (ExitEvent != nullptr)
{
CloseHandle(ExitEvent);
}
if (BufferDoneEvent != nullptr)
{
CloseHandle(BufferDoneEvent);
}
} }
//========================================================================== //==========================================================================
@ -153,7 +171,7 @@ int WinMIDIDevice::Open(MidiCallback callback, void *userdata)
Callback = callback; Callback = callback;
CallbackData = userdata; CallbackData = userdata;
if (MidiOut == NULL) if (MidiOut == nullptr)
{ {
err = midiStreamOpen(&MidiOut, &DeviceID, 1, (DWORD_PTR)CallbackFunc, (DWORD_PTR)this, CALLBACK_FUNCTION); err = midiStreamOpen(&MidiOut, &DeviceID, 1, (DWORD_PTR)CallbackFunc, (DWORD_PTR)this, CALLBACK_FUNCTION);
@ -185,10 +203,10 @@ int WinMIDIDevice::Open(MidiCallback callback, void *userdata)
void WinMIDIDevice::Close() void WinMIDIDevice::Close()
{ {
if (MidiOut != NULL) if (MidiOut != nullptr)
{ {
midiStreamClose(MidiOut); midiStreamClose(MidiOut);
MidiOut = NULL; MidiOut = nullptr;
} }
} }
@ -200,7 +218,7 @@ void WinMIDIDevice::Close()
bool WinMIDIDevice::IsOpen() const bool WinMIDIDevice::IsOpen() const
{ {
return MidiOut != NULL; return MidiOut != nullptr;
} }
//========================================================================== //==========================================================================
@ -244,6 +262,19 @@ int WinMIDIDevice::SetTimeDiv(int timediv)
return midiStreamProperty(MidiOut, (LPBYTE)&data, MIDIPROP_SET | MIDIPROP_TIMEDIV); return midiStreamProperty(MidiOut, (LPBYTE)&data, MIDIPROP_SET | MIDIPROP_TIMEDIV);
} }
//==========================================================================
//
// MIDIStreamer :: PlayerProc Static
//
// Entry point for the player thread.
//
//==========================================================================
DWORD WINAPI PlayerProc(LPVOID lpParameter)
{
return ((WinMIDIDevice *)lpParameter)->PlayerLoop();
}
//========================================================================== //==========================================================================
// //
// WinMIDIDevice :: Resume // WinMIDIDevice :: Resume
@ -252,7 +283,31 @@ int WinMIDIDevice::SetTimeDiv(int timediv)
int WinMIDIDevice::Resume() int WinMIDIDevice::Resume()
{ {
return midiStreamRestart(MidiOut); DWORD tid;
int ret = midiStreamRestart(MidiOut);
if (ret == 0)
{
PlayerThread = CreateThread(nullptr, 0, PlayerProc, this, 0, &tid);
if (PlayerThread == nullptr)
{
Printf("Creating MIDI thread failed\n");
Stop();
return MMSYSERR_NOTSUPPORTED;
}
}
return ret;
}
//==========================================================================
//
// WinMIDIDevice :: InitPlayback
//
//==========================================================================
void WinMIDIDevice::InitPlayback()
{
ResetEvent(ExitEvent);
ResetEvent(BufferDoneEvent);
} }
//========================================================================== //==========================================================================
@ -263,6 +318,14 @@ int WinMIDIDevice::Resume()
void WinMIDIDevice::Stop() void WinMIDIDevice::Stop()
{ {
if (PlayerThread != nullptr)
{
SetEvent(ExitEvent);
WaitForSingleObject(PlayerThread, INFINITE);
CloseHandle(PlayerThread);
PlayerThread = nullptr;
}
midiStreamStop(MidiOut); midiStreamStop(MidiOut);
midiOutReset((HMIDIOUT)MidiOut); midiOutReset((HMIDIOUT)MidiOut);
if (VolumeWorks) if (VolumeWorks)
@ -271,6 +334,40 @@ void WinMIDIDevice::Stop()
} }
} }
//==========================================================================
//
// MIDIStreamer :: PlayerLoop
//
// Services MIDI playback events.
//
//==========================================================================
DWORD WinMIDIDevice::PlayerLoop()
{
HANDLE events[2] = { BufferDoneEvent, ExitEvent };
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
for (;;)
{
switch (WaitForMultipleObjects(2, events, FALSE, INFINITE))
{
case WAIT_OBJECT_0:
if (Callback != nullptr) Callback(CallbackData);
break;
case WAIT_OBJECT_0 + 1:
return 0;
default:
// Should not happen.
return MMSYSERR_ERROR;
}
}
return 0;
}
//========================================================================== //==========================================================================
// //
// WinMIDIDevice :: PrecacheInstruments // WinMIDIDevice :: PrecacheInstruments
@ -391,9 +488,7 @@ int WinMIDIDevice::StreamOut(MidiHeader *header)
int WinMIDIDevice::StreamOutSync(MidiHeader *header) int WinMIDIDevice::StreamOutSync(MidiHeader *header)
{ {
auto syshdr = (MIDIHDR*)header->lpNext; return StreamOut(header);
assert(syshdr == &WinMidiHeaders[0] || syshdr == &WinMidiHeaders[1]);
return midiStreamOut(MidiOut, syshdr, sizeof(MIDIHDR));
} }
//========================================================================== //==========================================================================
@ -454,15 +549,71 @@ bool WinMIDIDevice::FakeVolume()
//========================================================================== //==========================================================================
// //
// WinMIDIDevice :: NeedThreadedCallback // WinMIDIDevice :: Update
//
// When using the MM system, the callback can't yet touch the buffer, so
// the real processing needs to happen in a different thread.
// //
//========================================================================== //==========================================================================
bool WinMIDIDevice::NeedThreadedCallback() bool WinMIDIDevice::Update()
{ {
// If the PlayerThread is signalled, then it's dead.
if (PlayerThread != nullptr &&
WaitForSingleObject(PlayerThread, 0) == WAIT_OBJECT_0)
{
static const char *const MMErrorCodes[] =
{
"No error",
"Unspecified error",
"Device ID out of range",
"Driver failed enable",
"Device already allocated",
"Device handle is invalid",
"No device driver present",
"Memory allocation error",
"Function isn't supported",
"Error value out of range",
"Invalid flag passed",
"Invalid parameter passed",
"Handle being used simultaneously on another thread",
"Specified alias not found",
"Bad registry database",
"Registry key not found",
"Registry read error",
"Registry write error",
"Registry delete error",
"Registry value not found",
"Driver does not call DriverCallback",
"More data to be returned",
};
static const char *const MidiErrorCodes[] =
{
"MIDI header not prepared",
"MIDI still playing something",
"MIDI no configured instruments",
"MIDI hardware is still busy",
"MIDI port no longer connected",
"MIDI invalid MIF",
"MIDI operation unsupported with open mode",
"MIDI through device 'eating' a message",
};
DWORD code = 0xABADCAFE;
GetExitCodeThread(PlayerThread, &code);
CloseHandle(PlayerThread);
PlayerThread = nullptr;
Printf("MIDI playback failure: ");
if (code < countof(MMErrorCodes))
{
Printf("%s\n", MMErrorCodes[code]);
}
else if (code >= MIDIERR_BASE && code < MIDIERR_BASE + countof(MidiErrorCodes))
{
Printf("%s\n", MidiErrorCodes[code - MIDIERR_BASE]);
}
else
{
Printf("%08x\n", code);
}
return false;
}
return true; return true;
} }
@ -475,9 +626,9 @@ bool WinMIDIDevice::NeedThreadedCallback()
void CALLBACK WinMIDIDevice::CallbackFunc(HMIDIOUT hOut, UINT uMsg, DWORD_PTR dwInstance, DWORD dwParam1, DWORD dwParam2) void CALLBACK WinMIDIDevice::CallbackFunc(HMIDIOUT hOut, UINT uMsg, DWORD_PTR dwInstance, DWORD dwParam1, DWORD dwParam2)
{ {
WinMIDIDevice *self = (WinMIDIDevice *)dwInstance; WinMIDIDevice *self = (WinMIDIDevice *)dwInstance;
if (self->Callback != NULL && uMsg == MOM_DONE) if (uMsg == MOM_DONE)
{ {
self->Callback(self->CallbackData); SetEvent(self->BufferDoneEvent);
} }
} }
@ -524,9 +675,9 @@ static bool IgnoreMIDIVolume(UINT id)
// Now try to create an IMMDeviceEnumerator interface. If it succeeds, // Now try to create an IMMDeviceEnumerator interface. If it succeeds,
// we know we're using the new audio stack introduced with Vista and // we know we're using the new audio stack introduced with Vista and
// should ignore this MIDI device's volume control. // should ignore this MIDI device's volume control.
if (SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, if (SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator), (void**)&enumerator)) __uuidof(IMMDeviceEnumerator), (void**)&enumerator))
&& enumerator != NULL) && enumerator != nullptr)
{ {
enumerator->Release(); enumerator->Release();
return true; return true;
@ -540,7 +691,13 @@ static bool IgnoreMIDIVolume(UINT id)
MIDIDevice *CreateWinMIDIDevice(int mididevice) MIDIDevice *CreateWinMIDIDevice(int mididevice)
{ {
return new WinMIDIDevice(mididevice); auto d = new WinMIDIDevice(mididevice);
if (d->BufferDoneEvent == nullptr || d->ExitEvent == nullptr)
{
delete d;
return nullptr;
}
return d;
} }
#endif #endif

View file

@ -111,10 +111,6 @@ extern char MIDI_CommonLengths[15];
XMISong::XMISong (FileReader &reader, EMidiDevice type, const char *args) XMISong::XMISong (FileReader &reader, EMidiDevice type, const char *args)
: MIDIStreamer(type, args), MusHeader(0), Songs(0) : MIDIStreamer(type, args), MusHeader(0), Songs(0)
{ {
if (!CheckExitEvent())
{
return;
}
SongLen = reader.GetLength(); SongLen = reader.GetLength();
MusHeader = new uint8_t[SongLen]; MusHeader = new uint8_t[SongLen];
if (reader.Read(MusHeader, SongLen) != SongLen) if (reader.Read(MusHeader, SongLen) != SongLen)