- moved the stream handling out of the MIDI device into the MIDIStreamer class.

This isn't the final location but this means that the device is merely a data provider, with the sole exception of the Win32 MIDI device whose unwieldy usage requirements unfortunately dictate much of the needed interface here.
This commit is contained in:
Christoph Oelckers 2019-09-28 10:00:22 +02:00
parent 67169b80e5
commit 123ed9d01d
12 changed files with 165 additions and 156 deletions

View file

@ -44,9 +44,9 @@ public:
ADLMIDIDevice(const ADLConfig *config);
~ADLMIDIDevice();
int Open(MidiCallback, void *userdata);
int OpenRenderer();
int GetDeviceType() const override { return MDEV_ADL; }
protected:
void HandleEvent(int status, int parm1, int parm2);
@ -133,14 +133,10 @@ int ADLMIDIDevice::LoadCustomBank(const ADLConfig *config)
//
//==========================================================================
int ADLMIDIDevice::Open(MidiCallback callback, void *userdata)
int ADLMIDIDevice::OpenRenderer()
{
int ret = OpenStream(2, 0, callback, userdata);
if (ret == 0)
{
adl_rt_resetState(Renderer);
}
return ret;
adl_rt_resetState(Renderer);
return 0;
}
//==========================================================================

View file

@ -187,3 +187,14 @@ FString MIDIDevice::GetStats()
{
return "This MIDI device does not have any stats.";
}
//==========================================================================
//
// MIDIDevice :: GetStreamInfo
//
//==========================================================================
SoundStreamInfo MIDIDevice::GetStreamInfo() const
{
return { 0, 0, 0 }; // i.e. do not use streaming.
}

View file

