mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2025-01-31 10:40:33 +00:00
- The full master volume SysEx is now always sent to the MIDI device, even if
it seems to have a working volume control. - Renamed music_midi_stream.cpp to music_midi_base.cpp. - Moved the WinMM MIDI code into a new container class. SVN r787 (trunk)
This commit is contained in:
parent
68a8ea2189
commit
68f726e422
7 changed files with 394 additions and 82 deletions
|
@ -1,6 +1,12 @@
|
||||||
|
March 6, 2008
|
||||||
|
- The full master volume SysEx is now always sent to the MIDI device, even if
|
||||||
|
it seems to have a working volume control.
|
||||||
|
- Renamed music_midi_stream.cpp to music_midi_base.cpp.
|
||||||
|
- Moved the WinMM MIDI code into a new container class.
|
||||||
|
|
||||||
March 4, 2008
|
March 4, 2008
|
||||||
- Moved the identical code between the MUS and MIDI streamers into a new base
|
- Moved the identical code between the MUS and MIDI streamers into a new base
|
||||||
class so they all the low-level details of MIDI streaming are kept in
|
class so that all the low-level details of MIDI streaming are kept in
|
||||||
one place.
|
one place.
|
||||||
- Converted the SMF MIDI playback to use the same MIDI streams as MUS
|
- Converted the SMF MIDI playback to use the same MIDI streams as MUS
|
||||||
playback.
|
playback.
|
||||||
|
@ -9,7 +15,7 @@ March 4, 2008
|
||||||
manner.
|
manner.
|
||||||
- Fixed: The MEVT_* values are not defined shifted into their spot for a
|
- Fixed: The MEVT_* values are not defined shifted into their spot for a
|
||||||
MIDIEVENT, so I need to do it myself.
|
MIDIEVENT, so I need to do it myself.
|
||||||
- Fixed: Pausing a MUS and the changing snd_midivolume caused the paused
|
- Fixed: Pausing a MUS and then changing snd_midivolume caused the paused
|
||||||
notes to become audible.
|
notes to become audible.
|
||||||
|
|
||||||
March 3, 2008
|
March 3, 2008
|
||||||
|
|
|
@ -55,8 +55,61 @@ public:
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
// A device that provides a WinMM-like MIDI streaming interface -------------
|
||||||
|
|
||||||
|
class MIDIDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MIDIDevice();
|
||||||
|
virtual ~MIDIDevice();
|
||||||
|
|
||||||
|
virtual int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) = 0;
|
||||||
|
virtual void Close() = 0;
|
||||||
|
virtual bool IsOpen() const = 0;
|
||||||
|
virtual int GetTechnology() const = 0;
|
||||||
|
virtual int SetTempo(int tempo) = 0;
|
||||||
|
virtual int SetTimeDiv(int timediv) = 0;
|
||||||
|
virtual int StreamOut(MIDIHDR *data) = 0;
|
||||||
|
virtual int Resume() = 0;
|
||||||
|
virtual void Stop() = 0;
|
||||||
|
virtual int PrepareHeader(MIDIHDR *data) = 0;
|
||||||
|
virtual int UnprepareHeader(MIDIHDR *data) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// WinMM implementation of a MIDI output device -----------------------------
|
||||||
|
|
||||||
|
class WinMIDIDevice : public MIDIDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WinMIDIDevice(int dev_id);
|
||||||
|
~WinMIDIDevice();
|
||||||
|
int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata);
|
||||||
|
void Close();
|
||||||
|
bool IsOpen() const;
|
||||||
|
int GetTechnology() const;
|
||||||
|
int SetTempo(int tempo);
|
||||||
|
int SetTimeDiv(int timediv);
|
||||||
|
int StreamOut(MIDIHDR *data);
|
||||||
|
int Resume();
|
||||||
|
void Stop();
|
||||||
|
int PrepareHeader(MIDIHDR *data);
|
||||||
|
int UnprepareHeader(MIDIHDR *data);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void CALLBACK CallbackFunc(HMIDIOUT, UINT, DWORD_PTR, DWORD, DWORD);
|
||||||
|
|
||||||
|
HMIDISTRM MidiOut;
|
||||||
|
UINT DeviceID;
|
||||||
|
DWORD SavedVolume;
|
||||||
|
bool VolumeWorks;
|
||||||
|
|
||||||
|
void (*Callback)(unsigned int, void *, DWORD, DWORD);
|
||||||
|
void *CallbackData;
|
||||||
|
};
|
||||||
|
|
||||||
// Base class for streaming MUS and MIDI files ------------------------------
|
// Base class for streaming MUS and MIDI files ------------------------------
|
||||||
|
|
||||||
|
|
||||||
class MIDIStreamer : public MusInfo
|
class MIDIStreamer : public MusInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -75,7 +128,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static DWORD WINAPI PlayerProc (LPVOID lpParameter);
|
static DWORD WINAPI PlayerProc (LPVOID lpParameter);
|
||||||
static void CALLBACK Callback(HMIDIOUT handle, UINT uMsg, DWORD_PTR dwInstance, DWORD dwParam1, DWORD dwParam2);
|
static void Callback(UINT uMsg, void *userdata, DWORD dwParam1, DWORD dwParam2);
|
||||||
DWORD PlayerLoop();
|
DWORD PlayerLoop();
|
||||||
void OutputVolume (DWORD volume);
|
void OutputVolume (DWORD volume);
|
||||||
int FillBuffer(int buffer_num, int max_events, DWORD max_time);
|
int FillBuffer(int buffer_num, int max_events, DWORD max_time);
|
||||||
|
@ -83,7 +136,7 @@ protected:
|
||||||
int VolumeControllerChange(int channel, int volume);
|
int VolumeControllerChange(int channel, int volume);
|
||||||
|
|
||||||
// Virtuals for subclasses to override
|
// Virtuals for subclasses to override
|
||||||
virtual void CheckCaps(DWORD dev_id);
|
virtual void CheckCaps();
|
||||||
virtual void DoInitialSetup() = 0;
|
virtual void DoInitialSetup() = 0;
|
||||||
virtual void DoRestart() = 0;
|
virtual void DoRestart() = 0;
|
||||||
virtual bool CheckDone() = 0;
|
virtual bool CheckDone() = 0;
|
||||||
|
@ -101,12 +154,11 @@ protected:
|
||||||
SONG_ERROR
|
SONG_ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
HMIDISTRM MidiOut;
|
MIDIDevice *MIDI;
|
||||||
HANDLE PlayerThread;
|
HANDLE PlayerThread;
|
||||||
HANDLE ExitEvent;
|
HANDLE ExitEvent;
|
||||||
HANDLE BufferDoneEvent;
|
HANDLE BufferDoneEvent;
|
||||||
DWORD SavedVolume;
|
|
||||||
bool VolumeWorks;
|
|
||||||
DWORD Events[2][MAX_EVENTS*3];
|
DWORD Events[2][MAX_EVENTS*3];
|
||||||
MIDIHDR Buffer[2];
|
MIDIHDR Buffer[2];
|
||||||
int BufferNum;
|
int BufferNum;
|
||||||
|
@ -150,7 +202,7 @@ public:
|
||||||
~MIDISong2 ();
|
~MIDISong2 ();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void CheckCaps(DWORD dev_id);
|
void CheckCaps();
|
||||||
void DoInitialSetup();
|
void DoInitialSetup();
|
||||||
void DoRestart();
|
void DoRestart();
|
||||||
bool CheckDone();
|
bool CheckDone();
|
||||||
|
|
|
@ -217,24 +217,22 @@ MIDISong2::~MIDISong2 ()
|
||||||
// MIDISong2 :: CheckCaps
|
// MIDISong2 :: CheckCaps
|
||||||
//
|
//
|
||||||
// Find out if this is an FM synth or not for EMIDI's benefit.
|
// Find out if this is an FM synth or not for EMIDI's benefit.
|
||||||
|
// (Do any released EMIDIs use track designations?)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
void MIDISong2::CheckCaps(DWORD dev_id)
|
void MIDISong2::CheckCaps()
|
||||||
{
|
{
|
||||||
MIDIOUTCAPS caps;
|
int tech = MIDI->GetTechnology();
|
||||||
|
|
||||||
DesignationMask = 0xFF0F;
|
DesignationMask = 0xFF0F;
|
||||||
if (MMSYSERR_NOERROR == midiOutGetDevCaps (dev_id, &caps, sizeof(caps)))
|
if (tech == MOD_FMSYNTH)
|
||||||
{
|
{
|
||||||
if (caps.wTechnology == MOD_FMSYNTH)
|
DesignationMask = 0x00F0;
|
||||||
{
|
}
|
||||||
DesignationMask = 0x00F0;
|
else if (tech == MOD_MIDIPORT)
|
||||||
}
|
{
|
||||||
else if (caps.wTechnology == MOD_MIDIPORT)
|
DesignationMask = 0x0001;
|
||||||
{
|
|
||||||
DesignationMask = 0x0001;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,7 +436,7 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
track->Designated = true;
|
track->Designated = true;
|
||||||
event = 0xFF;
|
event = MIDI_META;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 111: // EMIDI Track Exclusion
|
case 111: // EMIDI Track Exclusion
|
||||||
|
@ -446,7 +444,7 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay)
|
||||||
{
|
{
|
||||||
track->Designation &= ~(1 << data2);
|
track->Designation &= ~(1 << data2);
|
||||||
}
|
}
|
||||||
event = 0xFF;
|
event = MIDI_META;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 112: // EMIDI Program Change
|
case 112: // EMIDI Program Change
|
||||||
|
@ -729,9 +727,7 @@ MIDISong2::TrackInfo *MIDISong2::FindNextDue ()
|
||||||
|
|
||||||
void MIDISong2::SetTempo(int new_tempo)
|
void MIDISong2::SetTempo(int new_tempo)
|
||||||
{
|
{
|
||||||
MIDIPROPTEMPO tempo = { sizeof(MIDIPROPTEMPO), new_tempo };
|
if (MMSYSERR_NOERROR == MIDI->SetTempo(new_tempo))
|
||||||
|
|
||||||
if (MMSYSERR_NOERROR == midiStreamProperty(MidiOut, (LPBYTE)&tempo, MIDIPROP_SET | MIDIPROP_TEMPO))
|
|
||||||
{
|
{
|
||||||
Tempo = new_tempo;
|
Tempo = new_tempo;
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ extern UINT mididevice;
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
MIDIStreamer::MIDIStreamer()
|
MIDIStreamer::MIDIStreamer()
|
||||||
: MidiOut(0), PlayerThread(0), ExitEvent(0), BufferDoneEvent(0),
|
: MIDI(0), PlayerThread(0), ExitEvent(0), BufferDoneEvent(0),
|
||||||
Division(0), InitialTempo(500000)
|
Division(0), InitialTempo(500000)
|
||||||
{
|
{
|
||||||
BufferDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
BufferDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
@ -104,6 +104,10 @@ MIDIStreamer::~MIDIStreamer()
|
||||||
{
|
{
|
||||||
CloseHandle(BufferDoneEvent);
|
CloseHandle(BufferDoneEvent);
|
||||||
}
|
}
|
||||||
|
if (MIDI != NULL)
|
||||||
|
{
|
||||||
|
delete MIDI;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -139,7 +143,7 @@ bool MIDIStreamer::IsValid() const
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
void MIDIStreamer::CheckCaps(DWORD dev_id)
|
void MIDIStreamer::CheckCaps()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +156,6 @@ void MIDIStreamer::CheckCaps(DWORD dev_id)
|
||||||
void MIDIStreamer::Play (bool looping)
|
void MIDIStreamer::Play (bool looping)
|
||||||
{
|
{
|
||||||
DWORD tid;
|
DWORD tid;
|
||||||
UINT dev_id;
|
|
||||||
|
|
||||||
m_Status = STATE_Stopped;
|
m_Status = STATE_Stopped;
|
||||||
m_Looping = looping;
|
m_Looping = looping;
|
||||||
|
@ -160,41 +163,27 @@ void MIDIStreamer::Play (bool looping)
|
||||||
VolumeChanged = false;
|
VolumeChanged = false;
|
||||||
Restarting = true;
|
Restarting = true;
|
||||||
InitialPlayback = true;
|
InitialPlayback = true;
|
||||||
dev_id = MAX(mididevice, 0u);
|
|
||||||
|
|
||||||
if (MMSYSERR_NOERROR != midiStreamOpen(&MidiOut, &dev_id, 1, (DWORD_PTR)Callback, (DWORD_PTR)this, CALLBACK_FUNCTION))
|
assert(MIDI == NULL);
|
||||||
|
MIDI = new WinMIDIDevice(mididevice);
|
||||||
|
|
||||||
|
if (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");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckCaps(dev_id);
|
CheckCaps();
|
||||||
|
|
||||||
// Set time division and tempo.
|
// Set time division and tempo.
|
||||||
MIDIPROPTIMEDIV timediv = { sizeof(MIDIPROPTIMEDIV), Division };
|
if (0 != MIDI->SetTimeDiv(Division) ||
|
||||||
MIDIPROPTEMPO tempo = { sizeof(MIDIPROPTEMPO), Tempo = InitialTempo };
|
0 != MIDI->SetTempo(Tempo = InitialTempo))
|
||||||
|
|
||||||
if (MMSYSERR_NOERROR != midiStreamProperty(MidiOut, (LPBYTE)&timediv, MIDIPROP_SET | MIDIPROP_TIMEDIV) ||
|
|
||||||
MMSYSERR_NOERROR != midiStreamProperty(MidiOut, (LPBYTE)&tempo, MIDIPROP_SET | MIDIPROP_TEMPO))
|
|
||||||
{
|
{
|
||||||
Printf(PRINT_BOLD, "Setting MIDI stream speed failed\n");
|
Printf(PRINT_BOLD, "Setting MIDI stream speed failed\n");
|
||||||
midiStreamClose(MidiOut);
|
MIDI->Close();
|
||||||
MidiOut = NULL;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try two different methods for setting the stream to full volume.
|
|
||||||
// Unfortunately, this isn't as reliable as it once was, which is a pity.
|
|
||||||
// The real volume selection is done by setting the volume controller for
|
|
||||||
// each channel. Because every General MIDI-compliant device must support
|
|
||||||
// this controller, it is the most reliable means of setting the volume.
|
|
||||||
|
|
||||||
VolumeWorks = (MMSYSERR_NOERROR == midiOutGetVolume((HMIDIOUT)MidiOut, &SavedVolume));
|
|
||||||
if (VolumeWorks)
|
|
||||||
{
|
|
||||||
VolumeWorks &= (MMSYSERR_NOERROR == midiOutSetVolume((HMIDIOUT)MidiOut, 0xffffffff));
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_midivolume.Callback(); // set volume to current music's properties
|
snd_midivolume.Callback(); // set volume to current music's properties
|
||||||
OutputVolume (midivolume & 0xffff);
|
OutputVolume (midivolume & 0xffff);
|
||||||
|
|
||||||
|
@ -208,7 +197,7 @@ void MIDIStreamer::Play (bool looping)
|
||||||
int res = FillBuffer(BufferNum, MAX_EVENTS, MAX_TIME);
|
int res = FillBuffer(BufferNum, MAX_EVENTS, MAX_TIME);
|
||||||
if (res == SONG_MORE)
|
if (res == SONG_MORE)
|
||||||
{
|
{
|
||||||
if (MMSYSERR_NOERROR != midiStreamOut(MidiOut, &Buffer[BufferNum], sizeof(MIDIHDR)))
|
if (0 != MIDI->StreamOut(&Buffer[BufferNum]))
|
||||||
{
|
{
|
||||||
Printf ("Initial midiStreamOut failed\n");
|
Printf ("Initial midiStreamOut failed\n");
|
||||||
Stop();
|
Stop();
|
||||||
|
@ -223,7 +212,7 @@ void MIDIStreamer::Play (bool looping)
|
||||||
Restarting = true;
|
Restarting = true;
|
||||||
if (SONG_MORE == FillBuffer(BufferNum, MAX_EVENTS, MAX_TIME))
|
if (SONG_MORE == FillBuffer(BufferNum, MAX_EVENTS, MAX_TIME))
|
||||||
{
|
{
|
||||||
if (MMSYSERR_NOERROR != midiStreamOut(MidiOut, &Buffer[BufferNum], sizeof(MIDIHDR)))
|
if (0 != MIDI->StreamOut(&Buffer[BufferNum]))
|
||||||
{
|
{
|
||||||
Printf ("Initial midiStreamOut failed\n");
|
Printf ("Initial midiStreamOut failed\n");
|
||||||
Stop();
|
Stop();
|
||||||
|
@ -250,9 +239,9 @@ void MIDIStreamer::Play (bool looping)
|
||||||
}
|
}
|
||||||
while (BufferNum != 0);
|
while (BufferNum != 0);
|
||||||
|
|
||||||
if (MMSYSERR_NOERROR != midiStreamRestart(MidiOut))
|
if (0 != MIDI->Resume())
|
||||||
{
|
{
|
||||||
Printf ("midiStreamRestart failed\n");
|
Printf ("Starting MIDI playback failed\n");
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -260,7 +249,7 @@ void MIDIStreamer::Play (bool looping)
|
||||||
PlayerThread = CreateThread(NULL, 0, PlayerProc, this, 0, &tid);
|
PlayerThread = CreateThread(NULL, 0, PlayerProc, this, 0, &tid);
|
||||||
if (PlayerThread == NULL)
|
if (PlayerThread == NULL)
|
||||||
{
|
{
|
||||||
Printf ("MUS CreateThread failed\n");
|
Printf ("Creating MIDI thread failed\n");
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -324,18 +313,17 @@ void MIDIStreamer::Stop ()
|
||||||
CloseHandle(PlayerThread);
|
CloseHandle(PlayerThread);
|
||||||
PlayerThread = NULL;
|
PlayerThread = NULL;
|
||||||
}
|
}
|
||||||
if (MidiOut)
|
if (MIDI != NULL && MIDI->IsOpen())
|
||||||
{
|
{
|
||||||
midiStreamStop(MidiOut);
|
MIDI->Stop();
|
||||||
midiOutReset((HMIDIOUT)MidiOut);
|
MIDI->UnprepareHeader(&Buffer[0]);
|
||||||
if (VolumeWorks)
|
MIDI->UnprepareHeader(&Buffer[1]);
|
||||||
{
|
MIDI->Close();
|
||||||
midiOutSetVolume((HMIDIOUT)MidiOut, SavedVolume);
|
}
|
||||||
}
|
if (MIDI != NULL)
|
||||||
midiOutUnprepareHeader((HMIDIOUT)MidiOut, &Buffer[0], sizeof(MIDIHDR));
|
{
|
||||||
midiOutUnprepareHeader((HMIDIOUT)MidiOut, &Buffer[1], sizeof(MIDIHDR));
|
delete MIDI;
|
||||||
midiStreamClose(MidiOut);
|
MIDI = NULL;
|
||||||
MidiOut = NULL;
|
|
||||||
}
|
}
|
||||||
m_Status = STATE_Stopped;
|
m_Status = STATE_Stopped;
|
||||||
}
|
}
|
||||||
|
@ -402,9 +390,9 @@ int MIDIStreamer::VolumeControllerChange(int channel, int volume)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
void CALLBACK MIDIStreamer::Callback(HMIDIOUT hOut, UINT uMsg, DWORD_PTR dwInstance, DWORD dwParam1, DWORD dwParam2)
|
void MIDIStreamer::Callback(UINT uMsg, void *userdata, DWORD dwParam1, DWORD dwParam2)
|
||||||
{
|
{
|
||||||
MIDIStreamer *self = (MIDIStreamer *)dwInstance;
|
MIDIStreamer *self = (MIDIStreamer *)userdata;
|
||||||
|
|
||||||
if (self->EndQueued > 1)
|
if (self->EndQueued > 1)
|
||||||
{
|
{
|
||||||
|
@ -502,7 +490,7 @@ bool MIDIStreamer::ServiceEvent()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (MMSYSERR_NOERROR != midiOutUnprepareHeader((HMIDIOUT)MidiOut, &Buffer[BufferNum], sizeof(MIDIHDR)))
|
if (0 != MIDI->UnprepareHeader(&Buffer[BufferNum]))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -510,7 +498,7 @@ fill:
|
||||||
switch (FillBuffer(BufferNum, MAX_EVENTS, MAX_TIME))
|
switch (FillBuffer(BufferNum, MAX_EVENTS, MAX_TIME))
|
||||||
{
|
{
|
||||||
case SONG_MORE:
|
case SONG_MORE:
|
||||||
if (MMSYSERR_NOERROR != midiStreamOut(MidiOut, &Buffer[BufferNum], sizeof(MIDIHDR)))
|
if (0 != MIDI->StreamOut(&Buffer[BufferNum]))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -569,16 +557,13 @@ int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time)
|
||||||
if (InitialPlayback)
|
if (InitialPlayback)
|
||||||
{
|
{
|
||||||
InitialPlayback = false;
|
InitialPlayback = false;
|
||||||
if (!VolumeWorks)
|
// Send the full master volume SysEx message.
|
||||||
{
|
events[0] = 0; // dwDeltaTime
|
||||||
// Send the full master volume SysEx message.
|
events[1] = 0; // dwStreamID
|
||||||
events[0] = 0; // dwDeltaTime
|
events[2] = (MEVT_LONGMSG << 24) | 8; // dwEvent
|
||||||
events[1] = 0; // dwStreamID
|
events[3] = 0x047f7ff0; // dwParms[0]
|
||||||
events[2] = (MEVT_LONGMSG << 24) | 8; // dwEvent
|
events[4] = 0xf77f7f01; // dwParms[1]
|
||||||
events[3] = 0x047f7ff0; // dwParms[0]
|
events += 5;
|
||||||
events[4] = 0xf77f7f01; // dwParms[1]
|
|
||||||
events += 5;
|
|
||||||
}
|
|
||||||
DoInitialSetup();
|
DoInitialSetup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,10 +612,25 @@ int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time)
|
||||||
Buffer[buffer_num].lpData = (LPSTR)Events[buffer_num];
|
Buffer[buffer_num].lpData = (LPSTR)Events[buffer_num];
|
||||||
Buffer[buffer_num].dwBufferLength = DWORD((LPSTR)events - Buffer[buffer_num].lpData);
|
Buffer[buffer_num].dwBufferLength = DWORD((LPSTR)events - Buffer[buffer_num].lpData);
|
||||||
Buffer[buffer_num].dwBytesRecorded = Buffer[buffer_num].dwBufferLength;
|
Buffer[buffer_num].dwBytesRecorded = Buffer[buffer_num].dwBufferLength;
|
||||||
if (MMSYSERR_NOERROR != midiOutPrepareHeader((HMIDIOUT)MidiOut, &Buffer[buffer_num], sizeof(MIDIHDR)))
|
if (0 != MIDI->PrepareHeader(&Buffer[buffer_num]))
|
||||||
{
|
{
|
||||||
return SONG_ERROR;
|
return SONG_ERROR;
|
||||||
}
|
}
|
||||||
return SONG_MORE;
|
return SONG_MORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// MIDIDevice constructor and desctructor stubs.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
MIDIDevice::MIDIDevice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MIDIDevice::~MIDIDevice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
254
src/sound/music_win_mididevice.cpp
Normal file
254
src/sound/music_win_mididevice.cpp
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
/*
|
||||||
|
** music_mididevice.cpp
|
||||||
|
** Provides a WinMM implementation of a generic MIDI output device.
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 2008 Randy Heit
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
// HEADER FILES ------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "i_musicinterns.h"
|
||||||
|
#include "templates.h"
|
||||||
|
#include "doomdef.h"
|
||||||
|
#include "m_swap.h"
|
||||||
|
|
||||||
|
// MACROS ------------------------------------------------------------------
|
||||||
|
|
||||||
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||||||
|
|
||||||
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
||||||
|
|
||||||
|
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
||||||
|
|
||||||
|
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
||||||
|
|
||||||
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||||
|
|
||||||
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||||
|
|
||||||
|
// CODE --------------------------------------------------------------------
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// WinMIDIDevice Contructor
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
WinMIDIDevice::WinMIDIDevice(int dev_id)
|
||||||
|
{
|
||||||
|
DeviceID = MAX<DWORD>(dev_id, 0);
|
||||||
|
MidiOut = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// WinMIDIDevice Destructor
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
WinMIDIDevice::~WinMIDIDevice()
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// WinMIDIDevice :: Open
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int WinMIDIDevice::Open(void (*callback)(UINT, void *, DWORD, DWORD), void *userdata)
|
||||||
|
{
|
||||||
|
MMRESULT err;
|
||||||
|
|
||||||
|
Callback = callback;
|
||||||
|
CallbackData = userdata;
|
||||||
|
if (MidiOut == NULL)
|
||||||
|
{
|
||||||
|
err = midiStreamOpen(&MidiOut, &DeviceID, 1, (DWORD_PTR)CallbackFunc, (DWORD_PTR)this, CALLBACK_FUNCTION);
|
||||||
|
|
||||||
|
if (err == MMSYSERR_NOERROR)
|
||||||
|
{
|
||||||
|
// Set master volume to full, if the device allows it on this interface.
|
||||||
|
VolumeWorks = (MMSYSERR_NOERROR == midiOutGetVolume((HMIDIOUT)MidiOut, &SavedVolume));
|
||||||
|
if (VolumeWorks)
|
||||||
|
{
|
||||||
|
VolumeWorks &= (MMSYSERR_NOERROR == midiOutSetVolume((HMIDIOUT)MidiOut, 0xffffffff));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// WinMIDIDevice :: Close
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void WinMIDIDevice::Close()
|
||||||
|
{
|
||||||
|
if (MidiOut != NULL)
|
||||||
|
{
|
||||||
|
midiStreamClose(MidiOut);
|
||||||
|
MidiOut = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// WinMIDIDevice :: IsOpen
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
bool WinMIDIDevice::IsOpen() const
|
||||||
|
{
|
||||||
|
return MidiOut != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// WinMIDIDevice :: GetTechnology
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int WinMIDIDevice::GetTechnology() const
|
||||||
|
{
|
||||||
|
MIDIOUTCAPS caps;
|
||||||
|
|
||||||
|
if (MMSYSERR_NOERROR == midiOutGetDevCaps(DeviceID, &caps, sizeof(caps)))
|
||||||
|
{
|
||||||
|
return caps.wTechnology;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// WinMIDIDevice :: SetTempo
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int WinMIDIDevice::SetTempo(int tempo)
|
||||||
|
{
|
||||||
|
MIDIPROPTEMPO data = { sizeof(MIDIPROPTEMPO), tempo };
|
||||||
|
return midiStreamProperty(MidiOut, (LPBYTE)&data, MIDIPROP_SET | MIDIPROP_TEMPO);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// WinMIDIDevice :: SetTimeDiv
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int WinMIDIDevice::SetTimeDiv(int timediv)
|
||||||
|
{
|
||||||
|
MIDIPROPTIMEDIV data = { sizeof(MIDIPROPTIMEDIV), timediv };
|
||||||
|
return midiStreamProperty(MidiOut, (LPBYTE)&data, MIDIPROP_SET | MIDIPROP_TIMEDIV);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// WinMIDIDevice :: Resume
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int WinMIDIDevice::Resume()
|
||||||
|
{
|
||||||
|
return midiStreamRestart(MidiOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// WinMIDIDevice :: Stop
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void WinMIDIDevice::Stop()
|
||||||
|
{
|
||||||
|
midiStreamStop(MidiOut);
|
||||||
|
midiOutReset((HMIDIOUT)MidiOut);
|
||||||
|
if (VolumeWorks)
|
||||||
|
{
|
||||||
|
midiOutSetVolume((HMIDIOUT)MidiOut, SavedVolume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// WinMIDIDevice :: StreamOut
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int WinMIDIDevice::StreamOut(MIDIHDR *header)
|
||||||
|
{
|
||||||
|
return midiStreamOut(MidiOut, header, sizeof(MIDIHDR));
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// WinMIDIDevice :: PrepareHeader
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int WinMIDIDevice::PrepareHeader(MIDIHDR *header)
|
||||||
|
{
|
||||||
|
return midiOutPrepareHeader((HMIDIOUT)MidiOut, header, sizeof(MIDIHDR));
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// WinMIDIDevice :: UnprepareHeader
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int WinMIDIDevice::UnprepareHeader(MIDIHDR *header)
|
||||||
|
{
|
||||||
|
return midiOutUnprepareHeader((HMIDIOUT)MidiOut, header, sizeof(MIDIHDR));
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// WinMIDIDevice :: CallbackFunc static
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void CALLBACK WinMIDIDevice::CallbackFunc(HMIDIOUT hOut, UINT uMsg, DWORD_PTR dwInstance, DWORD dwParam1, DWORD dwParam2)
|
||||||
|
{
|
||||||
|
WinMIDIDevice *self = (WinMIDIDevice *)dwInstance;
|
||||||
|
if (self->Callback != NULL)
|
||||||
|
{
|
||||||
|
self->Callback(uMsg, self->CallbackData, dwParam1, dwParam2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -9255,11 +9255,11 @@
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="src\sound\music_midi_midiout.cpp"
|
RelativePath=".\src\sound\music_midi_base.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="src\sound\music_midi_stream.cpp"
|
RelativePath="src\sound\music_midi_midiout.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
|
@ -9290,6 +9290,10 @@
|
||||||
RelativePath="src\sound\music_stream.cpp"
|
RelativePath="src\sound\music_stream.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\src\sound\music_win_mididevice.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="src\sound\sample_flac.cpp"
|
RelativePath="src\sound\sample_flac.cpp"
|
||||||
>
|
>
|
||||||
|
|
Loading…
Reference in a new issue