diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 281be10b9..9fb4cab09 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -818,6 +818,7 @@ add_executable( zdoom WIN32 sound/music_mus_opl.cpp sound/music_stream.cpp sound/music_fluidsynth_mididevice.cpp + sound/music_softsynth_mididevice.cpp sound/music_timidity_mididevice.cpp sound/music_win_mididevice.cpp textures/automaptexture.cpp diff --git a/src/oplsynth/music_opl_mididevice.cpp b/src/oplsynth/music_opl_mididevice.cpp index 9413ae4bb..ade5b5c12 100644 --- a/src/oplsynth/music_opl_mididevice.cpp +++ b/src/oplsynth/music_opl_mididevice.cpp @@ -74,25 +74,9 @@ OPLMIDIDevice::OPLMIDIDevice() { - Stream = NULL; - Tempo = 0; - Division = 0; - Events = NULL; - Started = false; - FWadLump data = Wads.OpenLumpName("GENMIDI"); OPLloadBank(data); -} - -//========================================================================== -// -// OPLMIDIDevice Destructor -// -//========================================================================== - -OPLMIDIDevice::~OPLMIDIDevice() -{ - Close(); + SampleRate = (int)OPL_SAMPLE_RATE; } //========================================================================== @@ -109,25 +93,14 @@ int OPLMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), vo { return 1; } - - Stream = GSnd->CreateStream(FillStream, int(OPL_SAMPLE_RATE / 14) * 4, - SoundStream::Mono | SoundStream::Float, int(OPL_SAMPLE_RATE), this); - if (Stream == NULL) + int ret = OpenStream(14, SoundStream::Mono, callback, userdata); + if (ret == 0) { - return 2; + OPLstopMusic(); + OPLplayMusic(100); + DEBUGOUT("========= New song started ==========\n", 0, 0, 0); } - - Callback = callback; - CallbackData = userdata; - Tempo = 500000; - Division = 100; - CalcTickRate(); - - OPLstopMusic(); - OPLplayMusic(100); - DEBUGOUT("========= New song started ==========\n", 0, 0, 0); - - return 0; + return ret; } //========================================================================== @@ -138,24 +111,8 @@ int OPLMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), vo void OPLMIDIDevice::Close() { - if (Stream != NULL) - { - delete Stream; - Stream = NULL; - } + SoftSynthMIDIDevice::Close(); io->OPLdeinit(); - Started = false; -} - -//========================================================================== -// -// OPLMIDIDevice :: IsOpen -// -//========================================================================== - -bool OPLMIDIDevice::IsOpen() const -{ - return Stream != NULL; } //========================================================================== @@ -169,34 +126,6 @@ int OPLMIDIDevice::GetTechnology() const return MOD_FMSYNTH; } -//========================================================================== -// -// OPLMIDIDevice :: SetTempo -// -//========================================================================== - -int OPLMIDIDevice::SetTempo(int tempo) -{ - Tempo = tempo; - CalcTickRate(); - DEBUGOUT("Tempo changed to %.0f, %.2f samples/tick\n", Tempo, SamplesPerTick, 0); - return 0; -} - -//========================================================================== -// -// OPLMIDIDevice :: SetTimeDiv -// -//========================================================================== - -int OPLMIDIDevice::SetTimeDiv(int timediv) -{ - Division = timediv; - CalcTickRate(); - DEBUGOUT("Division changed to %.0f, %.2f samples/tick\n", Division, SamplesPerTick, 0); - return 0; -} - //========================================================================== // // OPLMIDIDevice :: CalcTickRate @@ -208,219 +137,22 @@ int OPLMIDIDevice::SetTimeDiv(int timediv) void OPLMIDIDevice::CalcTickRate() { - SamplesPerTick = OPL_SAMPLE_RATE / (1000000.0 / Tempo) / Division; - io->SetClockRate(SamplesPerTick); -} - -//========================================================================== -// -// OPLMIDIDevice :: Resume -// -//========================================================================== - -int OPLMIDIDevice::Resume() -{ - if (!Started) - { - if (Stream->Play(true, 1)) - { - Started = true; - return 0; - } - return 1; - } - return 0; -} - -//========================================================================== -// -// OPLMIDIDevice :: Stop -// -//========================================================================== - -void OPLMIDIDevice::Stop() -{ - if (Started) - { - Stream->Stop(); - Started = false; - } -} - -//========================================================================== -// -// OPLMIDIDevice :: StreamOutSync -// -// This version is called from the main game thread and needs to -// synchronize with the player thread. -// -//========================================================================== - -int OPLMIDIDevice::StreamOutSync(MIDIHDR *header) -{ - ChipAccess.Enter(); - StreamOut(header); - ChipAccess.Leave(); - return 0; -} - -//========================================================================== -// -// OPLMIDIDevice :: StreamOut -// -// This version is called from the player thread so does not need to -// arbitrate for access to the Events pointer. -// -//========================================================================== - -int OPLMIDIDevice::StreamOut(MIDIHDR *header) -{ - header->lpNext = NULL; - if (Events == NULL) - { - Events = header; - NextTickIn = SamplesPerTick * *(DWORD *)header->lpData; - Position = 0; - } - else - { - MIDIHDR **p; - - for (p = &Events; *p != NULL; p = &(*p)->lpNext) - { } - *p = header; - } - return 0; -} - -//========================================================================== -// -// OPLMIDIDevice :: PrepareHeader -// -//========================================================================== - -int OPLMIDIDevice::PrepareHeader(MIDIHDR *header) -{ - return 0; -} - -//========================================================================== -// -// OPLMIDIDevice :: UnprepareHeader -// -//========================================================================== - -int OPLMIDIDevice::UnprepareHeader(MIDIHDR *header) -{ - return 0; -} - -//========================================================================== -// -// OPLMIDIDevice :: FakeVolume -// -// Since the OPL output is rendered as a normal stream, its volume is -// controlled through the GSnd interface, not here. -// -//========================================================================== - -bool OPLMIDIDevice::FakeVolume() -{ - return false; -} - -//========================================================================== -// -// OPLMIDIDevice :: NeedThreadedCallabck -// -// OPL can service the callback directly rather than using a separate -// thread. -// -//========================================================================== - -bool OPLMIDIDevice::NeedThreadedCallback() -{ - return false; -} - -//========================================================================== -// -// OPLMIDIDevice :: Pause -// -//========================================================================== - -bool OPLMIDIDevice::Pause(bool paused) -{ - if (Stream != NULL) - { - return Stream->SetPaused(paused); - } - return true; + SoftSynthMIDIDevice::CalcTickRate(); + io->SetClockRate(OPLmusicBlock::SamplesPerTick = SoftSynthMIDIDevice::SamplesPerTick); } //========================================================================== // // OPLMIDIDevice :: PlayTick // -// event[0] = delta time -// event[1] = unused -// event[2] = event +// We derive from two base classes that both define PlayTick(), so we need +// to be unambiguous about which one to use. // //========================================================================== int OPLMIDIDevice::PlayTick() { - DWORD delay = 0; - - while (delay == 0 && Events != NULL) - { - DWORD *event = (DWORD *)(Events->lpData + Position); - if (MEVT_EVENTTYPE(event[2]) == MEVT_TEMPO) - { - SetTempo(MEVT_EVENTPARM(event[2])); - } - else if (MEVT_EVENTTYPE(event[2]) == MEVT_LONGMSG) - { // Should I handle master volume changes? - } - else if (MEVT_EVENTTYPE(event[2]) == 0) - { // Short MIDI event - int status = event[2] & 0xff; - int parm1 = (event[2] >> 8) & 0x7f; - int parm2 = (event[2] >> 16) & 0x7f; - HandleEvent(status, parm1, parm2); - } - - // Advance to next event. - if (event[2] < 0x80000000) - { // Short message - Position += 12; - } - else - { // Long message - Position += 12 + ((MEVT_EVENTPARM(event[2]) + 3) & ~3); - } - - // Did we use up this buffer? - if (Position >= Events->dwBytesRecorded) - { - Events = Events->lpNext; - Position = 0; - - if (Callback != NULL) - { - Callback(MOM_DONE, CallbackData, 0, 0); - } - } - - if (Events == NULL) - { // No more events. Just return something to keep the song playing - // while we wait for more to be submitted. - return int(Division); - } - - delay = *(DWORD *)(Events->lpData + Position); - } - return delay; + return SoftSynthMIDIDevice::PlayTick(); } //========================================================================== @@ -508,14 +240,35 @@ void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2) //========================================================================== // -// OPLMIDIDevice :: FillStream static +// OPLMIDIDevice :: HandleLongEvent // //========================================================================== -bool OPLMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *userdata) +void OPLMIDIDevice::HandleLongEvent(const BYTE *data, int len) { - OPLMIDIDevice *device = (OPLMIDIDevice *)userdata; - return device->ServiceStream(buff, len); +} + +//========================================================================== +// +// OPLMIDIDevice :: ComputeOutput +// +// We override ServiceStream, so this function is never actually called. +// +//========================================================================== + +void OPLMIDIDevice::ComputeOutput(float *buffer, int len) +{ +} + +//========================================================================== +// +// OPLMIDIDevice :: ServiceStream +// +//========================================================================== + +bool OPLMIDIDevice::ServiceStream(void *buff, int numbytes) +{ + return OPLmusicBlock::ServiceStream(buff, numbytes); } //========================================================================== diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 859422387..60a87ea67 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -810,7 +810,7 @@ CCMD (writeopl) } else { - Printf ("Usage: writeopl "); + Printf ("Usage: writeopl \n"); } } diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 2f7a83b22..334002963 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -139,14 +139,14 @@ protected: }; #endif -// OPL implementation of a MIDI output device ------------------------------- +// Base class for software synthesizer MIDI output devices ------------------ -class OPLMIDIDevice : public MIDIDevice, protected OPLmusicBlock +class SoftSynthMIDIDevice : public MIDIDevice { public: - OPLMIDIDevice(); - ~OPLMIDIDevice(); - int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + SoftSynthMIDIDevice(); + ~SoftSynthMIDIDevice(); + void Close(); bool IsOpen() const; int GetTechnology() const; @@ -161,24 +161,51 @@ public: bool FakeVolume(); bool NeedThreadedCallback(); bool Pause(bool paused); - FString GetStats(); protected: - static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata); + FCriticalSection CritSec; + SoundStream *Stream; + double Tempo; + double Division; + double SamplesPerTick; + double NextTickIn; + MIDIHDR *Events; + bool Started; + DWORD Position; + int SampleRate; void (*Callback)(unsigned int, void *, DWORD, DWORD); void *CallbackData; - void CalcTickRate(); - void HandleEvent(int status, int parm1, int parm2); + virtual void CalcTickRate(); int PlayTick(); + int OpenStream(int chunks, int flags, void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata); + virtual bool ServiceStream (void *buff, int numbytes); - SoundStream *Stream; - double Tempo; - double Division; - MIDIHDR *Events; - bool Started; - DWORD Position; + virtual void HandleEvent(int status, int parm1, int parm2) = 0; + virtual void HandleLongEvent(const BYTE *data, int len) = 0; + virtual void ComputeOutput(float *buffer, int len) = 0; +}; + +// OPL implementation of a MIDI output device ------------------------------- + +class OPLMIDIDevice : public SoftSynthMIDIDevice, protected OPLmusicBlock +{ +public: + OPLMIDIDevice(); + int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + void Close(); + int GetTechnology() const; + FString GetStats(); + +protected: + void CalcTickRate(); + int PlayTick(); + void HandleEvent(int status, int parm1, int parm2); + void HandleLongEvent(const BYTE *data, int len); + void ComputeOutput(float *buffer, int len); + bool ServiceStream(void *buff, int numbytes); }; // OPL dumper implementation of a MIDI output device ------------------------ @@ -196,52 +223,22 @@ public: namespace Timidity { struct Renderer; } -class TimidityMIDIDevice : public MIDIDevice +class TimidityMIDIDevice : public SoftSynthMIDIDevice { public: TimidityMIDIDevice(); - TimidityMIDIDevice(int rate); ~TimidityMIDIDevice(); 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 StreamOutSync(MIDIHDR *data); - int Resume(); - void Stop(); - int PrepareHeader(MIDIHDR *data); - int UnprepareHeader(MIDIHDR *data); - bool FakeVolume(); - bool Pause(bool paused); - bool NeedThreadedCallback(); void PrecacheInstruments(const WORD *instruments, int count); - void TimidityVolumeChanged(); FString GetStats(); protected: - static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata); - bool ServiceStream (void *buff, int numbytes); - - void (*Callback)(unsigned int, void *, DWORD, DWORD); - void *CallbackData; - - void CalcTickRate(); - int PlayTick(); - - FCriticalSection CritSec; - SoundStream *Stream; Timidity::Renderer *Renderer; - double Tempo; - double Division; - double SamplesPerTick; - double NextTickIn; - MIDIHDR *Events; - bool Started; - DWORD Position; + + void HandleEvent(int status, int parm1, int parm2); + void HandleLongEvent(const BYTE *data, int len); + void ComputeOutput(float *buffer, int len); }; // Internal TiMidity disk writing version of a MIDI device ------------------ @@ -268,57 +265,26 @@ struct fluid_settings_t; struct fluid_synth_t; #endif -class FluidSynthMIDIDevice : public MIDIDevice +class FluidSynthMIDIDevice : public SoftSynthMIDIDevice { public: FluidSynthMIDIDevice(); ~FluidSynthMIDIDevice(); 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 StreamOutSync(MIDIHDR *data); - int Resume(); - void Stop(); - int PrepareHeader(MIDIHDR *data); - int UnprepareHeader(MIDIHDR *data); - bool FakeVolume(); - bool Pause(bool paused); - bool NeedThreadedCallback(); - void PrecacheInstruments(const WORD *instruments, int count); FString GetStats(); void FluidSettingInt(const char *setting, int value); void FluidSettingNum(const char *setting, double value); void FluidSettingStr(const char *setting, const char *value); protected: - static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata); - bool ServiceStream(void *buff, int numbytes); void HandleEvent(int status, int parm1, int parm2); + void HandleLongEvent(const BYTE *data, int len); + void ComputeOutput(float *buffer, int len); int LoadPatchSets(const char *patches); - void (*Callback)(unsigned int, void *, DWORD, DWORD); - void *CallbackData; - - void CalcTickRate(); - int PlayTick(); - - FCriticalSection CritSec; - SoundStream *Stream; fluid_settings_t *FluidSettings; fluid_synth_t *FluidSynth; - double Tempo; - double Division; - double SamplesPerTick; - double NextTickIn; - MIDIHDR *Events; - bool Started; - DWORD Position; - int SampleRate; #ifdef DYN_FLUIDSYNTH enum { FLUID_FAILED = 1, FLUID_OK = 0 }; @@ -347,6 +313,7 @@ protected: int (STACK_ARGS *fluid_synth_sfload)(fluid_synth_t *, const char *, int); void (STACK_ARGS *fluid_synth_set_reverb)(fluid_synth_t *, double, double, double, double); void (STACK_ARGS *fluid_synth_set_chorus)(fluid_synth_t *, int, double, double, double, int); + int (STACK_ARGS *fluid_synth_sysex)(fluid_synth_t *, const char *, int, char *, int *, int *, int); #ifdef _WIN32 HMODULE FluidSynthDLL; diff --git a/src/sound/music_fluidsynth_mididevice.cpp b/src/sound/music_fluidsynth_mididevice.cpp index 32cd0da46..1e88af055 100644 --- a/src/sound/music_fluidsynth_mididevice.cpp +++ b/src/sound/music_fluidsynth_mididevice.cpp @@ -59,18 +59,18 @@ #define FLUIDSYNTHLIB "libfluidsynth.so.1" #endif -#define FLUID_REVERB_DEFAULT_ROOMSIZE 0.2f -#define FLUID_REVERB_DEFAULT_DAMP 0.0f -#define FLUID_REVERB_DEFAULT_WIDTH 0.5f -#define FLUID_REVERB_DEFAULT_LEVEL 0.9f +#define FLUID_REVERB_DEFAULT_ROOMSIZE 0.2f +#define FLUID_REVERB_DEFAULT_DAMP 0.0f +#define FLUID_REVERB_DEFAULT_WIDTH 0.5f +#define FLUID_REVERB_DEFAULT_LEVEL 0.9f #define FLUID_CHORUS_MOD_SINE 0 #define FLUID_CHORUS_MOD_TRIANGLE 1 -#define FLUID_CHORUS_DEFAULT_N 3 -#define FLUID_CHORUS_DEFAULT_LEVEL 2.0f -#define FLUID_CHORUS_DEFAULT_SPEED 0.3f -#define FLUID_CHORUS_DEFAULT_DEPTH 8.0f +#define FLUID_CHORUS_DEFAULT_N 3 +#define FLUID_CHORUS_DEFAULT_LEVEL 2.0f +#define FLUID_CHORUS_DEFAULT_SPEED 0.3f +#define FLUID_CHORUS_DEFAULT_DEPTH 8.0f #define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE #endif @@ -254,11 +254,6 @@ CUSTOM_CVAR(Int, fluid_chorus_type, FLUID_CHORUS_DEFAULT_TYPE, CVAR_ARCHIVE|CVAR FluidSynthMIDIDevice::FluidSynthMIDIDevice() { - Stream = NULL; - Tempo = 0; - Division = 0; - Events = NULL; - Started = false; FluidSynth = NULL; FluidSettings = NULL; #ifdef DYN_FLUIDSYNTH @@ -366,266 +361,12 @@ int FluidSynthMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWO { return 2; } - Stream = GSnd->CreateStream(FillStream, (SampleRate / 4) * 4, - SoundStream::Float, SampleRate, this); - if (Stream == NULL) + int ret = OpenStream(4, 0, callback, userdata); + if (ret == 0) { - return 2; + fluid_synth_system_reset(FluidSynth); } - - fluid_synth_system_reset(FluidSynth); - Callback = callback; - CallbackData = userdata; - Tempo = 500000; - Division = 100; - CalcTickRate(); - return 0; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: Close -// -//========================================================================== - -void FluidSynthMIDIDevice::Close() -{ - if (Stream != NULL) - { - delete Stream; - Stream = NULL; - } - Started = false; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: IsOpen -// -//========================================================================== - -bool FluidSynthMIDIDevice::IsOpen() const -{ - return Stream != NULL; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: GetTechnology -// -//========================================================================== - -int FluidSynthMIDIDevice::GetTechnology() const -{ - return MOD_SWSYNTH; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: SetTempo -// -//========================================================================== - -int FluidSynthMIDIDevice::SetTempo(int tempo) -{ - Tempo = tempo; - CalcTickRate(); - return 0; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: SetTimeDiv -// -//========================================================================== - -int FluidSynthMIDIDevice::SetTimeDiv(int timediv) -{ - Division = timediv; - CalcTickRate(); - return 0; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: CalcTickRate -// -// Tempo is the number of microseconds per quarter note. -// Division is the number of ticks per quarter note. -// -//========================================================================== - -void FluidSynthMIDIDevice::CalcTickRate() -{ - SamplesPerTick = SampleRate / (1000000.0 / Tempo) / Division; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: Resume -// -//========================================================================== - -int FluidSynthMIDIDevice::Resume() -{ - if (!Started) - { - if (Stream->Play(true, 1)) - { - Started = true; - return 0; - } - return 1; - } - return 0; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: Stop -// -//========================================================================== - -void FluidSynthMIDIDevice::Stop() -{ - if (Started) - { - Stream->Stop(); - Started = false; - } -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: StreamOutSync -// -// This version is called from the main game thread and needs to -// synchronize with the player thread. -// -//========================================================================== - -int FluidSynthMIDIDevice::StreamOutSync(MIDIHDR *header) -{ - CritSec.Enter(); - StreamOut(header); - CritSec.Leave(); - return 0; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: StreamOut -// -// This version is called from the player thread so does not need to -// arbitrate for access to the Events pointer. -// -//========================================================================== - -int FluidSynthMIDIDevice::StreamOut(MIDIHDR *header) -{ - header->lpNext = NULL; - if (Events == NULL) - { - Events = header; - NextTickIn = SamplesPerTick * *(DWORD *)header->lpData; - Position = 0; - } - else - { - MIDIHDR **p; - - for (p = &Events; *p != NULL; p = &(*p)->lpNext) - { } - *p = header; - } - return 0; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: PrepareHeader -// -//========================================================================== - -int FluidSynthMIDIDevice::PrepareHeader(MIDIHDR *header) -{ - return 0; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: UnprepareHeader -// -//========================================================================== - -int FluidSynthMIDIDevice::UnprepareHeader(MIDIHDR *header) -{ - return 0; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: FakeVolume -// -// Since the FluidSynth output is rendered as a normal stream, its volume is -// controlled through the GSnd interface, not here. -// -//========================================================================== - -bool FluidSynthMIDIDevice::FakeVolume() -{ - return false; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: NeedThreadedCallabck -// -// FluidSynth can service the callback directly rather than using a separate -// thread. -// -//========================================================================== - -bool FluidSynthMIDIDevice::NeedThreadedCallback() -{ - return false; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: Pause -// -//========================================================================== - -bool FluidSynthMIDIDevice::Pause(bool paused) -{ - if (Stream != NULL) - { - return Stream->SetPaused(paused); - } - return true; -} - -//========================================================================== -// -// FluidSynthMIDIDevice :: PrecacheInstruments -// -// Each entry is packed as follows: -// Bits 0- 6: Instrument number -// Bits 7-13: Bank number -// Bit 14: Select drum set if 1, tone bank if 0 -// -//========================================================================== - -void FluidSynthMIDIDevice::PrecacheInstruments(const WORD *instruments, int count) -{ -#if 0 - for (int i = 0; i < count; ++i) - { - Renderer->MarkInstrument((instruments[i] >> 7) & 127, instruments[i] >> 14, instruments[i] & 127); - } - Renderer->load_missing_instruments(); -#endif + return ret; } //========================================================================== @@ -674,136 +415,31 @@ void FluidSynthMIDIDevice::HandleEvent(int status, int parm1, int parm2) //========================================================================== // -// FluidSynthMIDIDevice :: PlayTick +// FluidSynthMIDIDevice :: HandleLongEvent // -// event[0] = delta time -// event[1] = unused -// event[2] = event +// Handle SysEx messages. // //========================================================================== -int FluidSynthMIDIDevice::PlayTick() +void FluidSynthMIDIDevice::HandleLongEvent(const BYTE *data, int len) { - DWORD delay = 0; - - while (delay == 0 && Events != NULL) + if (len > 1 && (data[0] == 0xF0 || data[0] == 0xF7)) { - DWORD *event = (DWORD *)(Events->lpData + Position); - if (MEVT_EVENTTYPE(event[2]) == MEVT_TEMPO) - { - SetTempo(MEVT_EVENTPARM(event[2])); - } - else if (MEVT_EVENTTYPE(event[2]) == MEVT_LONGMSG) - { -#if 0 - Renderer->HandleLongMessage((BYTE *)&event[3], MEVT_EVENTPARM(event[2])); -#endif - } - else if (MEVT_EVENTTYPE(event[2]) == 0) - { // Short MIDI event - int status = event[2] & 0xff; - int parm1 = (event[2] >> 8) & 0x7f; - int parm2 = (event[2] >> 16) & 0x7f; - HandleEvent(status, parm1, parm2); - } - - // Advance to next event. - if (event[2] < 0x80000000) - { // Short message - Position += 12; - } - else - { // Long message - Position += 12 + ((MEVT_EVENTPARM(event[2]) + 3) & ~3); - } - - // Did we use up this buffer? - if (Position >= Events->dwBytesRecorded) - { - Events = Events->lpNext; - Position = 0; - - if (Callback != NULL) - { - Callback(MOM_DONE, CallbackData, 0, 0); - } - } - - if (Events == NULL) - { // No more events. Just return something to keep the song playing - // while we wait for more to be submitted. - return int(Division); - } - - delay = *(DWORD *)(Events->lpData + Position); + fluid_synth_sysex(FluidSynth, (const char *)data + 1, len - 1, NULL, NULL, NULL, 0); } - return delay; } //========================================================================== // -// FluidSynthtMIDIDevice :: ServiceStream +// FluidSynthMIDIDevice :: ComputeOutput // //========================================================================== -bool FluidSynthMIDIDevice::ServiceStream (void *buff, int numbytes) +void FluidSynthMIDIDevice::ComputeOutput(float *buffer, int len) { - float *samples = (float *)buff; - float *samples1; - int numsamples = numbytes / sizeof(float) / 2; - bool prev_ended = false; - bool res = true; - - samples1 = samples; - memset(buff, 0, numbytes); - - CritSec.Enter(); - while (Events != NULL && numsamples > 0) - { - double ticky = NextTickIn; - int tick_in = int(NextTickIn); - int samplesleft = MIN(numsamples, tick_in); - - if (samplesleft > 0) - { - fluid_synth_write_float(FluidSynth, samplesleft, - samples1, 0, 2, - samples1, 1, 2); - assert(NextTickIn == ticky); - NextTickIn -= samplesleft; - assert(NextTickIn >= 0); - numsamples -= samplesleft; - samples1 += samplesleft * 2; - } - - if (NextTickIn < 1) - { - int next = PlayTick(); - assert(next >= 0); - if (next == 0) - { // end of song - if (numsamples > 0) - { - fluid_synth_write_float(FluidSynth, numsamples, - samples1, 0, 2, - samples1, 1, 2); - } - res = false; - break; - } - else - { - NextTickIn += SamplesPerTick * next; - assert(NextTickIn >= 0); - } - } - } - if (Events == NULL) - { - res = false; - } - CritSec.Leave(); - return res; + fluid_synth_write_float(FluidSynth, len, + buffer, 0, 2, + buffer, 1, 2); } //========================================================================== @@ -933,18 +569,6 @@ void FluidSynthMIDIDevice::FluidSettingStr(const char *setting, const char *valu } } -//========================================================================== -// -// FluidSynthMIDIDevice :: FillStream static -// -//========================================================================== - -bool FluidSynthMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *userdata) -{ - FluidSynthMIDIDevice *device = (FluidSynthMIDIDevice *)userdata; - return device->ServiceStream(buff, len); -} - //========================================================================== // // FluidSynthMIDIDevice :: GetStats @@ -1023,6 +647,7 @@ bool FluidSynthMIDIDevice::LoadFluidSynth() { (void **)&fluid_synth_sfload, "fluid_synth_sfload" }, { (void **)&fluid_synth_set_reverb, "fluid_synth_set_reverb" }, { (void **)&fluid_synth_set_chorus, "fluid_synth_set_chorus" }, + { (void **)&fluid_synth_sysex, "fluid_synth_sysex" }, }; int fail = 0; diff --git a/src/sound/music_softsynth_mididevice.cpp b/src/sound/music_softsynth_mididevice.cpp new file mode 100644 index 000000000..9d2e69298 --- /dev/null +++ b/src/sound/music_softsynth_mididevice.cpp @@ -0,0 +1,498 @@ +/* +** music_softsynth_mididevice.cpp +** Common base clase for software synthesis 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" +#include "w_wad.h" +#include "v_text.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +CVAR(Bool, synth_watch, false, 0) + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// SoftSynthMIDIDevice Constructor +// +//========================================================================== + +SoftSynthMIDIDevice::SoftSynthMIDIDevice() +{ + Stream = NULL; + Tempo = 0; + Division = 0; + Events = NULL; + Started = false; + SampleRate = GSnd != NULL ? (int)GSnd->GetOutputRate() : 44100; +} + +//========================================================================== +// +// SoftSynthMIDIDevice Destructor +// +//========================================================================== + +SoftSynthMIDIDevice::~SoftSynthMIDIDevice() +{ + Close(); +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: OpenStream +// +//========================================================================== + +int SoftSynthMIDIDevice::OpenStream(int chunks, int flags, void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +{ + Stream = GSnd->CreateStream(FillStream, (SampleRate / chunks) * 4, SoundStream::Float | flags, SampleRate, this); + if (Stream == NULL) + { + return 2; + } + + Callback = callback; + CallbackData = userdata; + Tempo = 500000; + Division = 100; + CalcTickRate(); + return 0; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: Close +// +//========================================================================== + +void SoftSynthMIDIDevice::Close() +{ + if (Stream != NULL) + { + delete Stream; + Stream = NULL; + } + Started = false; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: IsOpen +// +//========================================================================== + +bool SoftSynthMIDIDevice::IsOpen() const +{ + return Stream != NULL; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: GetTechnology +// +//========================================================================== + +int SoftSynthMIDIDevice::GetTechnology() const +{ + return MOD_SWSYNTH; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: SetTempo +// +//========================================================================== + +int SoftSynthMIDIDevice::SetTempo(int tempo) +{ + Tempo = tempo; + CalcTickRate(); + return 0; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: SetTimeDiv +// +//========================================================================== + +int SoftSynthMIDIDevice::SetTimeDiv(int timediv) +{ + Division = timediv; + CalcTickRate(); + return 0; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: CalcTickRate +// +// Tempo is the number of microseconds per quarter note. +// Division is the number of ticks per quarter note. +// +//========================================================================== + +void SoftSynthMIDIDevice::CalcTickRate() +{ + SamplesPerTick = SampleRate / (1000000.0 / Tempo) / Division; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: Resume +// +//========================================================================== + +int SoftSynthMIDIDevice::Resume() +{ + if (!Started) + { + if (Stream->Play(true, 1)) + { + Started = true; + return 0; + } + return 1; + } + return 0; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: Stop +// +//========================================================================== + +void SoftSynthMIDIDevice::Stop() +{ + if (Started) + { + Stream->Stop(); + Started = false; + } +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: StreamOutSync +// +// This version is called from the main game thread and needs to +// synchronize with the player thread. +// +//========================================================================== + +int SoftSynthMIDIDevice::StreamOutSync(MIDIHDR *header) +{ + CritSec.Enter(); + StreamOut(header); + CritSec.Leave(); + return 0; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: StreamOut +// +// This version is called from the player thread so does not need to +// arbitrate for access to the Events pointer. +// +//========================================================================== + +int SoftSynthMIDIDevice::StreamOut(MIDIHDR *header) +{ + header->lpNext = NULL; + if (Events == NULL) + { + Events = header; + NextTickIn = SamplesPerTick * *(DWORD *)header->lpData; + Position = 0; + } + else + { + MIDIHDR **p; + + for (p = &Events; *p != NULL; p = &(*p)->lpNext) + { } + *p = 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 +// +//========================================================================== + +bool SoftSynthMIDIDevice::Pause(bool paused) +{ + if (Stream != NULL) + { + return Stream->SetPaused(paused); + } + return true; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: PlayTick +// +// event[0] = delta time +// event[1] = unused +// event[2] = event +// +//========================================================================== + +int SoftSynthMIDIDevice::PlayTick() +{ + DWORD delay = 0; + + while (delay == 0 && Events != NULL) + { + DWORD *event = (DWORD *)(Events->lpData + Position); + if (MEVT_EVENTTYPE(event[2]) == MEVT_TEMPO) + { + SetTempo(MEVT_EVENTPARM(event[2])); + } + else if (MEVT_EVENTTYPE(event[2]) == MEVT_LONGMSG) + { + HandleLongEvent((BYTE *)&event[3], MEVT_EVENTPARM(event[2])); + } + else if (MEVT_EVENTTYPE(event[2]) == 0) + { // Short MIDI event + int status = event[2] & 0xff; + int parm1 = (event[2] >> 8) & 0x7f; + int parm2 = (event[2] >> 16) & 0x7f; + HandleEvent(status, parm1, parm2); + + if (synth_watch) + { + static const char *const commands[8] = + { + "Note off", + "Note on", + "Poly press", + "Ctrl change", + "Prgm change", + "Chan press", + "Pitch bend", + "SysEx" + }; + char buffer[128]; + mysnprintf(buffer, countof(buffer), "C%02d: %11s %3d %3d\n", (status & 15) + 1, commands[(status >> 4) & 7], parm1, parm2); +#ifdef _WIN32 + OutputDebugString(buffer); +#else + fputs(buffer, stderr); +#endif + } + } + + // Advance to next event. + if (event[2] < 0x80000000) + { // Short message + Position += 12; + } + else + { // Long message + Position += 12 + ((MEVT_EVENTPARM(event[2]) + 3) & ~3); + } + + // Did we use up this buffer? + if (Position >= Events->dwBytesRecorded) + { + Events = Events->lpNext; + Position = 0; + + if (Callback != NULL) + { + Callback(MOM_DONE, CallbackData, 0, 0); + } + } + + if (Events == NULL) + { // No more events. Just return something to keep the song playing + // while we wait for more to be submitted. + return int(Division); + } + + delay = *(DWORD *)(Events->lpData + Position); + } + return delay; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: ServiceStream +// +//========================================================================== + +bool SoftSynthMIDIDevice::ServiceStream (void *buff, int numbytes) +{ + float *samples = (float *)buff; + float *samples1; + int numsamples = numbytes / sizeof(float) / 2; + bool prev_ended = false; + bool res = true; + + samples1 = samples; + memset(buff, 0, numbytes); + + CritSec.Enter(); + while (Events != NULL && numsamples > 0) + { + double ticky = NextTickIn; + int tick_in = int(NextTickIn); + int samplesleft = MIN(numsamples, tick_in); + + if (samplesleft > 0) + { + ComputeOutput(samples1, samplesleft); + assert(NextTickIn == ticky); + NextTickIn -= samplesleft; + assert(NextTickIn >= 0); + numsamples -= samplesleft; + samples1 += samplesleft * 2; + } + + if (NextTickIn < 1) + { + int next = PlayTick(); + assert(next >= 0); + if (next == 0) + { // end of song + if (numsamples > 0) + { + ComputeOutput(samples1, numsamples); + } + res = false; + break; + } + else + { + NextTickIn += SamplesPerTick * next; + assert(NextTickIn >= 0); + } + } + } + + if (Events == NULL) + { + res = false; + } + CritSec.Leave(); + return res; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: FillStream static +// +//========================================================================== + +bool SoftSynthMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *userdata) +{ + SoftSynthMIDIDevice *device = (SoftSynthMIDIDevice *)userdata; + return device->ServiceStream(buff, len); +} diff --git a/src/sound/music_timidity_mididevice.cpp b/src/sound/music_timidity_mididevice.cpp index a13f46e61..9e5be625b 100644 --- a/src/sound/music_timidity_mididevice.cpp +++ b/src/sound/music_timidity_mididevice.cpp @@ -78,8 +78,6 @@ struct FmtChunk // PUBLIC DATA DEFINITIONS ------------------------------------------------- -CVAR(Bool, timidity_watch, false, 0) - // CODE -------------------------------------------------------------------- //========================================================================== @@ -90,33 +88,8 @@ CVAR(Bool, timidity_watch, false, 0) TimidityMIDIDevice::TimidityMIDIDevice() { - Stream = NULL; - Tempo = 0; - Division = 0; - Events = NULL; - Started = false; Renderer = NULL; - Renderer = new Timidity::Renderer(GSnd->GetOutputRate()); -} - -//========================================================================== -// -// TimidityMIDIDevice Constructor with rate parameter -// -//========================================================================== - -TimidityMIDIDevice::TimidityMIDIDevice(int rate) -{ - // Need to support multiple instances with different playback rates - // before we can use this parameter. - rate = (int)GSnd->GetOutputRate(); - Stream = NULL; - Tempo = 0; - Division = 0; - Events = NULL; - Started = false; - Renderer = NULL; - Renderer = new Timidity::Renderer((float)rate); + Renderer = new Timidity::Renderer((float)SampleRate); } //========================================================================== @@ -144,261 +117,12 @@ TimidityMIDIDevice::~TimidityMIDIDevice() int TimidityMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) { - Stream = GSnd->CreateStream(FillStream, int(Renderer->rate / 2) * 4, - SoundStream::Float, int(Renderer->rate), this); - if (Stream == NULL) + int ret = OpenStream(2, 0, callback, userdata); + if (ret == 0) { - return 2; + Renderer->Reset(); } - - Callback = callback; - CallbackData = userdata; - Tempo = 500000; - Division = 100; - CalcTickRate(); - Renderer->Reset(); - return 0; -} - -//========================================================================== -// -// TimidityMIDIDevice :: Close -// -//========================================================================== - -void TimidityMIDIDevice::Close() -{ - if (Stream != NULL) - { - delete Stream; - Stream = NULL; - } - Started = false; -} - -//========================================================================== -// -// TimidityMIDIDevice :: IsOpen -// -//========================================================================== - -bool TimidityMIDIDevice::IsOpen() const -{ - return Stream != NULL; -} - -//========================================================================== -// -// TimidityMIDIDevice :: GetTechnology -// -//========================================================================== - -int TimidityMIDIDevice::GetTechnology() const -{ - return MOD_SWSYNTH; -} - -//========================================================================== -// -// TimidityMIDIDevice :: SetTempo -// -//========================================================================== - -int TimidityMIDIDevice::SetTempo(int tempo) -{ - Tempo = tempo; - CalcTickRate(); - return 0; -} - -//========================================================================== -// -// TimidityMIDIDevice :: SetTimeDiv -// -//========================================================================== - -int TimidityMIDIDevice::SetTimeDiv(int timediv) -{ - Division = timediv; - CalcTickRate(); - return 0; -} - -//========================================================================== -// -// TimidityMIDIDevice :: CalcTickRate -// -// Tempo is the number of microseconds per quarter note. -// Division is the number of ticks per quarter note. -// -//========================================================================== - -void TimidityMIDIDevice::CalcTickRate() -{ - SamplesPerTick = Renderer->rate / (1000000.0 / Tempo) / Division; -} - -//========================================================================== -// -// TimidityMIDIDevice :: Resume -// -//========================================================================== - -int TimidityMIDIDevice::Resume() -{ - if (!Started) - { - if (Stream->Play(true, 1/*timidity_mastervolume*/)) - { - Started = true; - return 0; - } - return 1; - } - return 0; -} - -//========================================================================== -// -// TimidityMIDIDevice :: Stop -// -//========================================================================== - -void TimidityMIDIDevice::Stop() -{ - if (Started) - { - Stream->Stop(); - Started = false; - } -} - -//========================================================================== -// -// TimidityMIDIDevice :: StreamOutSync -// -// This version is called from the main game thread and needs to -// synchronize with the player thread. -// -//========================================================================== - -int TimidityMIDIDevice::StreamOutSync(MIDIHDR *header) -{ - CritSec.Enter(); - StreamOut(header); - CritSec.Leave(); - return 0; -} - -//========================================================================== -// -// TimidityMIDIDevice :: StreamOut -// -// This version is called from the player thread so does not need to -// arbitrate for access to the Events pointer. -// -//========================================================================== - -int TimidityMIDIDevice::StreamOut(MIDIHDR *header) -{ - header->lpNext = NULL; - if (Events == NULL) - { - Events = header; - NextTickIn = SamplesPerTick * *(DWORD *)header->lpData; - Position = 0; - } - else - { - MIDIHDR **p; - - for (p = &Events; *p != NULL; p = &(*p)->lpNext) - { } - *p = header; - } - return 0; -} - -//========================================================================== -// -// TimidityMIDIDevice :: PrepareHeader -// -//========================================================================== - -int TimidityMIDIDevice::PrepareHeader(MIDIHDR *header) -{ - return 0; -} - -//========================================================================== -// -// TimidityMIDIDevice :: UnprepareHeader -// -//========================================================================== - -int TimidityMIDIDevice::UnprepareHeader(MIDIHDR *header) -{ - return 0; -} - -//========================================================================== -// -// TimidityMIDIDevice :: FakeVolume -// -// Since the TiMidity output is rendered as a normal stream, its volume is -// controlled through the GSnd interface, not here. -// -//========================================================================== - -bool TimidityMIDIDevice::FakeVolume() -{ - return false; -} - -//========================================================================== -// -// TimidityMIDIDevice :: NeedThreadedCallabck -// -// OPL can service the callback directly rather than using a separate -// thread. -// -//========================================================================== - -bool TimidityMIDIDevice::NeedThreadedCallback() -{ - return false; -} - - -//========================================================================== -// -// TimidityMIDIDevice :: TimidityVolumeChanged -// -//========================================================================== - -void TimidityMIDIDevice::TimidityVolumeChanged() -{ - /* - if (Stream != NULL) - { - Stream->SetVolume(timidity_mastervolume); - } - */ -} - -//========================================================================== -// -// TimidityMIDIDevice :: Pause -// -//========================================================================== - -bool TimidityMIDIDevice::Pause(bool paused) -{ - if (Stream != NULL) - { - return Stream->SetPaused(paused); - } - return true; + return ret; } //========================================================================== @@ -423,164 +147,35 @@ void TimidityMIDIDevice::PrecacheInstruments(const WORD *instruments, int count) //========================================================================== // -// TimidityMIDIDevice :: PlayTick -// -// event[0] = delta time -// event[1] = unused -// event[2] = event +// TimidityMIDIDevice :: HandleEvent // //========================================================================== -int TimidityMIDIDevice::PlayTick() +void TimidityMIDIDevice::HandleEvent(int status, int parm1, int parm2) { - DWORD delay = 0; - - while (delay == 0 && Events != NULL) - { - DWORD *event = (DWORD *)(Events->lpData + Position); - if (MEVT_EVENTTYPE(event[2]) == MEVT_TEMPO) - { - SetTempo(MEVT_EVENTPARM(event[2])); - } - else if (MEVT_EVENTTYPE(event[2]) == MEVT_LONGMSG) - { - Renderer->HandleLongMessage((BYTE *)&event[3], MEVT_EVENTPARM(event[2])); - } - else if (MEVT_EVENTTYPE(event[2]) == 0) - { // Short MIDI event - int status = event[2] & 0xff; - int parm1 = (event[2] >> 8) & 0x7f; - int parm2 = (event[2] >> 16) & 0x7f; - Renderer->HandleEvent(status, parm1, parm2); - - if (timidity_watch) - { - static const char *const commands[8] = - { - "Note off", - "Note on", - "Poly press", - "Ctrl change", - "Prgm change", - "Chan press", - "Pitch bend", - "SysEx" - }; -#ifdef _WIN32 - char buffer[128]; - mysnprintf(buffer, countof(buffer), "C%02d: %11s %3d %3d\n", (status & 15) + 1, commands[(status >> 4) & 7], parm1, parm2); - OutputDebugString(buffer); -#else - //fprintf(stderr, "C%02d: %11s %3d %3d\n", (status & 15) + 1, commands[(status >> 4) & 7], parm1, parm2); -#endif - } - } - - // Advance to next event. - if (event[2] < 0x80000000) - { // Short message - Position += 12; - } - else - { // Long message - Position += 12 + ((MEVT_EVENTPARM(event[2]) + 3) & ~3); - } - - // Did we use up this buffer? - if (Position >= Events->dwBytesRecorded) - { - Events = Events->lpNext; - Position = 0; - - if (Callback != NULL) - { - Callback(MOM_DONE, CallbackData, 0, 0); - } - } - - if (Events == NULL) - { // No more events. Just return something to keep the song playing - // while we wait for more to be submitted. - return int(Division); - } - - delay = *(DWORD *)(Events->lpData + Position); - } - return delay; + Renderer->HandleEvent(status, parm1, parm2); } //========================================================================== // -// TimidityMIDIDevice :: ServiceStream +// TimidityMIDIDevice :: HandleLongEvent // //========================================================================== -bool TimidityMIDIDevice::ServiceStream (void *buff, int numbytes) +void TimidityMIDIDevice::HandleLongEvent(const BYTE *data, int len) { - float *samples = (float *)buff; - float *samples1; - int numsamples = numbytes / sizeof(float) / 2; - bool prev_ended = false; - bool res = true; - - samples1 = samples; - memset(buff, 0, numbytes); - - CritSec.Enter(); - while (Events != NULL && numsamples > 0) - { - double ticky = NextTickIn; - int tick_in = int(NextTickIn); - int samplesleft = MIN(numsamples, tick_in); - - if (samplesleft > 0) - { - Renderer->ComputeOutput(samples1, samplesleft); - assert(NextTickIn == ticky); - NextTickIn -= samplesleft; - assert(NextTickIn >= 0); - numsamples -= samplesleft; - samples1 += samplesleft * 2; - } - - if (NextTickIn < 1) - { - int next = PlayTick(); - assert(next >= 0); - if (next == 0) - { // end of song - if (numsamples > 0) - { - Renderer->ComputeOutput(samples1, numsamples); - } - res = false; - break; - } - else - { - NextTickIn += SamplesPerTick * next; - assert(NextTickIn >= 0); - } - } - } - if (Events == NULL) - { - res = false; - } - CritSec.Leave(); - return res; + Renderer->HandleLongMessage(data, len); } //========================================================================== // -// TimidityMIDIDevice :: FillStream static +// TimidityMIDIDevice :: ComputeOutput // //========================================================================== -bool TimidityMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *userdata) +void TimidityMIDIDevice::ComputeOutput(float *buffer, int len) { - TimidityMIDIDevice *device = (TimidityMIDIDevice *)userdata; - return device->ServiceStream(buff, len); + Renderer->ComputeOutput(buffer, len); } //========================================================================== diff --git a/zdoom.vcproj b/zdoom.vcproj index 3edc5d20e..7885a5174 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -5485,6 +5485,10 @@ RelativePath=".\src\oplsynth\music_opldumper_mididevice.cpp" > + +