mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-10 23:02:08 +00:00
- 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:
parent
8d6fe24945
commit
5374eed06d
8 changed files with 215 additions and 316 deletions
|
@ -74,7 +74,8 @@ public:
|
|||
virtual int UnprepareHeader(MidiHeader *data);
|
||||
virtual bool FakeVolume();
|
||||
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 TimidityVolumeChanged();
|
||||
virtual void FluidSettingInt(const char *setting, int value);
|
||||
|
@ -357,21 +358,20 @@ public:
|
|||
void FluidSettingStr(const char *setting, const char *value);
|
||||
void WildMidiSetOption(int opt, int set);
|
||||
void CreateSMF(TArray<uint8_t> &file, int looplimit=0);
|
||||
int ServiceEvent();
|
||||
|
||||
protected:
|
||||
MIDIStreamer(const char *dumpname, EMidiDevice type);
|
||||
|
||||
bool CheckExitEvent();
|
||||
void OutputVolume (uint32_t volume);
|
||||
int FillBuffer(int buffer_num, int max_events, uint32_t max_time);
|
||||
int FillStopBuffer(int buffer_num);
|
||||
uint32_t *WriteStopNotes(uint32_t *events);
|
||||
int ServiceEvent();
|
||||
int VolumeControllerChange(int channel, int volume);
|
||||
int ClampLoopCount(int loopcount);
|
||||
void SetTempo(int new_tempo);
|
||||
static EMidiDevice SelectMIDIDevice(EMidiDevice devtype);
|
||||
MIDIDevice *CreateMIDIDevice(EMidiDevice devtype) const;
|
||||
MIDIDevice *CreateMIDIDevice(EMidiDevice devtype);
|
||||
|
||||
static void Callback(void *userdata);
|
||||
|
||||
|
|
|
@ -122,6 +122,8 @@ CUSTOM_CVAR (Float, snd_sfxvolume, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOIN
|
|||
}
|
||||
}
|
||||
|
||||
class MIDIStreamer;
|
||||
|
||||
class NullSoundRenderer : public SoundRenderer
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -131,10 +131,6 @@ extern char MIDI_CommonLengths[15];
|
|||
HMISong::HMISong (FileReader &reader, EMidiDevice type, const char *args)
|
||||
: MIDIStreamer(type, args), MusHeader(0), Tracks(0)
|
||||
{
|
||||
if (!CheckExitEvent())
|
||||
{
|
||||
return;
|
||||
}
|
||||
int len = reader.GetLength();
|
||||
if (len < 0x100)
|
||||
{ // Way too small to be HMI.
|
||||
|
|
|
@ -36,15 +36,6 @@
|
|||
|
||||
|
||||
#include "i_midi_win32.h"
|
||||
/*
|
||||
#ifdef _WIN32
|
||||
DWORD PlayerLoop();
|
||||
|
||||
HANDLE PlayerThread;
|
||||
HANDLE ExitEvent;
|
||||
HANDLE BufferDoneEvent;
|
||||
#endif
|
||||
*/
|
||||
|
||||
#include "i_musicinterns.h"
|
||||
#include "templates.h"
|
||||
|
@ -73,8 +64,6 @@ EXTERN_CVAR(Float, snd_musicvolume)
|
|||
EXTERN_CVAR(Int, snd_mididevice)
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD WINAPI PlayerProc(LPVOID lpParameter);
|
||||
|
||||
extern unsigned mididevice;
|
||||
#endif
|
||||
|
||||
|
@ -105,27 +94,9 @@ static const uint8_t StaticMIDIhead[] =
|
|||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
:
|
||||
#ifdef _WIN32
|
||||
//PlayerThread(0), ExitEvent(0), BufferDoneEvent(0),
|
||||
#endif
|
||||
MIDI(0), Division(0), InitialTempo(500000), DeviceType(type), DumpFilename(dumpname)
|
||||
{
|
||||
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()
|
||||
{
|
||||
Stop();
|
||||
/*
|
||||
#ifdef _WIN32
|
||||
if (ExitEvent != NULL)
|
||||
{
|
||||
CloseHandle(ExitEvent);
|
||||
}
|
||||
if (BufferDoneEvent != NULL)
|
||||
{
|
||||
CloseHandle(BufferDoneEvent);
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
if (MIDI != NULL)
|
||||
{
|
||||
delete MIDI;
|
||||
|
@ -198,11 +148,7 @@ bool MIDIStreamer::IsMIDI() const
|
|||
|
||||
bool MIDIStreamer::IsValid() const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return /*ExitEvent != NULL &&*/ Division != 0;
|
||||
#else
|
||||
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)
|
||||
{
|
||||
|
@ -331,7 +277,6 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const
|
|||
|
||||
void MIDIStreamer::Play(bool looping, int subsong)
|
||||
{
|
||||
DWORD tid;
|
||||
EMidiDevice devtype;
|
||||
|
||||
m_Status = STATE_Stopped;
|
||||
|
@ -359,10 +304,6 @@ void MIDIStreamer::Play(bool looping, int subsong)
|
|||
MIDI = CreateMIDIDevice(devtype);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
assert(MIDI == NULL || MIDI->NeedThreadedCallback() == false);
|
||||
#endif
|
||||
|
||||
if (MIDI == NULL || 0 != MIDI->Open(Callback, this))
|
||||
{
|
||||
Printf(PRINT_BOLD, "Could not open MIDI out device\n");
|
||||
|
@ -393,27 +334,7 @@ void MIDIStreamer::Play(bool looping, int subsong)
|
|||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
*/
|
||||
{
|
||||
m_Status = STATE_Playing;
|
||||
}
|
||||
m_Status = STATE_Playing;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -440,12 +361,7 @@ void MIDIStreamer::StartPlayback()
|
|||
MusicVolumeChanged(); // set volume to current music's properties
|
||||
OutputVolume(Volume);
|
||||
|
||||
/*
|
||||
#ifdef _WIN32
|
||||
ResetEvent(ExitEvent);
|
||||
ResetEvent(BufferDoneEvent);
|
||||
#endif
|
||||
*/
|
||||
MIDI->InitPlayback();
|
||||
|
||||
// Fill the initial buffers for the song.
|
||||
BufferNum = 0;
|
||||
|
@ -532,17 +448,6 @@ void MIDIStreamer::Stop()
|
|||
{
|
||||
EndQueued = 4;
|
||||
|
||||
/*
|
||||
#ifdef _WIN32
|
||||
if (PlayerThread != NULL)
|
||||
{
|
||||
SetEvent(ExitEvent);
|
||||
WaitForSingleObject(PlayerThread, INFINITE);
|
||||
CloseHandle(PlayerThread);
|
||||
PlayerThread = NULL;
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
if (MIDI != NULL && MIDI->IsOpen())
|
||||
{
|
||||
MIDI->Stop();
|
||||
|
@ -714,10 +619,6 @@ int MIDIStreamer::VolumeControllerChange(int channel, int volume)
|
|||
//
|
||||
// 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)
|
||||
|
@ -728,18 +629,7 @@ void MIDIStreamer::Callback(void *userdata)
|
|||
{
|
||||
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()
|
||||
{
|
||||
/*
|
||||
#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
|
||||
*/
|
||||
if (!MIDI->Update()) Stop();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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
|
||||
|
@ -913,8 +684,8 @@ fill:
|
|||
switch (res & 3)
|
||||
{
|
||||
case SONG_MORE:
|
||||
if ((MIDI->NeedThreadedCallback() && 0 != (res = MIDI->StreamOutSync(&Buffer[BufferNum]))) ||
|
||||
(!MIDI->NeedThreadedCallback() && 0 != (res = MIDI->StreamOut(&Buffer[BufferNum]))))
|
||||
res = MIDI->StreamOut(&Buffer[BufferNum]);
|
||||
if (res != 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
@ -1433,27 +1204,6 @@ bool MIDIStreamer::SetMIDISubsong(int subsong)
|
|||
return subsong == 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDIStreamer :: CheckExitEvent
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool MIDIStreamer::CheckExitEvent()
|
||||
{
|
||||
/*
|
||||
#ifdef _WIN32
|
||||
if (ExitEvent == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -96,11 +96,6 @@ static const uint8_t CtrlTranslate[15] =
|
|||
MUSSong2::MUSSong2 (FileReader &reader, EMidiDevice type, const char *args)
|
||||
: MIDIStreamer(type, args), MusHeader(0), MusBuffer(0)
|
||||
{
|
||||
if (!CheckExitEvent())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t front[32];
|
||||
int start;
|
||||
|
||||
|
|
|
@ -108,10 +108,6 @@ MIDISong2::MIDISong2 (FileReader &reader, EMidiDevice type, const char *args)
|
|||
int p;
|
||||
int i;
|
||||
|
||||
if (!CheckExitEvent())
|
||||
{
|
||||
return;
|
||||
}
|
||||
SongLen = reader.GetLength();
|
||||
MusHeader = new uint8_t[SongLen];
|
||||
if (reader.Read(MusHeader, SongLen) != SongLen)
|
||||
|
|
|
@ -34,19 +34,7 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#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>
|
||||
#include "i_midi_win32.h"
|
||||
|
||||
// HEADER FILES ------------------------------------------------------------
|
||||
|
||||
|
@ -92,13 +80,16 @@ public:
|
|||
int PrepareHeader(MidiHeader *data);
|
||||
int UnprepareHeader(MidiHeader *data);
|
||||
bool FakeVolume();
|
||||
bool NeedThreadedCallback();
|
||||
bool Pause(bool paused);
|
||||
void InitPlayback() override;
|
||||
bool Update() override;
|
||||
void PrecacheInstruments(const uint16_t *instruments, int count);
|
||||
DWORD PlayerLoop();
|
||||
|
||||
protected:
|
||||
//protected:
|
||||
static void CALLBACK CallbackFunc(HMIDIOUT, UINT, DWORD_PTR, DWORD, DWORD);
|
||||
|
||||
MIDIStreamer *Streamer;
|
||||
HMIDISTRM MidiOut;
|
||||
UINT DeviceID;
|
||||
DWORD SavedVolume;
|
||||
|
@ -108,6 +99,12 @@ protected:
|
|||
|
||||
MidiCallback Callback;
|
||||
void *CallbackData;
|
||||
|
||||
HANDLE BufferDoneEvent;
|
||||
HANDLE ExitEvent;
|
||||
HANDLE PlayerThread;
|
||||
|
||||
|
||||
};
|
||||
|
||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||
|
@ -128,6 +125,18 @@ WinMIDIDevice::WinMIDIDevice(int dev_id)
|
|||
MidiOut = 0;
|
||||
HeaderIndex = 0;
|
||||
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()
|
||||
{
|
||||
Close();
|
||||
|
||||
if (ExitEvent != nullptr)
|
||||
{
|
||||
CloseHandle(ExitEvent);
|
||||
}
|
||||
if (BufferDoneEvent != nullptr)
|
||||
{
|
||||
CloseHandle(BufferDoneEvent);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -153,7 +171,7 @@ int WinMIDIDevice::Open(MidiCallback callback, void *userdata)
|
|||
|
||||
Callback = callback;
|
||||
CallbackData = userdata;
|
||||
if (MidiOut == NULL)
|
||||
if (MidiOut == nullptr)
|
||||
{
|
||||
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()
|
||||
{
|
||||
if (MidiOut != NULL)
|
||||
if (MidiOut != nullptr)
|
||||
{
|
||||
midiStreamClose(MidiOut);
|
||||
MidiOut = NULL;
|
||||
MidiOut = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,7 +218,7 @@ void WinMIDIDevice::Close()
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDIStreamer :: PlayerProc Static
|
||||
//
|
||||
// Entry point for the player thread.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DWORD WINAPI PlayerProc(LPVOID lpParameter)
|
||||
{
|
||||
return ((WinMIDIDevice *)lpParameter)->PlayerLoop();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// WinMIDIDevice :: Resume
|
||||
|
@ -252,7 +283,31 @@ int WinMIDIDevice::SetTimeDiv(int timediv)
|
|||
|
||||
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()
|
||||
{
|
||||
if (PlayerThread != nullptr)
|
||||
{
|
||||
SetEvent(ExitEvent);
|
||||
WaitForSingleObject(PlayerThread, INFINITE);
|
||||
CloseHandle(PlayerThread);
|
||||
PlayerThread = nullptr;
|
||||
}
|
||||
|
||||
midiStreamStop(MidiOut);
|
||||
midiOutReset((HMIDIOUT)MidiOut);
|
||||
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
|
||||
|
@ -391,9 +488,7 @@ int WinMIDIDevice::StreamOut(MidiHeader *header)
|
|||
|
||||
int WinMIDIDevice::StreamOutSync(MidiHeader *header)
|
||||
{
|
||||
auto syshdr = (MIDIHDR*)header->lpNext;
|
||||
assert(syshdr == &WinMidiHeaders[0] || syshdr == &WinMidiHeaders[1]);
|
||||
return midiStreamOut(MidiOut, syshdr, sizeof(MIDIHDR));
|
||||
return StreamOut(header);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -454,15 +549,71 @@ bool WinMIDIDevice::FakeVolume()
|
|||
|
||||
//==========================================================================
|
||||
//
|
||||
// WinMIDIDevice :: NeedThreadedCallback
|
||||
//
|
||||
// When using the MM system, the callback can't yet touch the buffer, so
|
||||
// the real processing needs to happen in a different thread.
|
||||
// WinMIDIDevice :: Update
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -475,9 +626,9 @@ bool WinMIDIDevice::NeedThreadedCallback()
|
|||
void CALLBACK WinMIDIDevice::CallbackFunc(HMIDIOUT hOut, UINT uMsg, DWORD_PTR dwInstance, DWORD dwParam1, DWORD dwParam2)
|
||||
{
|
||||
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,
|
||||
// we know we're using the new audio stack introduced with Vista and
|
||||
// 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))
|
||||
&& enumerator != NULL)
|
||||
&& enumerator != nullptr)
|
||||
{
|
||||
enumerator->Release();
|
||||
return true;
|
||||
|
@ -540,7 +691,13 @@ static bool IgnoreMIDIVolume(UINT id)
|
|||
|
||||
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
|
||||
|
||||
|
|
|
@ -111,10 +111,6 @@ extern char MIDI_CommonLengths[15];
|
|||
XMISong::XMISong (FileReader &reader, EMidiDevice type, const char *args)
|
||||
: MIDIStreamer(type, args), MusHeader(0), Songs(0)
|
||||
{
|
||||
if (!CheckExitEvent())
|
||||
{
|
||||
return;
|
||||
}
|
||||
SongLen = reader.GetLength();
|
||||
MusHeader = new uint8_t[SongLen];
|
||||
if (reader.Read(MusHeader, SongLen) != SongLen)
|
||||
|
|
Loading…
Reference in a new issue