From fc6eba0c268110c762c85374bd2f9af5b702238a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 28 Sep 2019 16:50:00 +0200 Subject: [PATCH] - more dependency removal, this time from the MIDI devices. --- libraries/oplsynth/opl_mus_player.cpp | 1 - libraries/oplsynth/oplsynth/genmidi.h | 1 + libraries/timidity/timidity.cpp | 39 ++- libraries/timidity/timidity/instrum.h | 2 +- libraries/timidity/timidity/timidity_file.h | 9 +- src/sound/backend/i_soundinternal.h | 16 - src/sound/mididevices/midi_cvars.cpp | 3 + src/sound/mididevices/midiconfig.h | 129 ++++++++ src/sound/{music => mididevices}/mididefs.h | 0 src/sound/mididevices/mididevice.h | 181 +++++++++++ src/sound/{music => mididevices}/mus2midi.h | 0 .../mididevices/music_adlmidi_mididevice.cpp | 2 +- .../mididevices/music_base_mididevice.cpp | 22 +- .../music_fluidsynth_mididevice.cpp | 13 +- .../mididevices/music_opl_mididevice.cpp | 10 +- .../mididevices/music_opnmidi_mididevice.cpp | 5 +- .../music_softsynth_mididevice.cpp | 7 +- .../mididevices/music_timidity_mididevice.cpp | 13 +- .../music_timiditypp_mididevice.cpp | 9 +- .../music_wavewriter_mididevice.cpp | 92 +++--- .../mididevices/music_wildmidi_mididevice.cpp | 34 +-- .../mididevices/music_win_mididevice.cpp | 55 ++-- src/sound/midisources/midisource.h | 4 +- src/sound/music/i_music.cpp | 18 +- src/sound/music/i_music.h | 2 +- src/sound/music/i_musicinterns.h | 283 +----------------- src/sound/musicformats/music_midistream.cpp | 30 +- src/sound/s_advsound.cpp | 3 + src/sound/s_music.h | 5 - 29 files changed, 509 insertions(+), 479 deletions(-) create mode 100644 src/sound/mididevices/midiconfig.h rename src/sound/{music => mididevices}/mididefs.h (100%) create mode 100644 src/sound/mididevices/mididevice.h rename src/sound/{music => mididevices}/mus2midi.h (100%) diff --git a/libraries/oplsynth/opl_mus_player.cpp b/libraries/oplsynth/opl_mus_player.cpp index ebb39f382..fe771fdd9 100644 --- a/libraries/oplsynth/opl_mus_player.cpp +++ b/libraries/oplsynth/opl_mus_player.cpp @@ -269,7 +269,6 @@ bool OPLmusicBlock::ServiceStream (void *buff, int numbytes) io->chips[i]->Update(samples1, samplesleft); } OffsetSamples(samples1, samplesleft << stereoshift); - assert(NextTickIn == ticky); NextTickIn -= samplesleft; assert (NextTickIn >= 0); numsamples -= samplesleft; diff --git a/libraries/oplsynth/oplsynth/genmidi.h b/libraries/oplsynth/oplsynth/genmidi.h index 82fbe3e51..6d8cd68a9 100644 --- a/libraries/oplsynth/oplsynth/genmidi.h +++ b/libraries/oplsynth/oplsynth/genmidi.h @@ -1,3 +1,4 @@ +#pragma once #include #if defined(__GNUC__) diff --git a/libraries/timidity/timidity.cpp b/libraries/timidity/timidity.cpp index cfc6f331b..7224cf609 100644 --- a/libraries/timidity/timidity.cpp +++ b/libraries/timidity/timidity.cpp @@ -587,13 +587,40 @@ int Instruments::read_config_file(const char *name) return 0; } -// When loading DMXGUS the sfreader's default file must be the DMXGUS file, not the config as for patch sets. +static char* gets(const char *&input, const char *eof, char* strbuf, size_t len) +{ + if (ptrdiff_t(len) > eof - input) len = eof - input; + if (len <= 0) return nullptr; -int Instruments::LoadDMXGUS(int gus_memsize) + char* p = strbuf; + while (len > 1) + { + if (*input == 0) + { + input++; + break; + } + if (*input != '\r') + { + *p++ = *input; + len--; + if (*input == '\n') + { + input++; + break; + } + } + input++; + } + if (p == strbuf) return nullptr; + *p++ = 0; + return strbuf; +} + +int Instruments::LoadDMXGUS(int gus_memsize, const char* dmxgusdata, size_t dmxgussize) { char readbuffer[1024]; - auto data = sfreader->open_timidity_file(nullptr); - long size = (data->seek(0, SEEK_END), data->tell()); + const char* eof = dmxgusdata + dmxgussize; long read = 0; uint8_t remap[256]; @@ -604,9 +631,7 @@ int Instruments::LoadDMXGUS(int gus_memsize) int status = -1; int gusbank = (gus_memsize >= 1 && gus_memsize <= 4) ? gus_memsize : -1; - data->seek(0, SEEK_SET); - - while (data->gets(readbuffer, 1024) && read < size) + while (gets(dmxgusdata, eof, readbuffer, 1024)) { int i = 0; while (readbuffer[i] != 0 && i < 1024) diff --git a/libraries/timidity/timidity/instrum.h b/libraries/timidity/timidity/instrum.h index cc14dba1e..c8c806abc 100644 --- a/libraries/timidity/timidity/instrum.h +++ b/libraries/timidity/timidity/instrum.h @@ -175,7 +175,7 @@ public: int LoadConfig() { return read_config_file(nullptr); } int read_config_file(const char* name); - int LoadDMXGUS(int gus_memsize); + int LoadDMXGUS(int gus_memsize, const char *dmxgusdata, size_t dmxgussize); void font_freeall(); FontFile* font_find(const char* filename); diff --git a/libraries/timidity/timidity/timidity_file.h b/libraries/timidity/timidity/timidity_file.h index 67eeb17d8..f9b111631 100644 --- a/libraries/timidity/timidity/timidity_file.h +++ b/libraries/timidity/timidity/timidity_file.h @@ -48,7 +48,7 @@ public: // A minimalistic sound font reader interface. Normally this should be replaced with something better tied into the host application. -#ifdef USE_BASE_INTERFACE +#if 1//def USE_BASE_INTERFACE // Base version of timidity_file using stdio's FILE. struct timidity_file_FILE : public timidity_file @@ -89,7 +89,7 @@ struct timidity_file_FILE : public timidity_file class BaseSoundFontReader : public SoundFontReaderInterface { std::vector paths; - + bool IsAbsPath(const char *name) { if (name[0] == '/' || name[0] == '\\') return true; @@ -100,7 +100,8 @@ class BaseSoundFontReader : public SoundFontReaderInterface return 0; } - struct timidity_file* open_timidityplus_file(const char* fn) +public: + struct timidity_file* open_timidity_file(const char* fn) { FILE *f = nullptr; std::string fullname; @@ -129,7 +130,7 @@ class BaseSoundFontReader : public SoundFontReaderInterface return tf; } - void timidityplus_add_path(const char* path) + void timidity_add_path(const char* path) { std::string p = path; if (p.back() != '/' && p.back() != '\\') p += '/'; // always let it end with a slash. diff --git a/src/sound/backend/i_soundinternal.h b/src/sound/backend/i_soundinternal.h index 18259c1be..fe043f12d 100644 --- a/src/sound/backend/i_soundinternal.h +++ b/src/sound/backend/i_soundinternal.h @@ -153,22 +153,6 @@ private: SoundDecoder& operator=(const SoundDecoder &rhs); }; -enum EMidiDevice -{ - MDEV_DEFAULT = -1, - MDEV_MMAPI = 0, - MDEV_OPL = 1, - MDEV_SNDSYS = 2, - MDEV_TIMIDITY = 3, - MDEV_FLUIDSYNTH = 4, - MDEV_GUS = 5, - MDEV_WILDMIDI = 6, - MDEV_ADL = 7, - MDEV_OPN = 8, - - MDEV_COUNT -}; - class MusInfo; struct MusPlayingInfo { diff --git a/src/sound/mididevices/midi_cvars.cpp b/src/sound/mididevices/midi_cvars.cpp index a26fa74af..15a3bdcc6 100644 --- a/src/sound/mididevices/midi_cvars.cpp +++ b/src/sound/mididevices/midi_cvars.cpp @@ -915,3 +915,6 @@ bool WildMidi_SetupConfig(WildMidiConfig* config, const char* args) return true; } +// This one is for Win32 MMAPI. + +CVAR(Bool, snd_midiprecache, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); diff --git a/src/sound/mididevices/midiconfig.h b/src/sound/mididevices/midiconfig.h new file mode 100644 index 000000000..2398da0a9 --- /dev/null +++ b/src/sound/mididevices/midiconfig.h @@ -0,0 +1,129 @@ +#pragma once + +#include +#include +#include + +struct ADLConfig +{ + int adl_chips_count = 6; + int adl_emulator_id = 0; + int adl_bank = 14; + int adl_volume_model = 3; // DMX + bool adl_run_at_pcm_rate = 0; + bool adl_fullpan = 1; + std::string adl_custom_bank; +}; + +struct FluidConfig +{ + std::string fluid_lib; + std::vector fluid_patchset; + bool fluid_reverb = false; + bool fluid_chorus = false; + int fluid_voices = 128; + int fluid_interp = 1; + int fluid_samplerate = 0; + int fluid_threads = 1; + int fluid_chorus_voices = 3; + int fluid_chorus_type = 0; + float fluid_gain = 0.5f; + float fluid_reverb_roomsize = 0.61f; + float fluid_reverb_damping = 0.23f; + float fluid_reverb_width = 0.76f; + float fluid_reverb_level = 0.57f; + float fluid_chorus_level = 1.2f; + float fluid_chorus_speed = 0.3f; + float fluid_chorus_depth = 8; +}; + +#include "oplsynth/genmidi.h" + +struct OPLMidiConfig +{ + int numchips = 2; + int core = 0; + bool fullpan = true; + struct GenMidiInstrument OPLinstruments[GENMIDI_NUM_TOTAL]; +}; + +struct OpnConfig +{ + int opn_chips_count = 8; + int opn_emulator_id = 0; + bool opn_run_at_pcm_rate = false; + bool opn_fullpan = 1; + std::string opn_custom_bank; + std::vector default_bank; +}; + +namespace Timidity +{ + class Instruments; + class SoundFontReaderInterface; +} + +struct GUSConfig +{ + // This one is a bit more complex because it also implements the instrument cache. + int midi_voices = 32; + int gus_memsize = 0; + void (*errorfunc)(int type, int verbosity_level, const char* fmt, ...) = nullptr; + + Timidity::SoundFontReaderInterface *reader; + std::string readerName; + std::vector dmxgus; // can contain the contents of a DMXGUS lump that may be used as the instrument set. In this case gus_patchdir must point to the location of the GUS data. + std::string gus_patchdir; + + // These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time. + // Thus, this config should always be stored globally to avoid this. + // If the last loaded instrument set is to be reused or the caller wants to manage them itself, both 'reader' and 'dmxgus' fields should be left empty. + std::string loadedConfig; + std::shared_ptr instruments; // this is held both by the config and the device +}; + +namespace TimidityPlus +{ + class Instruments; + class SoundFontReaderInterface; +} + +struct TimidityConfig +{ + void (*errorfunc)(int type, int verbosity_level, const char* fmt, ...) = nullptr; + + TimidityPlus::SoundFontReaderInterface* reader; + std::string readerName; + + // These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time. + // Thus, this config should always be stored globally to avoid this. + // If the last loaded instrument set is to be reused or the caller wants to manage them itself, 'reader' should be left empty. + std::string loadedConfig; + std::shared_ptr instruments; // this is held both by the config and the device + +}; + +namespace WildMidi +{ + struct Instruments; + class SoundFontReaderInterface; +} + +struct WildMidiConfig +{ + bool reverb = false; + bool enhanced_resampling = true; + void (*errorfunc)(const char* wmfmt, va_list args) = nullptr; + + WildMidi::SoundFontReaderInterface* reader; + std::string readerName; + + // These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time. + // Thus, this config should always be stored globally to avoid this. + // If the last loaded instrument set is to be reused or the caller wants to manage them itself, 'reader' should be left empty. + std::string loadedConfig; + std::shared_ptr instruments; // this is held both by the config and the device + +}; + + diff --git a/src/sound/music/mididefs.h b/src/sound/mididevices/mididefs.h similarity index 100% rename from src/sound/music/mididefs.h rename to src/sound/mididevices/mididefs.h diff --git a/src/sound/mididevices/mididevice.h b/src/sound/mididevices/mididevice.h new file mode 100644 index 000000000..4923f755e --- /dev/null +++ b/src/sound/mididevices/mididevice.h @@ -0,0 +1,181 @@ +#pragma once + +#include +#include "midiconfig.h" +#include "mididefs.h" + +void TimidityPP_Shutdown(); +typedef void(*MidiCallback)(void *); + +// A device that provides a WinMM-like MIDI streaming interface ------------- + +enum EMidiDevice +{ + MDEV_DEFAULT = -1, + MDEV_MMAPI = 0, + MDEV_OPL = 1, + MDEV_SNDSYS = 2, + MDEV_TIMIDITY = 3, + MDEV_FLUIDSYNTH = 4, + MDEV_GUS = 5, + MDEV_WILDMIDI = 6, + MDEV_ADL = 7, + MDEV_OPN = 8, + + MDEV_COUNT +}; + +struct MidiHeader +{ + uint8_t *lpData; + uint32_t dwBufferLength; + uint32_t dwBytesRecorded; + MidiHeader *lpNext; +}; + +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 MIDIDevice +{ +public: + MIDIDevice() = default; + virtual ~MIDIDevice(); + + 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; + virtual int SetTempo(int tempo) = 0; + virtual int SetTimeDiv(int timediv) = 0; + virtual int StreamOut(MidiHeader *data) = 0; + virtual int StreamOutSync(MidiHeader *data) = 0; + virtual int Resume() = 0; + virtual void Stop() = 0; + virtual int PrepareHeader(MidiHeader *data); + virtual int UnprepareHeader(MidiHeader *data); + virtual bool FakeVolume(); + virtual bool Pause(bool paused) = 0; + virtual void InitPlayback(); + virtual bool Update(); + virtual void PrecacheInstruments(const uint16_t *instruments, int count); + virtual void ChangeSettingInt(const char *setting, int value); + virtual void ChangeSettingNum(const char *setting, double value); + virtual void ChangeSettingString(const char *setting, const char *value); + virtual std::string GetStats(); + virtual int GetDeviceType() const { return MDEV_DEFAULT; } + virtual bool CanHandleSysex() const { return true; } + virtual SoundStreamInfo GetStreamInfo() const; + +protected: + MidiCallback Callback; + void* CallbackData; +}; + +// Base class for software synthesizer MIDI output devices ------------------ + +class SoftSynthMIDIDevice : public MIDIDevice +{ + friend class MIDIWaveWriter; +public: + SoftSynthMIDIDevice(int samplerate, int minrate = 1, int maxrate = 1000000 /* something higher than any valid value */); + ~SoftSynthMIDIDevice(); + + void Close(); + bool IsOpen() const; + int GetTechnology() const; + int SetTempo(int tempo); + int SetTimeDiv(int timediv); + int StreamOut(MidiHeader *data); + int StreamOutSync(MidiHeader *data); + int Resume(); + 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; + 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; + int StreamBlockSize = 2; + + virtual void CalcTickRate(); + int PlayTick(); + + 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; +}; + + +// Internal disk writing version of a MIDI device ------------------ + +class MIDIWaveWriter : public SoftSynthMIDIDevice +{ +public: + MIDIWaveWriter(const char *filename, SoftSynthMIDIDevice *devtouse); + //~MIDIWaveWriter(); + bool CloseFile(); + int Resume() override; + int Open() override + { + return playDevice->Open(); + } + 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) 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: + FILE *File; + SoftSynthMIDIDevice *playDevice; +}; + + +// MIDI devices + +MIDIDevice *CreateFluidSynthMIDIDevice(int samplerate, const FluidConfig* config, int (*printfunc)(const char*, ...)); +MIDIDevice *CreateADLMIDIDevice(const ADLConfig* config); +MIDIDevice *CreateOPNMIDIDevice(const OpnConfig *args); +MIDIDevice *CreateOplMIDIDevice(const OPLMidiConfig* config); +MIDIDevice *CreateTimidityMIDIDevice(GUSConfig *config, int samplerate); +MIDIDevice *CreateTimidityPPMIDIDevice(TimidityConfig *config, int samplerate); +MIDIDevice *CreateWildMIDIDevice(WildMidiConfig *config, int samplerate); + +#ifdef _WIN32 +MIDIDevice* CreateWinMIDIDevice(int mididevice, bool precache); +#endif + diff --git a/src/sound/music/mus2midi.h b/src/sound/mididevices/mus2midi.h similarity index 100% rename from src/sound/music/mus2midi.h rename to src/sound/mididevices/mus2midi.h diff --git a/src/sound/mididevices/music_adlmidi_mididevice.cpp b/src/sound/mididevices/music_adlmidi_mididevice.cpp index 2d7b93f44..a04ff224d 100644 --- a/src/sound/mididevices/music_adlmidi_mididevice.cpp +++ b/src/sound/mididevices/music_adlmidi_mididevice.cpp @@ -34,7 +34,7 @@ // HEADER FILES ------------------------------------------------------------ -#include "i_musicinterns.h" +#include "mididevice.h" #include "adlmidi.h" class ADLMIDIDevice : public SoftSynthMIDIDevice diff --git a/src/sound/mididevices/music_base_mididevice.cpp b/src/sound/mididevices/music_base_mididevice.cpp index d3c58eeaa..fa6f9dff0 100644 --- a/src/sound/mididevices/music_base_mididevice.cpp +++ b/src/sound/mididevices/music_base_mididevice.cpp @@ -35,10 +35,7 @@ // HEADER FILES ------------------------------------------------------------ -#include "i_musicinterns.h" -#include "templates.h" -#include "doomerrors.h" -#include "v_text.h" +#include "mididevice.h" //========================================================================== @@ -71,21 +68,6 @@ void MIDIDevice::PrecacheInstruments(const uint16_t *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, bool looping) -{ - return true; -} - //========================================================================== // // MIDIDevice :: PrepareHeader @@ -183,7 +165,7 @@ void MIDIDevice::ChangeSettingString(const char *setting, const char *value) // //========================================================================== -FString MIDIDevice::GetStats() +std::string MIDIDevice::GetStats() { return "This MIDI device does not have any stats."; } diff --git a/src/sound/mididevices/music_fluidsynth_mididevice.cpp b/src/sound/mididevices/music_fluidsynth_mididevice.cpp index b78cd10e4..cc491d319 100644 --- a/src/sound/mididevices/music_fluidsynth_mididevice.cpp +++ b/src/sound/mididevices/music_fluidsynth_mididevice.cpp @@ -34,7 +34,10 @@ // HEADER FILES ------------------------------------------------------------ -#include "i_musicinterns.h" +#include +#include +#include "mididevice.h" +#include "mus2midi.h" // FluidSynth implementation of a MIDI device ------------------------------- @@ -55,7 +58,7 @@ public: ~FluidSynthMIDIDevice(); int OpenRenderer(); - FString GetStats(); + std::string GetStats() override; void ChangeSettingInt(const char *setting, int value); void ChangeSettingNum(const char *setting, double value); void ChangeSettingString(const char *setting, const char *value); @@ -504,13 +507,12 @@ void FluidSynthMIDIDevice::ChangeSettingString(const char *setting, const char * // //========================================================================== -FString FluidSynthMIDIDevice::GetStats() +std::string FluidSynthMIDIDevice::GetStats() { if (FluidSynth == NULL || FluidSettings == NULL) { return "FluidSynth is invalid"; } - FString out; std::lock_guard lock(CritSec); int polyphony = fluid_synth_get_polyphony(FluidSynth); @@ -521,7 +523,8 @@ FString FluidSynthMIDIDevice::GetStats() fluid_settings_getint(FluidSettings, "synth.reverb.active", &reverb); fluid_settings_getint(FluidSettings, "synth.polyphony", &maxpoly); - out.Format("Voices: %3d/%3d(%3d) %6.2f%% CPU Reverb: %3s Chorus: %3s", + char out[100]; + snprintf(out, 100,"Voices: %3d/%3d(%3d) %6.2f%% CPU Reverb: %3s Chorus: %3s", voices, polyphony, maxpoly, load, reverb ? "yes" : "no", chorus ? "yes" : "no"); return out; } diff --git a/src/sound/mididevices/music_opl_mididevice.cpp b/src/sound/mididevices/music_opl_mididevice.cpp index 467ebe4c2..bc6bdad65 100644 --- a/src/sound/mididevices/music_opl_mididevice.cpp +++ b/src/sound/mididevices/music_opl_mididevice.cpp @@ -35,8 +35,10 @@ // HEADER FILES ------------------------------------------------------------ -#include "i_musicinterns.h" +#include "mididevice.h" +#include "mus2midi.h" #include "oplsynth/opl.h" +#include "oplsynth/opl_mus_player.h" // MACROS ------------------------------------------------------------------ @@ -59,7 +61,7 @@ public: int OpenRenderer(); void Close(); int GetTechnology() const; - FString GetStats(); + std::string GetStats() override; protected: void CalcTickRate(); @@ -285,9 +287,9 @@ bool OPLMIDIDevice::ServiceStream(void *buff, int numbytes) // //========================================================================== -FString OPLMIDIDevice::GetStats() +std::string OPLMIDIDevice::GetStats() { - FString out; + std::string out; for (uint32_t i = 0; i < io->NumChannels; ++i) { char star = '*'; diff --git a/src/sound/mididevices/music_opnmidi_mididevice.cpp b/src/sound/mididevices/music_opnmidi_mididevice.cpp index 4c6e33319..bff838478 100644 --- a/src/sound/mididevices/music_opnmidi_mididevice.cpp +++ b/src/sound/mididevices/music_opnmidi_mididevice.cpp @@ -34,11 +34,8 @@ // HEADER FILES ------------------------------------------------------------ -#include "i_musicinterns.h" -#include "w_wad.h" -#include "doomerrors.h" +#include "mididevice.h" #include "opnmidi.h" -#include "i_soundfont.h" class OPNMIDIDevice : public SoftSynthMIDIDevice { diff --git a/src/sound/mididevices/music_softsynth_mididevice.cpp b/src/sound/mididevices/music_softsynth_mididevice.cpp index 3c6dd75f9..cc178ae34 100644 --- a/src/sound/mididevices/music_softsynth_mididevice.cpp +++ b/src/sound/mididevices/music_softsynth_mididevice.cpp @@ -34,9 +34,8 @@ // HEADER FILES ------------------------------------------------------------ -#include "i_musicinterns.h" -#include "templates.h" -#include "i_system.h" +#include +#include "mididevice.h" // MACROS ------------------------------------------------------------------ @@ -379,7 +378,7 @@ bool SoftSynthMIDIDevice::ServiceStream (void *buff, int numbytes) { double ticky = NextTickIn; int tick_in = int(NextTickIn); - int samplesleft = MIN(numsamples, tick_in); + int samplesleft = std::min(numsamples, tick_in); if (samplesleft > 0) { diff --git a/src/sound/mididevices/music_timidity_mididevice.cpp b/src/sound/mididevices/music_timidity_mididevice.cpp index 13b811a26..56321262b 100644 --- a/src/sound/mididevices/music_timidity_mididevice.cpp +++ b/src/sound/mididevices/music_timidity_mididevice.cpp @@ -34,13 +34,12 @@ // HEADER FILES ------------------------------------------------------------ -#include "i_musicinterns.h" -#include "v_text.h" +#include "mididevice.h" #include "timidity/timidity.h" #include "timidity/playmidi.h" #include "timidity/instrum.h" -#include "i_soundfont.h" -#include "doomerrors.h" +#define USE_BASE_INTERFACE +#include "timidity/timidity_file.h" // MACROS ------------------------------------------------------------------ @@ -97,9 +96,7 @@ void TimidityMIDIDevice::LoadInstruments(GUSConfig *config) FString ultradir = getenv("ULTRADIR"); if (ultradir.IsNotEmpty() || config->gus_patchdir.length() != 0) { - FileReader fr; - fr.OpenMemory((const char *)config->dmxgus.data(), (long)config->dmxgus.size()); - auto psreader = new FPatchSetReader(fr); + auto psreader = new Timidity::BaseSoundFontReader; // The GUS put its patches in %ULTRADIR%/MIDI so we can try that if (ultradir.IsNotEmpty()) @@ -111,7 +108,7 @@ void TimidityMIDIDevice::LoadInstruments(GUSConfig *config) if (config->gus_patchdir.length() != 0) psreader->timidity_add_path(config->gus_patchdir.c_str()); config->instruments.reset(new Timidity::Instruments(psreader)); - bool success = config->instruments->LoadDMXGUS(config->gus_memsize) >= 0; + bool success = config->instruments->LoadDMXGUS(config->gus_memsize, (const char*)config->dmxgus.data(), config->dmxgus.size()) >= 0; config->dmxgus.clear(); diff --git a/src/sound/mididevices/music_timiditypp_mididevice.cpp b/src/sound/mididevices/music_timiditypp_mididevice.cpp index 02a1de4fb..cc2a81538 100644 --- a/src/sound/mididevices/music_timiditypp_mididevice.cpp +++ b/src/sound/mididevices/music_timiditypp_mididevice.cpp @@ -32,12 +32,7 @@ ** */ -#include - -#include "i_musicinterns.h" -#include "v_text.h" -#include "doomerrors.h" -#include "i_soundfont.h" +#include "mididevice.h" #include "timiditypp/timidity.h" #include "timiditypp/instrum.h" @@ -55,7 +50,7 @@ public: int OpenRenderer(); void PrecacheInstruments(const uint16_t *instruments, int count); - //FString GetStats(); + //std::string GetStats(); int GetDeviceType() const override { return MDEV_TIMIDITY; } double test[3] = { 0, 0, 0 }; diff --git a/src/sound/mididevices/music_wavewriter_mididevice.cpp b/src/sound/mididevices/music_wavewriter_mididevice.cpp index 2fd230363..1c360c5b3 100644 --- a/src/sound/mididevices/music_wavewriter_mididevice.cpp +++ b/src/sound/mididevices/music_wavewriter_mididevice.cpp @@ -35,7 +35,8 @@ // HEADER FILES ------------------------------------------------------------ -#include "i_musicinterns.h" +#include "mididevice.h" +#include "m_swap.h" #include // MACROS ------------------------------------------------------------------ @@ -44,7 +45,7 @@ struct FmtChunk { - uint32_t ChunkID; + //uint32_t ChunkID; uint32_t ChunkLen; uint16_t FormatTag; uint16_t Channels; @@ -74,32 +75,48 @@ struct FmtChunk // PUBLIC DATA DEFINITIONS ------------------------------------------------- // CODE -------------------------------------------------------------------- +#ifdef _WIN32 + // I'd rather not include Windows.h for just this. +extern "C" __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned CodePage, unsigned long dwFlags, const char* lpMultiByteStr, int cbMultiByte, const wchar_t* lpWideCharStr, int cchWideChar); +#endif + +static FILE* my_fopen(const char* filename) +{ +#ifndef _WIN32 + return fopen(filename, "wb"); +#else + // This is supposed to handle UTF-8 which Windows does not support with fopen. :( + const int CP_UTF8 = 65001; + + std::wstring filePathW; + auto len = strlen(filename); + filePathW.resize(len); + int newSize = MultiByteToWideChar(CP_UTF8, 0, filename, (int)len, const_cast(filePathW.c_str()), (int)len); + filePathW.resize(newSize); + return _wfopen(filePathW.c_str(), L"wb"); +#endif + +} //========================================================================== // -// TimidityWaveWriterMIDIDevice Constructor +// MIDIWaveWriter Constructor // //========================================================================== MIDIWaveWriter::MIDIWaveWriter(const char *filename, SoftSynthMIDIDevice *playdevice) : SoftSynthMIDIDevice(playdevice->GetSampleRate()) { - File = FileWriter::Open(filename); + File = my_fopen(filename); playDevice = playdevice; if (File != nullptr) { // Write wave header - uint32_t work[3]; FmtChunk fmt; - work[0] = MAKE_ID('R','I','F','F'); - work[1] = 0; // filled in later - work[2] = MAKE_ID('W','A','V','E'); - if (4*3 != File->Write(work, 4 * 3)) goto fail; - + if (fwrite("RIFF\0\0\0\0WAVEfmt ", 1, 16, File) != 16) goto fail; playDevice->CalcTickRate(); - fmt.ChunkID = MAKE_ID('f','m','t',' '); - fmt.ChunkLen = LittleLong(uint32_t(sizeof(fmt) - 8)); + fmt.ChunkLen = LittleLong(uint32_t(sizeof(fmt) - 4)); fmt.FormatTag = LittleShort((uint16_t)0xFFFE); // WAVE_FORMAT_EXTENSIBLE fmt.Channels = LittleShort((uint16_t)2); fmt.SamplesPerSec = LittleLong(SampleRate); @@ -120,58 +137,60 @@ MIDIWaveWriter::MIDIWaveWriter(const char *filename, SoftSynthMIDIDevice *playde fmt.SubFormatD[5] = 0x38; fmt.SubFormatD[6] = 0x9b; fmt.SubFormatD[7] = 0x71; - if (sizeof(fmt) != File->Write(&fmt, sizeof(fmt))) goto fail; + if (sizeof(fmt) != fwrite(&fmt, 1, sizeof(fmt), File)) goto fail; - work[0] = MAKE_ID('d','a','t','a'); - work[1] = 0; // filled in later - if (8 !=File->Write(work, 8)) goto fail; + if (fwrite("data\0\0\0\0", 1, 8, File) != 8) goto fail; return; -fail: - Printf("Failed to write %s: %s\n", filename, strerror(errno)); - delete File; + fail: + char buffer[80]; + fclose(File); File = nullptr; + snprintf(buffer, 80, "Failed to write %s: %s\n", filename, strerror(errno)); + throw std::runtime_error(buffer); } } //========================================================================== // -// TimidityWaveWriterMIDIDevice Destructor +// MIDIWaveWriter Destructor // //========================================================================== -MIDIWaveWriter::~MIDIWaveWriter() +bool MIDIWaveWriter::CloseFile() { if (File != nullptr) { - long pos = File->Tell(); + auto pos = ftell(File); uint32_t size; // data chunk size size = LittleLong(uint32_t(pos - 8)); - if (0 == File->Seek(4, SEEK_SET)) + if (0 == fseek(File, 4, SEEK_SET)) { - if (4 == File->Write(&size, 4)) + if (4 == fwrite(&size, 1, 4, File)) { size = LittleLong(uint32_t(pos - 12 - sizeof(FmtChunk) - 8)); - if (0 == File->Seek(4 + sizeof(FmtChunk) + 4, SEEK_CUR)) + if (0 == fseek(File, 4 + sizeof(FmtChunk) + 4, SEEK_CUR)) { - if (4 == File->Write(&size, 4)) + if (4 == fwrite(&size, 1, 5, File)) { - delete File; - return; + fclose(File); + File = nullptr; + return true; } } } } - Printf("Could not finish writing wave file: %s\n", strerror(errno)); - delete File; + fclose(File); + File = nullptr; + return false; } } //========================================================================== // -// TimidityWaveWriterMIDIDevice :: Resume +// MIDIWaveWriter :: Resume // //========================================================================== @@ -181,10 +200,13 @@ int MIDIWaveWriter::Resume() while (ServiceStream(writebuffer, sizeof(writebuffer))) { - if (File->Write(writebuffer, sizeof(writebuffer)) != sizeof(writebuffer)) + if (fwrite(writebuffer, 1, sizeof(writebuffer), File) != sizeof(writebuffer)) { - Printf("Could not write entire wave file: %s\n", strerror(errno)); - return 1; + fclose(File); + File = nullptr; + char buffer[80]; + snprintf(buffer, 80, "Could not write entire wave file: %s\n", strerror(errno)); + throw std::runtime_error(buffer); } } return 0; @@ -192,7 +214,7 @@ int MIDIWaveWriter::Resume() //========================================================================== // -// TimidityWaveWriterMIDIDevice Stop +// MIDIWaveWriter Stop // //========================================================================== diff --git a/src/sound/mididevices/music_wildmidi_mididevice.cpp b/src/sound/mididevices/music_wildmidi_mididevice.cpp index 9cb65b4f4..2cce68f70 100644 --- a/src/sound/mididevices/music_wildmidi_mididevice.cpp +++ b/src/sound/mididevices/music_wildmidi_mididevice.cpp @@ -34,12 +34,8 @@ // HEADER FILES ------------------------------------------------------------ -#include "i_musicinterns.h" -#include "doomerrors.h" -#include "i_soundfont.h" - -#include "c_console.h" -#include "v_text.h" +#include "mididevice.h" +#include "wildmidi/wildmidi_lib.h" // MACROS ------------------------------------------------------------------ @@ -55,7 +51,7 @@ public: int OpenRenderer(); void PrecacheInstruments(const uint16_t *instruments, int count); - FString GetStats(); + std::string GetStats(); int GetDeviceType() const override { return MDEV_WILDMIDI; } protected: @@ -71,22 +67,6 @@ protected: }; - - -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- - -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -// EXTERNAL DATA DECLARATIONS ---------------------------------------------- - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - - - // CODE -------------------------------------------------------------------- //========================================================================== @@ -228,16 +208,16 @@ void WildMIDIDevice::ComputeOutput(float *buffer, int len) // //========================================================================== -FString WildMIDIDevice::GetStats() +std::string WildMIDIDevice::GetStats() { - FString out; - out.Format("%3d voices", Renderer->GetVoiceCount()); + char out[20]; + snprintf(out, 20, "%3d voices", Renderer->GetVoiceCount()); return out; } //========================================================================== // -// WildMIDIDevice :: GetStats +// WildMIDIDevice :: ChangeSettingInt // //========================================================================== diff --git a/src/sound/mididevices/music_win_mididevice.cpp b/src/sound/mididevices/music_win_mididevice.cpp index 612ca76f4..6ef9bfc81 100644 --- a/src/sound/mididevices/music_win_mididevice.cpp +++ b/src/sound/mididevices/music_win_mididevice.cpp @@ -37,14 +37,15 @@ #define WIN32_LEAN_AND_MEAN #include #include +#include +#include +#include // HEADER FILES ------------------------------------------------------------ -#include "i_musicinterns.h" -#include "templates.h" -#include "doomdef.h" +#include "mididevice.h" #include "m_swap.h" -#include "doomerrors.h" +#include "mus2midi.h" #ifndef __GNUC__ #include @@ -68,7 +69,7 @@ static bool IgnoreMIDIVolume(UINT id); class WinMIDIDevice : public MIDIDevice { public: - WinMIDIDevice(int dev_id); + WinMIDIDevice(int dev_id, bool precache); ~WinMIDIDevice(); int Open(); void Close(); @@ -104,6 +105,7 @@ public: MIDIHDR WinMidiHeaders[2]; int HeaderIndex; bool VolumeWorks; + bool Precache; HANDLE BufferDoneEvent; HANDLE ExitEvent; @@ -114,8 +116,6 @@ public: // PUBLIC DATA DEFINITIONS ------------------------------------------------- -CVAR (Bool, snd_midiprecache, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); - // CODE -------------------------------------------------------------------- //========================================================================== @@ -124,22 +124,25 @@ CVAR (Bool, snd_midiprecache, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); // //========================================================================== -WinMIDIDevice::WinMIDIDevice(int dev_id) +WinMIDIDevice::WinMIDIDevice(int dev_id, bool precache) { - DeviceID = MAX(dev_id, 0); + DeviceID = std::max(dev_id, 0); MidiOut = 0; HeaderIndex = 0; + Precache = precache; memset(WinMidiHeaders, 0, sizeof(WinMidiHeaders)); BufferDoneEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); if (BufferDoneEvent == nullptr) { - Printf(PRINT_BOLD, "Could not create buffer done event for MIDI playback\n"); + throw std::runtime_error("Could not create buffer done event for MIDI playback"); } ExitEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); if (ExitEvent == nullptr) { - Printf(PRINT_BOLD, "Could not create exit event for MIDI playback\n"); + CloseHandle(BufferDoneEvent); + BufferDoneEvent = nullptr; + throw std::runtime_error("Could not create exit event for MIDI playback"); } PlayerThread = nullptr; } @@ -297,9 +300,8 @@ int WinMIDIDevice::Resume() PlayerThread = CreateThread(nullptr, 0, PlayerProc, this, 0, &tid); if (PlayerThread == nullptr) { - Printf("Creating MIDI thread failed\n"); Stop(); - return MMSYSERR_NOTSUPPORTED; + throw std::runtime_error("Creating MIDI thread failed\n"); } } return ret; @@ -400,7 +402,7 @@ void WinMIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count) { // Setting snd_midiprecache to false disables this precaching, since it // does involve sleeping for more than a miniscule amount of time. - if (!snd_midiprecache) + if (!Precache) { return; } @@ -606,20 +608,21 @@ bool WinMIDIDevice::Update() GetExitCodeThread(PlayerThread, &code); CloseHandle(PlayerThread); PlayerThread = nullptr; - Printf("MIDI playback failure: "); - if (code < countof(MMErrorCodes)) + char errmsg[100]; + const char *m = "MIDI playback failure: "; + if (code < 8) { - Printf("%s\n", MMErrorCodes[code]); + snprintf(errmsg, 100, "%s%s", m, MMErrorCodes[code]); } - else if (code >= MIDIERR_BASE && code < MIDIERR_BASE + countof(MidiErrorCodes)) + else if (code >= MIDIERR_BASE && code < MIDIERR_BASE + 8) { - Printf("%s\n", MidiErrorCodes[code - MIDIERR_BASE]); + snprintf(errmsg, 100, "%s%s", m, MMErrorCodes[code - MIDIERR_BASE]); } else { - Printf("%08x\n", code); + snprintf(errmsg, 100, "%s%08x", m, code); } - return false; + throw std::runtime_error(errmsg); } return true; } @@ -683,15 +686,9 @@ static bool IgnoreMIDIVolume(UINT id) return false; } -MIDIDevice *CreateWinMIDIDevice(int mididevice) +MIDIDevice *CreateWinMIDIDevice(int mididevice, bool precache) { - auto d = new WinMIDIDevice(mididevice); - if (d->BufferDoneEvent == nullptr || d->ExitEvent == nullptr) - { - delete d; - I_Error("failed to create MIDI events"); - } - return d; + return new WinMIDIDevice(mididevice, precache); } #endif diff --git a/src/sound/midisources/midisource.h b/src/sound/midisources/midisource.h index e8626572a..41b394204 100644 --- a/src/sound/midisources/midisource.h +++ b/src/sound/midisources/midisource.h @@ -11,8 +11,8 @@ #include #include #include -#include "mus2midi.h" -#include "mididefs.h" +#include "mididevices/mus2midi.h" +#include "mididevices/mididefs.h" extern char MIDI_EventLengths[7]; extern char MIDI_CommonLengths[15]; diff --git a/src/sound/music/i_music.cpp b/src/sound/music/i_music.cpp index 71defd16c..a34333123 100644 --- a/src/sound/music/i_music.cpp +++ b/src/sound/music/i_music.cpp @@ -718,6 +718,8 @@ UNSAFE_CCMD (writewave) else if (!stricmp(argv[5], "Timidity") || !stricmp(argv[5], "Timidity++")) dev = MDEV_TIMIDITY; else if (!stricmp(argv[5], "FluidSynth")) dev = MDEV_FLUIDSYNTH; else if (!stricmp(argv[5], "OPL")) dev = MDEV_OPL; + else if (!stricmp(argv[5], "OPN")) dev = MDEV_OPN; + else if (!stricmp(argv[5], "ADL")) dev = MDEV_ADL; else { Printf("%s: Unknown MIDI device\n", argv[5]); @@ -728,12 +730,18 @@ UNSAFE_CCMD (writewave) auto savedsong = mus_playing; S_StopMusic(true); if (dev == MDEV_DEFAULT && snd_mididevice >= 0) dev = MDEV_FLUIDSYNTH; // The Windows system synth cannot dump a wave. - auto streamer = new MIDIStreamer(dev, argv.argc() < 6 ? nullptr : argv[6]); - streamer->SetMIDISource(source); - streamer->DumpWave(argv[2], argv.argc() <4? 0: (int)strtol(argv[3], nullptr, 10), argv.argc() <5 ? 0 : (int)strtol(argv[4], nullptr, 10)); - delete streamer; - S_ChangeMusic(savedsong.name, savedsong.baseorder, savedsong.loop, true); + try + { + MIDIStreamer streamer(dev, argv.argc() < 6 ? nullptr : argv[6]); + streamer.SetMIDISource(source); + streamer.DumpWave(argv[2], argv.argc() < 4 ? 0 : (int)strtol(argv[3], nullptr, 10), argv.argc() < 5 ? 0 : (int)strtol(argv[4], nullptr, 10)); + } + catch (const std::runtime_error& err) + { + Printf("MIDI dump failed: %s\n", err.what()); + } + S_ChangeMusic(savedsong.name, savedsong.baseorder, savedsong.loop, true); } else { diff --git a/src/sound/music/i_music.h b/src/sound/music/i_music.h index c78af7ec9..00898adba 100644 --- a/src/sound/music/i_music.h +++ b/src/sound/music/i_music.h @@ -75,7 +75,7 @@ public: virtual bool SetPosition (unsigned int ms); virtual bool SetSubsong (int subsong); virtual void Update(); - virtual int GetDeviceType() const { return MDEV_DEFAULT; } // MDEV_DEFAULT stands in for anything that cannot change playback parameters which needs a restart. + virtual int GetDeviceType() const { return -1; } // MDEV_DEFAULT stands in for anything that cannot change playback parameters which needs a restart. virtual FString GetStats(); virtual MusInfo *GetOPLDumper(const char *filename); virtual MusInfo *GetWaveDumper(const char *filename, int rate); diff --git a/src/sound/music/i_musicinterns.h b/src/sound/music/i_musicinterns.h index 5252771f7..369261843 100644 --- a/src/sound/music/i_musicinterns.h +++ b/src/sound/music/i_musicinterns.h @@ -3,13 +3,14 @@ #include #include "oplsynth/opl_mus_player.h" #include "c_cvars.h" -#include "mus2midi.h" +#include "mididevices/mus2midi.h" #include "i_sound.h" #include "i_music.h" #include "s_sound.h" #include "files.h" #include "wildmidi/wildmidi_lib.h" #include "midisources/midisource.h" +#include "mididevices/mididevice.h" void I_InitMusicWin32 (); @@ -17,283 +18,17 @@ extern float relative_volume; class MIDISource; -// A device that provides a WinMM-like MIDI streaming interface ------------- - -struct MidiHeader -{ - uint8_t *lpData; - uint32_t dwBufferLength; - uint32_t dwBytesRecorded; - MidiHeader *lpNext; -}; - -struct ADLConfig -{ - int adl_chips_count = 6; - int adl_emulator_id = 0; - int adl_bank = 14; - int adl_volume_model = 3; // DMX - bool adl_run_at_pcm_rate = 0; - bool adl_fullpan = 1; - std::string adl_custom_bank; -}; - extern ADLConfig adlConfig; - -struct FluidConfig -{ - std::string fluid_lib; - std::vector fluid_patchset; - bool fluid_reverb = false; - bool fluid_chorus = false; - int fluid_voices = 128; - int fluid_interp = 1; - int fluid_samplerate = 0; - int fluid_threads = 1; - int fluid_chorus_voices = 3; - int fluid_chorus_type = 0; - float fluid_gain = 0.5f; - float fluid_reverb_roomsize = 0.61f; - float fluid_reverb_damping = 0.23f; - float fluid_reverb_width = 0.76f; - float fluid_reverb_level = 0.57f; - float fluid_chorus_level = 1.2f; - float fluid_chorus_speed = 0.3f; - float fluid_chorus_depth = 8; -}; - extern FluidConfig fluidConfig; - -struct OPLMidiConfig -{ - int numchips = 2; - int core = 0; - bool fullpan = true; - struct GenMidiInstrument OPLinstruments[GENMIDI_NUM_TOTAL]; -}; - extern OPLMidiConfig oplMidiConfig; - -struct OpnConfig -{ - int opn_chips_count = 8; - int opn_emulator_id = 0; - bool opn_run_at_pcm_rate = false; - bool opn_fullpan = 1; - std::string opn_custom_bank; - std::vector default_bank; -}; - extern OpnConfig opnConfig; - -namespace Timidity -{ - class Instruments; - class SoundFontReaderInterface; -} - -struct GUSConfig -{ - // This one is a bit more complex because it also implements the instrument cache. - int midi_voices = 32; - int gus_memsize = 0; - void (*errorfunc)(int type, int verbosity_level, const char* fmt, ...) = nullptr; - - Timidity::SoundFontReaderInterface *reader; - std::string readerName; - std::vector dmxgus; // can contain the contents of a DMXGUS lump that may be used as the instrument set. In this case gus_patchdir must point to the location of the GUS data. - std::string gus_patchdir; - - // These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time. - // Thus, this config should always be stored globally to avoid this. - // If the last loaded instrument set is to be reused or the caller wants to manage them itself, both 'reader' and 'dmxgus' fields should be left empty. - std::string loadedConfig; - std::shared_ptr instruments; // this is held both by the config and the device -}; - extern GUSConfig gusConfig; - -namespace TimidityPlus -{ - class Instruments; - class SoundFontReaderInterface; -} - -struct TimidityConfig -{ - void (*errorfunc)(int type, int verbosity_level, const char* fmt, ...) = nullptr; - - TimidityPlus::SoundFontReaderInterface* reader; - std::string readerName; - - // These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time. - // Thus, this config should always be stored globally to avoid this. - // If the last loaded instrument set is to be reused or the caller wants to manage them itself, 'reader' should be left empty. - std::string loadedConfig; - std::shared_ptr instruments; // this is held both by the config and the device - -}; - extern TimidityConfig timidityConfig; - -struct WildMidiConfig -{ - bool reverb = false; - bool enhanced_resampling = true; - void (*errorfunc)(const char* wmfmt, va_list args) = nullptr; - - WildMidi::SoundFontReaderInterface* reader; - std::string readerName; - - // These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time. - // Thus, this config should always be stored globally to avoid this. - // If the last loaded instrument set is to be reused or the caller wants to manage them itself, 'reader' should be left empty. - std::string loadedConfig; - std::shared_ptr instruments; // this is held both by the config and the device - -}; - 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 *); -class MIDIDevice -{ -public: - MIDIDevice() = default; - virtual ~MIDIDevice(); - - 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; - virtual int SetTempo(int tempo) = 0; - virtual int SetTimeDiv(int timediv) = 0; - virtual int StreamOut(MidiHeader *data) = 0; - virtual int StreamOutSync(MidiHeader *data) = 0; - virtual int Resume() = 0; - virtual void Stop() = 0; - virtual int PrepareHeader(MidiHeader *data); - virtual int UnprepareHeader(MidiHeader *data); - virtual bool FakeVolume(); - virtual bool Pause(bool paused) = 0; - virtual void InitPlayback(); - virtual bool Update(); - virtual void PrecacheInstruments(const uint16_t *instruments, int count); - virtual void ChangeSettingInt(const char *setting, int value); - virtual void ChangeSettingNum(const char *setting, double value); - virtual void ChangeSettingString(const char *setting, const char *value); - virtual bool Preprocess(MIDIStreamer *song, bool looping); - 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; -}; - - - -void TimidityPP_Shutdown(); - - -// Base class for software synthesizer MIDI output devices ------------------ - -class SoftSynthMIDIDevice : public MIDIDevice -{ - friend class MIDIWaveWriter; -public: - SoftSynthMIDIDevice(int samplerate, int minrate = 1, int maxrate = 1000000 /* something higher than any valid value */); - ~SoftSynthMIDIDevice(); - - void Close(); - bool IsOpen() const; - int GetTechnology() const; - int SetTempo(int tempo); - int SetTimeDiv(int timediv); - int StreamOut(MidiHeader *data); - int StreamOutSync(MidiHeader *data); - int Resume(); - 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; - 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; - int StreamBlockSize = 2; - - virtual void CalcTickRate(); - int PlayTick(); - - 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; -}; - - -// Internal disk writing version of a MIDI device ------------------ - -class MIDIWaveWriter : public SoftSynthMIDIDevice -{ -public: - MIDIWaveWriter(const char *filename, SoftSynthMIDIDevice *devtouse); - ~MIDIWaveWriter(); - int Resume() override; - int Open() override - { - return playDevice->Open(); - } - 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) 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; - SoftSynthMIDIDevice *playDevice; -}; // Base class for streaming MUS and MIDI files ------------------------------ @@ -448,20 +183,6 @@ public: CDDAFile (FileReader &reader); }; -// MIDI devices - -MIDIDevice *CreateFluidSynthMIDIDevice(int samplerate, const FluidConfig* config, int (*printfunc)(const char*, ...)); -MIDIDevice *CreateADLMIDIDevice(const ADLConfig* config); -MIDIDevice *CreateOPNMIDIDevice(const OpnConfig *args); -MIDIDevice *CreateOplMIDIDevice(const OPLMidiConfig* config); -MIDIDevice *CreateTimidityMIDIDevice(GUSConfig *config, int samplerate); -MIDIDevice *CreateTimidityPPMIDIDevice(TimidityConfig *config, int samplerate); -MIDIDevice *CreateWildMIDIDevice(WildMidiConfig *config, int samplerate); - -#ifdef _WIN32 -MIDIDevice* CreateWinMIDIDevice(int mididevice); -#endif - // Data interface void Fluid_SetupConfig(FluidConfig *config, const char* patches, bool systemfallback); diff --git a/src/sound/musicformats/music_midistream.cpp b/src/sound/musicformats/music_midistream.cpp index 93f8c12a7..f2494b9b2 100644 --- a/src/sound/musicformats/music_midistream.cpp +++ b/src/sound/musicformats/music_midistream.cpp @@ -55,7 +55,7 @@ static void WriteVarLen (TArray &file, uint32_t value); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- - +EXTERN_CVAR(Bool, snd_midiprecache); EXTERN_CVAR(Float, snd_musicvolume) EXTERN_CVAR(Int, snd_mididevice) @@ -207,7 +207,7 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate) case MDEV_MMAPI: #ifdef _WIN32 - dev = CreateWinMIDIDevice(mididevice); + dev = CreateWinMIDIDevice(mididevice, snd_midiprecache); break; #endif // Intentional fall-through for non-Windows systems. @@ -316,8 +316,16 @@ bool MIDIStreamer::DumpWave(const char *filename, int subsong, int samplerate) throw std::runtime_error("MMAPI device is not supported"); } auto iMIDI = CreateMIDIDevice(devtype, samplerate); - MIDI.reset(new MIDIWaveWriter(filename, static_cast(iMIDI))); - return InitPlayback(); + auto writer = new MIDIWaveWriter(filename, static_cast(iMIDI)); + MIDI.reset(writer); + bool res = InitPlayback(); + if (!writer->CloseFile()) + { + char buffer[80]; + snprintf(buffer, 80, "Could not finish writing wave file: %s\n", strerror(errno)); + throw std::runtime_error(buffer); + } + return res; } //========================================================================== @@ -349,13 +357,10 @@ bool MIDIStreamer::InitPlayback() Stream.reset(GSnd->CreateStream(FillStream, streamInfo.mBufferSize, streamInfo.mNumChannels == 1 ? SoundStream::Float | SoundStream::Mono : SoundStream::Float, streamInfo.mSampleRate, MIDI.get())); } - if (MIDI->Preprocess(this, m_Looping)) - { - StartPlayback(); - if (MIDI == nullptr) - { // The MIDI file had no content and has been automatically closed. - return false; - } + StartPlayback(); + if (MIDI == nullptr) + { // The MIDI file had no content and has been automatically closed. + return false; } int res = 1; @@ -893,7 +898,8 @@ FString MIDIStreamer::GetStats() { return "No MIDI device in use."; } - return MIDI->GetStats(); + auto s = MIDI->GetStats(); + return s.c_str(); } //========================================================================== diff --git a/src/sound/s_advsound.cpp b/src/sound/s_advsound.cpp index 83715eef3..c58515f1a 100644 --- a/src/sound/s_advsound.cpp +++ b/src/sound/s_advsound.cpp @@ -50,6 +50,7 @@ #include "i_system.h" #include "atterm.h" #include "s_music.h" +#include "mididevices/mididevice.h" // MACROS ------------------------------------------------------------------ @@ -1462,6 +1463,8 @@ static void S_AddSNDINFO (int lump) else if (sc.Compare("fluidsynth")) devset.device = MDEV_FLUIDSYNTH; else if (sc.Compare("gus")) devset.device = MDEV_GUS; else if (sc.Compare("wildmidi")) devset.device = MDEV_WILDMIDI; + else if (sc.Compare("adl")) devset.device = MDEV_ADL; + else if (sc.Compare("opn")) devset.device = MDEV_OPN; else sc.ScriptError("Unknown MIDI device %s\n", sc.String); if (sc.CheckString(",")) diff --git a/src/sound/s_music.h b/src/sound/s_music.h index 3f10eb2b8..35ebc4c3e 100644 --- a/src/sound/s_music.h +++ b/src/sound/s_music.h @@ -72,11 +72,6 @@ struct MidiDeviceSetting { int device; FString args; - - MidiDeviceSetting() - { - device = MDEV_DEFAULT; - } }; typedef TMap MusicAliasMap;