- Reimplemented FMOD MIDI playback as a psuedo-MIDI device so that it can take advantage of XMI subsongs.

SVN r2872 (trunk)
This commit is contained in:
Randy Heit 2010-10-02 02:19:50 +00:00
parent 024bbeb171
commit b6941be55f
5 changed files with 434 additions and 93 deletions

View file

@ -72,6 +72,8 @@ typedef BYTE *LPSTR;
#endif #endif
#endif #endif
class MIDIStreamer;
class MIDIDevice class MIDIDevice
{ {
public: public:
@ -88,16 +90,17 @@ public:
virtual int StreamOutSync(MIDIHDR *data) = 0; virtual int StreamOutSync(MIDIHDR *data) = 0;
virtual int Resume() = 0; virtual int Resume() = 0;
virtual void Stop() = 0; virtual void Stop() = 0;
virtual int PrepareHeader(MIDIHDR *data) = 0; virtual int PrepareHeader(MIDIHDR *data);
virtual int UnprepareHeader(MIDIHDR *data) = 0; virtual int UnprepareHeader(MIDIHDR *data);
virtual bool FakeVolume() = 0; virtual bool FakeVolume();
virtual bool Pause(bool paused) = 0; virtual bool Pause(bool paused) = 0;
virtual bool NeedThreadedCallback() = 0; virtual bool NeedThreadedCallback();
virtual void PrecacheInstruments(const WORD *instruments, int count); virtual void PrecacheInstruments(const WORD *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);
virtual void FluidSettingNum(const char *setting, double value); virtual void FluidSettingNum(const char *setting, double value);
virtual void FluidSettingStr(const char *setting, const char *value); virtual void FluidSettingStr(const char *setting, const char *value);
virtual bool Preprocess(MIDIStreamer *song);
virtual FString GetStats(); virtual FString GetStats();
}; };
@ -139,6 +142,42 @@ protected:
}; };
#endif #endif
// Base class for psuedo-MIDI devices ---------------------------------------
class PsuedoMIDIDevice : public MIDIDevice
{
public:
PsuedoMIDIDevice();
~PsuedoMIDIDevice();
void Close();
bool IsOpen() const;
int GetTechnology() const;
bool Pause(bool paused);
int Resume();
void Stop();
int StreamOut(MIDIHDR *data);
int StreamOutSync(MIDIHDR *data);
int SetTempo(int tempo);
int SetTimeDiv(int timediv);
FString GetStats();
protected:
SoundStream *Stream;
bool Started;
int SampleRate;
};
// FMOD psuedo-MIDI device --------------------------------------------------
class FMODMIDIDevice : public PsuedoMIDIDevice
{
public:
int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata);
bool Preprocess(MIDIStreamer *song);
};
// Base class for software synthesizer MIDI output devices ------------------ // Base class for software synthesizer MIDI output devices ------------------
class SoftSynthMIDIDevice : public MIDIDevice class SoftSynthMIDIDevice : public MIDIDevice
@ -156,10 +195,6 @@ public:
int StreamOutSync(MIDIHDR *data); int StreamOutSync(MIDIHDR *data);
int Resume(); int Resume();
void Stop(); void Stop();
int PrepareHeader(MIDIHDR *data);
int UnprepareHeader(MIDIHDR *data);
bool FakeVolume();
bool NeedThreadedCallback();
bool Pause(bool paused); bool Pause(bool paused);
protected: protected:
@ -335,10 +370,10 @@ enum EMIDIDevice
MIDI_OPL, MIDI_OPL,
MIDI_GUS, MIDI_GUS,
MIDI_Fluid, MIDI_Fluid,
MIDI_FMOD,
// only used by I_RegisterSong // only used by I_RegisterSong
MIDI_Null, MIDI_Null,
MIDI_FMOD,
MIDI_Timidity MIDI_Timidity
}; };
@ -378,6 +413,7 @@ protected:
static void Callback(unsigned int uMsg, void *userdata, DWORD dwParam1, DWORD dwParam2); static void Callback(unsigned int uMsg, void *userdata, DWORD dwParam1, DWORD dwParam2);
// Virtuals for subclasses to override // Virtuals for subclasses to override
virtual void StartPlayback();
virtual void CheckCaps(int tech); virtual void CheckCaps(int tech);
virtual void DoInitialSetup() = 0; virtual void DoInitialSetup() = 0;
virtual void DoRestart() = 0; virtual void DoRestart() = 0;

View file

