From b6941be55fa6aa4f80880003b963af19fa06695f Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sat, 2 Oct 2010 02:19:50 +0000 Subject: [PATCH] - Reimplemented FMOD MIDI playback as a psuedo-MIDI device so that it can take advantage of XMI subsongs. SVN r2872 (trunk) --- src/sound/i_musicinterns.h | 54 ++++- src/sound/music_midistream.cpp | 157 +++++++++++--- src/sound/music_psuedo_mididevice.cpp | 260 +++++++++++++++++++++++ src/sound/music_softsynth_mididevice.cpp | 52 +---- zdoom.vcproj | 4 + 5 files changed, 434 insertions(+), 93 deletions(-) create mode 100644 src/sound/music_psuedo_mididevice.cpp diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index d518dbab9..6c43cd904 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -72,6 +72,8 @@ typedef BYTE *LPSTR; #endif #endif +class MIDIStreamer; + class MIDIDevice { public: @@ -88,16 +90,17 @@ public: virtual int StreamOutSync(MIDIHDR *data) = 0; virtual int Resume() = 0; virtual void Stop() = 0; - virtual int PrepareHeader(MIDIHDR *data) = 0; - virtual int UnprepareHeader(MIDIHDR *data) = 0; - virtual bool FakeVolume() = 0; + virtual int PrepareHeader(MIDIHDR *data); + virtual int UnprepareHeader(MIDIHDR *data); + virtual bool FakeVolume(); virtual bool Pause(bool paused) = 0; - virtual bool NeedThreadedCallback() = 0; + virtual bool NeedThreadedCallback(); virtual void PrecacheInstruments(const WORD *instruments, int count); virtual void TimidityVolumeChanged(); virtual void FluidSettingInt(const char *setting, int value); virtual void FluidSettingNum(const char *setting, double value); virtual void FluidSettingStr(const char *setting, const char *value); + virtual bool Preprocess(MIDIStreamer *song); virtual FString GetStats(); }; @@ -139,6 +142,42 @@ protected: }; #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 ------------------ class SoftSynthMIDIDevice : public MIDIDevice @@ -156,10 +195,6 @@ public: int StreamOutSync(MIDIHDR *data); int Resume(); void Stop(); - int PrepareHeader(MIDIHDR *data); - int UnprepareHeader(MIDIHDR *data); - bool FakeVolume(); - bool NeedThreadedCallback(); bool Pause(bool paused); protected: @@ -335,10 +370,10 @@ enum EMIDIDevice MIDI_OPL, MIDI_GUS, MIDI_Fluid, + MIDI_FMOD, // only used by I_RegisterSong MIDI_Null, - MIDI_FMOD, MIDI_Timidity }; @@ -378,6 +413,7 @@ protected: static void Callback(unsigned int uMsg, void *userdata, DWORD dwParam1, DWORD dwParam2); // Virtuals for subclasses to override + virtual void StartPlayback(); virtual void CheckCaps(int tech); virtual void DoInitialSetup() = 0; virtual void DoRestart() = 0; diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 5d271a11e..aa1e3feab 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -239,6 +239,10 @@ void MIDIStreamer::Play(bool looping, int subsong) break; #endif + case MIDI_FMOD: + MIDI = new FMODMIDIDevice; + break; + case MIDI_GUS: MIDI = new TimidityMIDIDevice; break; @@ -264,6 +268,49 @@ void MIDIStreamer::Play(bool looping, int subsong) SetMIDISubsong(subsong); 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(); LoopLimit = 0; @@ -312,34 +359,6 @@ void MIDIStreamer::Play(bool looping, int subsong) } } 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() { if (MIDI != NULL) @@ -1102,11 +1127,8 @@ static void WriteVarLen (TArray &file, DWORD value) void MIDIStreamer::SetTempo(int new_tempo) { - if (NULL == MIDI) - { - InitialTempo = new_tempo; - } - else if (0 == MIDI->SetTempo(new_tempo)) + InitialTempo = new_tempo; + if (NULL != MIDI && 0 == MIDI->SetTempo(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 diff --git a/src/sound/music_psuedo_mididevice.cpp b/src/sound/music_psuedo_mididevice.cpp new file mode 100644 index 000000000..11ddea63e --- /dev/null +++ b/src/sound/music_psuedo_mididevice.cpp @@ -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 midi; + + song->CreateSMF(midi); + Stream = GSnd->OpenStream((char *)&midi[0], SoundStream::Loop, -1, midi.Size()); + return false; +} diff --git a/src/sound/music_softsynth_mididevice.cpp b/src/sound/music_softsynth_mididevice.cpp index 9d2e69298..5cb21687a 100644 --- a/src/sound/music_softsynth_mididevice.cpp +++ b/src/sound/music_softsynth_mididevice.cpp @@ -1,6 +1,6 @@ /* ** 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 @@ -269,56 +269,6 @@ int SoftSynthMIDIDevice::StreamOut(MIDIHDR *header) 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 diff --git a/zdoom.vcproj b/zdoom.vcproj index 8e6c9b9ca..9986f70b9 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -5497,6 +5497,10 @@ RelativePath=".\src\oplsynth\music_opldumper_mididevice.cpp" > + +