@ -54,7 +54,7 @@ public:
FluidSynthMIDIDevice(int samplerate, const FluidConfig *config, int (*printfunc_)(const char *, ...));
~FluidSynthMIDIDevice();
int Open(MidiCallback, void *userdata);
int OpenRenderer();
FString GetStats();
void ChangeSettingInt(const char *setting, int value);
void ChangeSettingNum(const char *setting, double value);
@ -165,6 +165,8 @@ const char *BaseFileSearch(const char *file, const char *ext, bool lookfirstinpr
FluidSynthMIDIDevice::FluidSynthMIDIDevice(int samplerate, const FluidConfig *config, int (*printfunc_)(const char*, ...) = nullptr)
: SoftSynthMIDIDevice(samplerate <= 0? config->fluid_samplerate : samplerate, 22050, 96000)
{
StreamBlockSize = 4;
// These are needed for changing the settings. If something posts a transient config in here we got no way to retrieve the values afterward.
fluid_reverb_roomsize = config->fluid_reverb_roomsize;
fluid_reverb_damping = config->fluid_reverb_damping;
@ -255,18 +257,10 @@ FluidSynthMIDIDevice::~FluidSynthMIDIDevice()
//
//==========================================================================
int FluidSynthMIDIDevice::Open(MidiCallback callback, void *userdata)
int FluidSynthMIDIDevice::OpenRenderer()
{
if (FluidSynth == NULL)
{
return 2;
}
int ret = OpenStream(4, 0, callback, userdata);
if (ret == 0)
{
fluid_synth_system_reset(FluidSynth);
}
return ret;
fluid_synth_system_reset(FluidSynth);
return 0;
}
//==========================================================================

View file

@ -56,7 +56,7 @@ class OPLMIDIDevice : public SoftSynthMIDIDevice, protected OPLmusicBlock
{
public:
OPLMIDIDevice(const OPLMidiConfig *config);
int Open(MidiCallback, void *userdata);
int OpenRenderer();
void Close();
int GetTechnology() const;
FString GetStats();
@ -87,6 +87,7 @@ OPLMIDIDevice::OPLMIDIDevice(const OPLMidiConfig *config)
{
FullPan = config->fullpan;
memcpy(OPLinstruments, config->OPLinstruments, sizeof(OPLinstruments));
StreamBlockSize = 14;
}
//==========================================================================
@ -97,19 +98,16 @@ OPLMIDIDevice::OPLMIDIDevice(const OPLMidiConfig *config)
//
//==========================================================================
int OPLMIDIDevice::Open(MidiCallback callback, void *userdata)
int OPLMIDIDevice::OpenRenderer()
{
if (io == NULL || 0 == (NumChips = io->Init(currentCore, NumChips, FullPan, true)))
{
return 1;
}
int ret = OpenStream(14, (FullPan || io->IsOPL3) ? 0 : SoundStream::Mono, callback, userdata);
if (ret == 0)
{
stopAllVoices();
resetAllControllers(100);
}
return ret;
isMono = !FullPan && !io->IsOPL3;
stopAllVoices();
resetAllControllers(100);
return 0;
}
//==========================================================================

View file

@ -48,7 +48,7 @@ public:
~OPNMIDIDevice();
int Open(MidiCallback, void *userdata);
int OpenRenderer();
int GetDeviceType() const override { return MDEV_OPN; }
protected:
@ -146,14 +146,10 @@ int OPNMIDIDevice::LoadCustomBank(const char *bankfile)
//
//==========================================================================
int OPNMIDIDevice::Open(MidiCallback callback, void *userdata)
int OPNMIDIDevice::OpenRenderer()
{
int ret = OpenStream(2, 0, callback, userdata);
if (ret == 0)
{
opn2_rt_resetState(Renderer);
}
return ret;
opn2_rt_resetState(Renderer);
return 0;
}
//==========================================================================

View file

@ -66,7 +66,6 @@
SoftSynthMIDIDevice::SoftSynthMIDIDevice(int samplerate, int minrate, int maxrate)
{
Stream = NULL;
Tempo = 0;
Division = 0;
Events = NULL;
@ -88,29 +87,33 @@ SoftSynthMIDIDevice::~SoftSynthMIDIDevice()
//==========================================================================
//
// SoftSynthMIDIDevice :: OpenStream
// SoftSynthMIDIDevice :: GwrStreamInfo
//
//==========================================================================
int SoftSynthMIDIDevice::OpenStream(int chunks, int flags, MidiCallback callback, void *userdata)
SoundStreamInfo SoftSynthMIDIDevice::GetStreamInfo() const
{
int chunksize = (SampleRate / chunks) * 4;
if (!(flags & SoundStream::Mono))
int chunksize = (SampleRate / StreamBlockSize) * 4;
if (!isMono)
{
chunksize *= 2;
}
Stream = GSnd->CreateStream(FillStream, chunksize, SoundStream::Float | flags, SampleRate, this);
if (Stream == NULL)
{
return 2;
}
return { chunksize, SampleRate, isMono };
}
Callback = callback;
CallbackData = userdata;
//==========================================================================
//
// SoftSynthMIDIDevice :: Open
//
//==========================================================================
int SoftSynthMIDIDevice::Open()
{
Tempo = 500000;
Division = 100;
CalcTickRate();
return 0;
isOpen = true;
return OpenRenderer();
}
//==========================================================================
@ -121,11 +124,6 @@ int SoftSynthMIDIDevice::OpenStream(int chunks, int flags, MidiCallback callback
void SoftSynthMIDIDevice::Close()
{
if (Stream != NULL)
{
delete Stream;
Stream = NULL;
}
Started = false;
}
@ -137,7 +135,7 @@ void SoftSynthMIDIDevice::Close()
bool SoftSynthMIDIDevice::IsOpen() const
{
return Stream != NULL;
return isOpen;
}
//==========================================================================
@ -199,15 +197,7 @@ void SoftSynthMIDIDevice::CalcTickRate()
int SoftSynthMIDIDevice::Resume()
{
if (!Started)
{
if (Stream->Play(true, 1))
{
Started = true;
return 0;
}
return 1;
}
Started = 1;
return 0;
}
@ -219,11 +209,6 @@ int SoftSynthMIDIDevice::Resume()
void SoftSynthMIDIDevice::Stop()
{
if (Started)
{
Stream->Stop();
Started = false;
}
}
//==========================================================================
@ -279,10 +264,6 @@ int SoftSynthMIDIDevice::StreamOut(MidiHeader *header)
bool SoftSynthMIDIDevice::Pause(bool paused)
{
if (Stream != NULL)
{
return Stream->SetPaused(paused);
}
return true;
}
@ -437,15 +418,3 @@ bool SoftSynthMIDIDevice::ServiceStream (void *buff, int numbytes)
}
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);
}

View file

@ -72,7 +72,7 @@ public:
TimidityMIDIDevice(GUSConfig *config, int samplerate);
~TimidityMIDIDevice();
int Open(MidiCallback, void *userdata);
int OpenRenderer();
void PrecacheInstruments(const uint16_t *instruments, int count);
int GetDeviceType() const override { return MDEV_GUS; }
@ -182,14 +182,10 @@ TimidityMIDIDevice::~TimidityMIDIDevice()
//
//==========================================================================
int TimidityMIDIDevice::Open(MidiCallback callback, void *userdata)
int TimidityMIDIDevice::OpenRenderer()
{
int ret = OpenStream(2, 0, callback, userdata);
if (ret == 0)
{
Renderer->Reset();
}
return ret;
Renderer->Reset();
return 9;
}
//==========================================================================

View file

@ -53,7 +53,7 @@ public:
TimidityPPMIDIDevice(TimidityConfig *config, int samplerate);
~TimidityPPMIDIDevice();
int Open(MidiCallback, void *userdata);
int OpenRenderer();
void PrecacheInstruments(const uint16_t *instruments, int count);
//FString GetStats();
int GetDeviceType() const override { return MDEV_TIMIDITY; }
@ -133,14 +133,10 @@ TimidityPPMIDIDevice::~TimidityPPMIDIDevice ()
//
//==========================================================================
int TimidityPPMIDIDevice::Open(MidiCallback callback, void *userdata)
int TimidityPPMIDIDevice::OpenRenderer()
{
int ret = OpenStream(2, 0, callback, userdata);
if (ret == 0 && Renderer != nullptr)
{
Renderer->playmidi_stream_init();
}
return ret;
Renderer->playmidi_stream_init();
return 0;
}
//==========================================================================

View file

@ -53,7 +53,7 @@ public:
WildMIDIDevice(WildMidiConfig* config, int samplerate);
~WildMIDIDevice();
int Open(MidiCallback, void *userdata);
int OpenRenderer();
void PrecacheInstruments(const uint16_t *instruments, int count);
FString GetStats();
int GetDeviceType() const override { return MDEV_WILDMIDI; }
@ -164,18 +164,9 @@ WildMIDIDevice::~WildMIDIDevice()
//
//==========================================================================
int WildMIDIDevice::Open(MidiCallback callback, void *userdata)
int WildMIDIDevice::OpenRenderer()
{
if (Renderer == NULL)
{
return 1;
}
int ret = OpenStream(2, 0, callback, userdata);
if (ret == 0)
{
// Renderer->Reset();
}
return ret;
return 0; // This one's a no-op
}
//==========================================================================

View file

@ -70,7 +70,7 @@ class WinMIDIDevice : public MIDIDevice
public:
WinMIDIDevice(int dev_id);
~WinMIDIDevice();
int Open(MidiCallback, void *userdata);
int Open();
void Close();
bool IsOpen() const;
int GetTechnology() const;
@ -105,9 +105,6 @@ public:
int HeaderIndex;
bool VolumeWorks;
MidiCallback Callback;
void *CallbackData;
HANDLE BufferDoneEvent;
HANDLE ExitEvent;
HANDLE PlayerThread;
@ -173,12 +170,10 @@ WinMIDIDevice::~WinMIDIDevice()
//
//==========================================================================
int WinMIDIDevice::Open(MidiCallback callback, void *userdata)
int WinMIDIDevice::Open()
{
MMRESULT err;
Callback = callback;
CallbackData = userdata;
if (MidiOut == nullptr)
{
err = midiStreamOpen(&MidiOut, &DeviceID, 1, (DWORD_PTR)CallbackFunc, (DWORD_PTR)this, CALLBACK_FUNCTION);

View file

@ -155,6 +155,15 @@ struct WildMidiConfig
extern WildMidiConfig wildMidiConfig;
struct SoundStreamInfo
{
// Format is always 32 bit float. If mBufferSize is 0, the song doesn't use streaming but plays through a different interface.
int mBufferSize;
int mSampleRate;
int mNumChannels;
};
class MIDIStreamer;
typedef void(*MidiCallback)(void *);
@ -164,7 +173,12 @@ public:
MIDIDevice() = default;
virtual ~MIDIDevice();
virtual int Open(MidiCallback, void *userdata) = 0;
void SetCallback(MidiCallback callback, void* userdata)
{
Callback = callback;
CallbackData = userdata;
}
virtual int Open() = 0;
virtual void Close() = 0;
virtual bool IsOpen() const = 0;
virtual int GetTechnology() const = 0;
@ -188,6 +202,11 @@ public:
virtual FString GetStats();
virtual int GetDeviceType() const { return MDEV_DEFAULT; }
virtual bool CanHandleSysex() const { return true; }
virtual SoundStreamInfo GetStreamInfo() const;
protected:
MidiCallback Callback;
void* CallbackData;
};
@ -215,28 +234,29 @@ public:
void Stop();
bool Pause(bool paused);
virtual int Open();
virtual bool ServiceStream(void* buff, int numbytes);
int GetSampleRate() const { return SampleRate; }
SoundStreamInfo GetStreamInfo() const override;
protected:
std::mutex CritSec;
SoundStream *Stream;
double Tempo;
double Division;
double SamplesPerTick;
double NextTickIn;
MidiHeader *Events;
bool Started;
bool isMono = false; // only relevant for OPL.
bool isOpen = false;
uint32_t Position;
int SampleRate;
MidiCallback Callback;
void *CallbackData;
int StreamBlockSize = 2;
virtual void CalcTickRate();
int PlayTick();
int OpenStream(int chunks, int flags, MidiCallback, void *userdata);
static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata);
virtual bool ServiceStream (void *buff, int numbytes);
int GetSampleRate() const { return SampleRate; }
virtual int OpenRenderer() = 0;
virtual void HandleEvent(int status, int parm1, int parm2) = 0;
virtual void HandleLongEvent(const uint8_t *data, int len) = 0;
virtual void ComputeOutput(float *buffer, int len) = 0;
@ -250,24 +270,25 @@ class MIDIWaveWriter : public SoftSynthMIDIDevice
public:
MIDIWaveWriter(const char *filename, SoftSynthMIDIDevice *devtouse);
~MIDIWaveWriter();
int Resume();
int Open(MidiCallback cb, void *userdata)
int Resume() override;
int Open() override
{
return playDevice->Open(cb, userdata);
return playDevice->Open();
}
void Stop();
void HandleEvent(int status, int parm1, int parm2) { playDevice->HandleEvent(status, parm1, parm2); }
void HandleLongEvent(const uint8_t *data, int len) { playDevice->HandleLongEvent(data, len); }
void ComputeOutput(float *buffer, int len) { playDevice->ComputeOutput(buffer, len); }
int StreamOutSync(MidiHeader *data) { return playDevice->StreamOutSync(data); }
int StreamOut(MidiHeader *data) { return playDevice->StreamOut(data); }
int OpenRenderer() override { return playDevice->OpenRenderer(); }
void Stop() override;
void HandleEvent(int status, int parm1, int parm2) override { playDevice->HandleEvent(status, parm1, parm2); }
void HandleLongEvent(const uint8_t *data, int len) override { playDevice->HandleLongEvent(data, len); }
void ComputeOutput(float *buffer, int len) override { playDevice->ComputeOutput(buffer, len); }
int StreamOutSync(MidiHeader *data) override { return playDevice->StreamOutSync(data); }
int StreamOut(MidiHeader *data) override { return playDevice->StreamOut(data); }
int GetDeviceType() const override { return playDevice->GetDeviceType(); }
bool ServiceStream (void *buff, int numbytes) { return playDevice->ServiceStream(buff, numbytes); }
int GetTechnology() const { return playDevice->GetTechnology(); }
int SetTempo(int tempo) { return playDevice->SetTempo(tempo); }
int SetTimeDiv(int timediv) { return playDevice->SetTimeDiv(timediv); }
bool IsOpen() const { return playDevice->IsOpen(); }
void CalcTickRate() { playDevice->CalcTickRate(); }
bool ServiceStream (void *buff, int numbytes) override { return playDevice->ServiceStream(buff, numbytes); }
int GetTechnology() const override { return playDevice->GetTechnology(); }
int SetTempo(int tempo) override { return playDevice->SetTempo(tempo); }
int SetTimeDiv(int timediv) override { return playDevice->SetTimeDiv(timediv); }
bool IsOpen() const override { return playDevice->IsOpen(); }
void CalcTickRate() override { playDevice->CalcTickRate(); }
protected:
FileWriter *File;
@ -307,6 +328,7 @@ public:
}
bool DumpWave(const char *filename, int subsong, int samplerate);
static bool FillStream(SoundStream* stream, void* buff, int len, void* userdata);
protected:
@ -337,7 +359,7 @@ protected:
SONG_ERROR
};
MIDIDevice *MIDI;
MIDIDevice *MIDI = nullptr;
uint32_t Events[2][MAX_MIDI_EVENTS*3];
MidiHeader Buffer[2];
int BufferNum;
@ -351,8 +373,8 @@ protected:
bool CallbackIsThreaded;
int LoopLimit;
FString Args;
MIDISource *source;
MIDISource *source = nullptr;
SoundStream* Stream = nullptr;
};
// Anything supported by the sound system out of the box --------------------