@ -239,6 +239,10 @@ void MIDIStreamer::Play(bool looping, int subsong)
break; break;
#endif #endif
case MIDI_FMOD:
MIDI = new FMODMIDIDevice;
break;
case MIDI_GUS: case MIDI_GUS:
MIDI = new TimidityMIDIDevice; MIDI = new TimidityMIDIDevice;
break; break;
@ -264,6 +268,49 @@ void MIDIStreamer::Play(bool looping, int subsong)
SetMIDISubsong(subsong); SetMIDISubsong(subsong);
CheckCaps(MIDI->GetTechnology()); CheckCaps(MIDI->GetTechnology());
if (MIDI->Preprocess(this))
{
StartPlayback();
}
if (0 != MIDI->Resume())
{
Printf ("Starting MIDI playback failed\n");
Stop();
}
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;
}
}
}
//==========================================================================
//
// MIDIStreamer :: StartPlayback
//
//==========================================================================
void MIDIStreamer::StartPlayback()
{
Precache(); Precache();
LoopLimit = 0; LoopLimit = 0;
@ -312,34 +359,6 @@ void MIDIStreamer::Play(bool looping, int subsong)
} }
} }
while (BufferNum != 0); while (BufferNum != 0);
if (0 != MIDI->Resume())
{
Printf ("Starting MIDI playback failed\n");
Stop();
}
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;
}
}
} }
//========================================================================== //==========================================================================
@ -457,6 +476,12 @@ void MIDIStreamer::MusicVolumeChanged()
} }
} }
//==========================================================================
//
// MIDIStreamer :: TimidityVolumeChanged
//
//==========================================================================
void MIDIStreamer::TimidityVolumeChanged() void MIDIStreamer::TimidityVolumeChanged()
{ {
if (MIDI != NULL) if (MIDI != NULL)
@ -1101,12 +1126,9 @@ static void WriteVarLen (TArray<BYTE> &file, DWORD value)
//========================================================================== //==========================================================================
void MIDIStreamer::SetTempo(int new_tempo) void MIDIStreamer::SetTempo(int new_tempo)
{
if (NULL == MIDI)
{ {
InitialTempo = new_tempo; InitialTempo = new_tempo;
} if (NULL != MIDI && 0 == MIDI->SetTempo(new_tempo))
else if (0 == MIDI->SetTempo(new_tempo))
{ {
Tempo = new_tempo; Tempo = new_tempo;
} }
@ -1227,6 +1249,75 @@ void MIDIDevice::PrecacheInstruments(const WORD *instruments, int count)
{ {
} }
//==========================================================================
//
// MIDIDevice :: Preprocess
//
// Gives the MIDI device a chance to do some processing with the song before
// it starts playing it. Returns true if MIDIStreamer should perform its
// standard playback startup sequence.
//
//==========================================================================
bool MIDIDevice::Preprocess(MIDIStreamer *song)
{
return true;
}
//==========================================================================
//
// MIDIDevice :: PrepareHeader
//
// Wrapper for MCI's midiOutPrepareHeader.
//
//==========================================================================
int MIDIDevice::PrepareHeader(MIDIHDR *header)
{
return 0;
}
//==========================================================================
//
// MIDIDevice :: UnprepareHeader
//
// Wrapper for MCI's midiOutUnprepareHeader.
//
//==========================================================================
int MIDIDevice::UnprepareHeader(MIDIHDR *header)
{
return 0;
}
//==========================================================================
//
// MIDIDevice :: FakeVolume
//
// Since most implementations render as a normal stream, their volume is
// controlled through the GSnd interface, not here.
//
//==========================================================================
bool MIDIDevice::FakeVolume()
{
return false;
}
//==========================================================================
//
// MIDIDevice :: NeedThreadedCallabck
//
// Most implementations can service the callback directly rather than using
// a separate thread.
//
//==========================================================================
bool MIDIDevice::NeedThreadedCallback()
{
return false;
}
//========================================================================== //==========================================================================
// //
// MIDIDevice :: TimidityVolumeChanged // MIDIDevice :: TimidityVolumeChanged

View file

@ -0,0 +1,260 @@
/*
** music_psuedo_mididevice.cpp
** Common base class for psuedo MIDI devices.
**
**---------------------------------------------------------------------------
** Copyright 2008-2010 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.
**---------------------------------------------------------------------------
**
*/
// HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h"
#include "templates.h"
#include "doomdef.h"
#include "m_swap.h"
// MACROS ------------------------------------------------------------------
// TYPES -------------------------------------------------------------------
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
// PRIVATE DATA DEFINITIONS ------------------------------------------------
// PUBLIC DATA DEFINITIONS -------------------------------------------------
// CODE --------------------------------------------------------------------
//==========================================================================
//
// PsuedoMIDIDevice Constructor
//
//==========================================================================
PsuedoMIDIDevice::PsuedoMIDIDevice()
{
Stream = NULL;
Started = false;
SampleRate = GSnd != NULL ? (int)GSnd->GetOutputRate() : 44100;
}
//==========================================================================
//
// PsuedoMIDIDevice Destructor
//
//==========================================================================
PsuedoMIDIDevice::~PsuedoMIDIDevice()
{
Close();
}
//==========================================================================
//
// PsuedoMIDIDevice :: Close
//
//==========================================================================
void PsuedoMIDIDevice::Close()
{
if (Stream != NULL)
{
delete Stream;
Stream = NULL;
}
Started = false;
}
//==========================================================================
//
// PsuedoMIDIDevice :: IsOpen
//
//==========================================================================
bool PsuedoMIDIDevice::IsOpen() const
{
return Stream != NULL;
}
//==========================================================================
//
// PsuedoMIDIDevice :: GetTechnology
//
//==========================================================================
int PsuedoMIDIDevice::GetTechnology() const
{
return MOD_MIDIPORT;
}
//==========================================================================
//
// PsuedoMIDIDevice :: Resume
//
//==========================================================================
int PsuedoMIDIDevice::Resume()
{
if (!Started)
{
if (Stream->Play(true, 1))
{
Started = true;
return 0;
}
return 1;
}
return 0;
}
//==========================================================================
//
// PsuedoMIDIDevice :: Stop
//
//==========================================================================
void PsuedoMIDIDevice::Stop()
{
if (Started)
{
Stream->Stop();
Started = false;
}
}
//==========================================================================
//
// PsuedoMIDIDevice :: Pause
//
//==========================================================================
bool PsuedoMIDIDevice::Pause(bool paused)
{
if (Stream != NULL)
{
return Stream->SetPaused(paused);
}
return true;
}
//==========================================================================
//
// PsuedoMIDIDevice :: StreamOutSync
//
//==========================================================================
int PsuedoMIDIDevice::StreamOutSync(MIDIHDR *header)
{
assert(0);
return 0;
}
//==========================================================================
//
// PsuedoMIDIDevice :: StreamOut
//
//==========================================================================
int PsuedoMIDIDevice::StreamOut(MIDIHDR *header)
{
assert(0);
return 0;
}
//==========================================================================
//
// PsuedoMIDIDevice :: SetTempo
//
//==========================================================================
int PsuedoMIDIDevice::SetTempo(int tempo)
{
return 0;
}
//==========================================================================
//
// PsuedoMIDIDevice :: SetTimeDiv
//
//==========================================================================
int PsuedoMIDIDevice::SetTimeDiv(int timediv)
{
return 0;
}
//==========================================================================
//
// PsuedoMIDIDevice :: GetStats
//
//==========================================================================
FString PsuedoMIDIDevice::GetStats()
{
if (Stream != NULL)
{
return Stream->GetStats();
}
return "Psuedo MIDI device not open";
}
//==========================================================================
//
// FMODMIDIDevice :: Open
//
//==========================================================================
int FMODMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata)
{
return 0;
}
//==========================================================================
//
// FMODMIDIDevice :: Preprocess
//
// Create a standard MIDI file and stream it.
//
//==========================================================================
bool FMODMIDIDevice::Preprocess(MIDIStreamer *song)
{
TArray<BYTE> midi;
song->CreateSMF(midi);
Stream = GSnd->OpenStream((char *)&midi[0], SoundStream::Loop, -1, midi.Size());
return false;
}

View file

@ -1,6 +1,6 @@
/* /*
** music_softsynth_mididevice.cpp ** music_softsynth_mididevice.cpp
** Common base clase for software synthesis MIDI devices. ** Common base class for software synthesis MIDI devices.
** **
**--------------------------------------------------------------------------- **---------------------------------------------------------------------------
** Copyright 2008-2010 Randy Heit ** Copyright 2008-2010 Randy Heit
@ -269,56 +269,6 @@ int SoftSynthMIDIDevice::StreamOut(MIDIHDR *header)
return 0; return 0;
} }
//==========================================================================
//
// SoftSynthMIDIDevice :: PrepareHeader
//
//==========================================================================
int SoftSynthMIDIDevice::PrepareHeader(MIDIHDR *header)
{
return 0;
}
//==========================================================================
//
// SoftSynthMIDIDevice :: UnprepareHeader
//
//==========================================================================
int SoftSynthMIDIDevice::UnprepareHeader(MIDIHDR *header)
{
return 0;
}
//==========================================================================
//
// SoftSynthMIDIDevice :: FakeVolume
//
// Since the softsynth output is rendered as a normal stream, its volume is
// controlled through the GSnd interface, not here.
//
//==========================================================================
bool SoftSynthMIDIDevice::FakeVolume()
{
return false;
}
//==========================================================================
//
// SoftSynthMIDIDevice :: NeedThreadedCallabck
//
// We can service the callback directly rather than using a separate
// thread.
//
//==========================================================================
bool SoftSynthMIDIDevice::NeedThreadedCallback()
{
return false;
}
//========================================================================== //==========================================================================
// //
// SoftSynthMIDIDevice :: Pause // SoftSynthMIDIDevice :: Pause

View file

@ -5497,6 +5497,10 @@
RelativePath=".\src\oplsynth\music_opldumper_mididevice.cpp" RelativePath=".\src\oplsynth\music_opldumper_mididevice.cpp"
> >
</File> </File>
<File
RelativePath=".\src\sound\music_psuedo_mididevice.cpp"
>
</File>
<File <File
RelativePath=".\src\sound\music_smf_midiout.cpp" RelativePath=".\src\sound\music_smf_midiout.cpp"
> >