View file

@ -77,7 +77,7 @@ extern unsigned mididevice;
MIDIStreamer::MIDIStreamer(EMidiDevice type, const char *args)
:
MIDI(0), DeviceType(type), Args(args)
DeviceType(type), Args(args)
{
memset(Buffer, 0, sizeof(Buffer));
}
@ -91,10 +91,14 @@ MIDIStreamer::MIDIStreamer(EMidiDevice type, const char *args)
MIDIStreamer::~MIDIStreamer()
{
Stop();
if (MIDI != NULL)
if (MIDI != nullptr)
{
delete MIDI;
}
if (Stream != nullptr)
{
delete Stream;
}
if (source != nullptr)
{
delete source;
@ -342,8 +346,9 @@ bool MIDIStreamer::InitPlayback()
VolumeChanged = false;
Restarting = true;
InitialPlayback = true;
if (MIDI) MIDI->SetCallback(Callback, this);
if (MIDI == NULL || 0 != MIDI->Open(Callback, this))
if (MIDI == NULL || 0 != MIDI->Open())
{
Printf(PRINT_BOLD, "Could not open MIDI out device\n");
if (MIDI != NULL)
@ -357,16 +362,31 @@ bool MIDIStreamer::InitPlayback()
source->CheckCaps(MIDI->GetTechnology());
if (!MIDI->CanHandleSysex()) source->SkipSysex();
auto streamInfo = MIDI->GetStreamInfo();
if (streamInfo.mBufferSize > 0)
{
Stream = GSnd->CreateStream(FillStream, streamInfo.mBufferSize, streamInfo.mNumChannels == 1 ? SoundStream::Float | SoundStream::Mono : SoundStream::Float, streamInfo.mSampleRate, MIDI);
}
if (MIDI->Preprocess(this, m_Looping))
{
StartPlayback();
if (MIDI == NULL)
if (MIDI == nullptr)
{ // The MIDI file had no content and has been automatically closed.
if (Stream)
{
delete Stream;
Stream = nullptr;
}
return false;
}
}
if (0 != MIDI->Resume())
int res = 1;
if (Stream) res = Stream->Play(true, 1);
if (res) res = MIDI->Resume();
if (res)
{
Printf ("Starting MIDI playback failed\n");
Stop();
@ -454,6 +474,10 @@ void MIDIStreamer::Pause()
{
OutputVolume(0);
}
if (Stream != nullptr)
{
Stream->SetPaused(true);
}
}
}
@ -474,6 +498,10 @@ void MIDIStreamer::Resume()
{
OutputVolume(Volume);
}
if (Stream != nullptr)
{
Stream->SetPaused(false);
}
m_Status = STATE_Playing;
}
}
@ -497,11 +525,18 @@ void MIDIStreamer::Stop()
MIDI->UnprepareHeader(&Buffer[1]);
MIDI->Close();
}
if (MIDI != NULL)
if (MIDI != nullptr)
{
delete MIDI;
MIDI = NULL;
MIDI = nullptr;
}
if (Stream != nullptr)
{
Stream->Stop();
delete Stream;
Stream = nullptr;
}
m_Status = STATE_Stopped;
}
@ -912,4 +947,14 @@ bool MIDIStreamer::SetSubsong(int subsong)
return false;
}
//==========================================================================
//
// SoftSynthMIDIDevice :: FillStream static
//
//==========================================================================
bool MIDIStreamer::FillStream(SoundStream* stream, void* buff, int len, void* userdata)
{
SoftSynthMIDIDevice* device = (SoftSynthMIDIDevice*)userdata;
return device->ServiceStream(buff, len);
}