From b3b870d67ee093820cde78f643c2ccccf5667efa Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 27 Sep 2019 01:51:05 +0200 Subject: [PATCH 1/7] - Gave OPN device the same treatment Also made some improvements to the interface. --- src/sound/mididevices/midi_cvars.cpp | 120 ++++++++++++++++-- .../mididevices/music_adlmidi_mididevice.cpp | 17 +-- .../music_fluidsynth_mididevice.cpp | 8 +- .../mididevices/music_opl_mididevice.cpp | 6 +- .../mididevices/music_opnmidi_mididevice.cpp | 84 +++--------- src/sound/music/i_musicinterns.h | 26 +++- src/sound/musicformats/music_midistream.cpp | 9 +- 7 files changed, 172 insertions(+), 98 deletions(-) diff --git a/src/sound/mididevices/midi_cvars.cpp b/src/sound/mididevices/midi_cvars.cpp index 99a9de6eaf..7120df3cf4 100644 --- a/src/sound/mididevices/midi_cvars.cpp +++ b/src/sound/mididevices/midi_cvars.cpp @@ -40,7 +40,9 @@ #include "doomerrors.h" // do this without including windows.h for this one single prototype +#ifdef _WIN32 extern "C" unsigned __stdcall GetSystemDirectoryA(char* lpBuffer, unsigned uSize); +#endif static void CheckRestart(int devtype) { @@ -53,7 +55,7 @@ static void CheckRestart(int devtype) ADLConfig adlConfig; FluidConfig fluidConfig; OPLMidiConfig oplMidiConfig; - +OpnConfig opnConfig; CUSTOM_CVAR(Int, adl_chips_count, 6, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { @@ -87,20 +89,38 @@ CUSTOM_CVAR(Int, adl_bank, 14, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CUSTOM_CVAR(Bool, adl_use_custom_bank, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { - adlConfig.adl_use_custom_bank = self; CheckRestart(MDEV_ADL); } CUSTOM_CVAR(String, adl_custom_bank, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { - CheckRestart(MDEV_ADL); - - //Resolve the path here, so that the renderer does not have to do the work itself and only needs to process final names. - auto info = sfmanager.FindSoundFont(self, SF_WOPL); - if (info == nullptr) adlConfig.adl_custom_bank = nullptr; - else adlConfig.adl_custom_bank = info->mFilename; + if (adl_use_custom_bank) CheckRestart(MDEV_ADL); } +void SetAdlCustomBank(const char *Args) +{ + //Resolve the path here, so that the renderer does not have to do the work itself and only needs to process final names. + const char *bank = Args && *Args? Args : adl_use_custom_bank? *adl_custom_bank : nullptr; + adlConfig.adl_bank = adl_bank; + if (bank && *bank) + { + auto info = sfmanager.FindSoundFont(bank, SF_WOPL); + if (info == nullptr) + { + if (*bank >= '0' && *bank <= '9') + { + adlConfig.adl_bank = (int)strtoll(bank, nullptr, 10); + } + adlConfig.adl_custom_bank = nullptr; + } + else + { + adlConfig.adl_custom_bank = info->mFilename; + } + } +} + + CUSTOM_CVAR(Int, adl_volume_model, ADLMIDI_VolumeModel_DMX, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { adlConfig.adl_volume_model = self; @@ -469,3 +489,87 @@ int getOPLCore(const char* args) if (args != NULL && *args >= '0' && *args < '4') current_opl_core = *args - '0'; return current_opl_core; } + + + + + +CUSTOM_CVAR(Int, opn_chips_count, 8, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + opnConfig.opn_chips_count = self; + if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) + { + MIDIDeviceChanged(-1, true); + } +} + +CUSTOM_CVAR(Int, opn_emulator_id, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + opnConfig.opn_emulator_id = self; + if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) + { + MIDIDeviceChanged(-1, true); + } +} + +CUSTOM_CVAR(Bool, opn_run_at_pcm_rate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + opnConfig.opn_run_at_pcm_rate = self; + if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) + { + MIDIDeviceChanged(-1, true); + } +} + +CUSTOM_CVAR(Bool, opn_fullpan, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + opnConfig.opn_fullpan = self; + if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) + { + MIDIDeviceChanged(-1, true); + } +} + +CUSTOM_CVAR(Bool, opn_use_custom_bank, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) + { + MIDIDeviceChanged(-1, true); + } +} + +CUSTOM_CVAR(String, opn_custom_bank, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (opn_use_custom_bank && currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) + { + MIDIDeviceChanged(-1, true); + } +} + +void SetOpnCustomBank(const char *Args) +{ + //Resolve the path here, so that the renderer does not have to do the work itself and only needs to process final names. + const char *bank = Args && *Args? Args : opn_use_custom_bank? *opn_custom_bank : nullptr; + if (bank && *bank) + { + auto info = sfmanager.FindSoundFont(bank, SF_WOPN); + if (info == nullptr) + { + opnConfig.opn_custom_bank = ""; + } + else + { + opnConfig.opn_custom_bank = info->mFilename; + } + } + + int lump = Wads.CheckNumForFullName("xg.wopn"); + if (lump < 0) + { + opnConfig.default_bank.resize(0); + return; + } + FMemLump data = Wads.ReadLump(lump); + opnConfig.default_bank.resize(data.GetSize()); + memcpy(opnConfig.default_bank.data(), data.GetMem(), data.GetSize()); +} diff --git a/src/sound/mididevices/music_adlmidi_mididevice.cpp b/src/sound/mididevices/music_adlmidi_mididevice.cpp index 01fe9a3732..27090446a7 100644 --- a/src/sound/mididevices/music_adlmidi_mididevice.cpp +++ b/src/sound/mididevices/music_adlmidi_mididevice.cpp @@ -41,7 +41,7 @@ class ADLMIDIDevice : public SoftSynthMIDIDevice { struct ADL_MIDIPlayer *Renderer; public: - ADLMIDIDevice(const char *args, const ADLConfig *config); + ADLMIDIDevice(const ADLConfig *config); ~ADLMIDIDevice(); int Open(MidiCallback, void *userdata); @@ -54,7 +54,7 @@ protected: void ComputeOutput(float *buffer, int len); private: - int LoadCustomBank(const char *bankfile, const ADLConfig *config); + int LoadCustomBank(const ADLConfig *config); }; @@ -75,7 +75,7 @@ enum // //========================================================================== -ADLMIDIDevice::ADLMIDIDevice(const char *args, const ADLConfig *config) +ADLMIDIDevice::ADLMIDIDevice(const ADLConfig *config) :SoftSynthMIDIDevice(44100) { Renderer = adl_init(44100); // todo: make it configurable @@ -83,7 +83,7 @@ ADLMIDIDevice::ADLMIDIDevice(const char *args, const ADLConfig *config) { adl_switchEmulator(Renderer, config->adl_emulator_id); adl_setRunAtPcmRate(Renderer, config->adl_run_at_pcm_rate); - if (!LoadCustomBank(config->adl_custom_bank, config)) + if (!LoadCustomBank(config)) adl_setBank(Renderer, config->adl_bank); adl_setNumChips(Renderer, config->adl_chips_count); adl_setVolumeRangeModel(Renderer, config->adl_volume_model); @@ -116,9 +116,10 @@ ADLMIDIDevice::~ADLMIDIDevice() // //========================================================================== -int ADLMIDIDevice::LoadCustomBank(const char *bankfile, const ADLConfig *config) +int ADLMIDIDevice::LoadCustomBank(const ADLConfig *config) { - if(!config->adl_use_custom_bank || !bankfile || !*bankfile) + const char *bankfile = config->adl_custom_bank.c_str(); + if(!*bankfile) return 0; return (adl_openBankFile(Renderer, bankfile) == 0); } @@ -225,9 +226,9 @@ void ADLMIDIDevice::ComputeOutput(float *buffer, int len) // //========================================================================== -MIDIDevice *CreateADLMIDIDevice(const char *args, const ADLConfig *config) +MIDIDevice *CreateADLMIDIDevice(const ADLConfig *config) { - return new ADLMIDIDevice(args, config); + return new ADLMIDIDevice(config); } diff --git a/src/sound/mididevices/music_fluidsynth_mididevice.cpp b/src/sound/mididevices/music_fluidsynth_mididevice.cpp index 69493ee6e4..f0c1c04496 100644 --- a/src/sound/mididevices/music_fluidsynth_mididevice.cpp +++ b/src/sound/mididevices/music_fluidsynth_mididevice.cpp @@ -51,7 +51,7 @@ struct fluid_synth_t; class FluidSynthMIDIDevice : public SoftSynthMIDIDevice { public: - FluidSynthMIDIDevice(int samplerate, FluidConfig *config, int (*printfunc_)(const char *, ...)); + FluidSynthMIDIDevice(int samplerate, const FluidConfig *config, int (*printfunc_)(const char *, ...)); ~FluidSynthMIDIDevice(); int Open(MidiCallback, void *userdata); @@ -162,7 +162,7 @@ const char *BaseFileSearch(const char *file, const char *ext, bool lookfirstinpr // //========================================================================== -FluidSynthMIDIDevice::FluidSynthMIDIDevice(int samplerate, FluidConfig *config, int (*printfunc_)(const char*, ...) = nullptr) +FluidSynthMIDIDevice::FluidSynthMIDIDevice(int samplerate, const FluidConfig *config, int (*printfunc_)(const char*, ...) = nullptr) : SoftSynthMIDIDevice(samplerate <= 0? config->fluid_samplerate : samplerate, 22050, 96000) { // These are needed for changing the settings. If something posts a transient config in here we got no way to retrieve the values afterward. @@ -180,7 +180,7 @@ FluidSynthMIDIDevice::FluidSynthMIDIDevice(int samplerate, FluidConfig *config, FluidSynth = NULL; FluidSettings = NULL; #ifdef DYN_FLUIDSYNTH - if (!LoadFluidSynth(config->fluid_lib)) + if (!LoadFluidSynth(config->fluid_lib.c_str())) { throw std::runtime_error("Failed to load FluidSynth.\n"); } @@ -621,7 +621,7 @@ void FluidSynthMIDIDevice::UnloadFluidSynth() // //========================================================================== -MIDIDevice *CreateFluidSynthMIDIDevice(int samplerate, FluidConfig *config, int (*printfunc)(const char*, ...)) +MIDIDevice *CreateFluidSynthMIDIDevice(int samplerate, const FluidConfig *config, int (*printfunc)(const char*, ...)) { return new FluidSynthMIDIDevice(samplerate, config, printfunc); } diff --git a/src/sound/mididevices/music_opl_mididevice.cpp b/src/sound/mididevices/music_opl_mididevice.cpp index 66074dd94c..744581d6bf 100644 --- a/src/sound/mididevices/music_opl_mididevice.cpp +++ b/src/sound/mididevices/music_opl_mididevice.cpp @@ -55,7 +55,7 @@ class OPLMIDIDevice : public SoftSynthMIDIDevice, protected OPLmusicBlock { public: - OPLMIDIDevice(OPLMidiConfig *config); + OPLMIDIDevice(const OPLMidiConfig *config); int Open(MidiCallback, void *userdata); void Close(); int GetTechnology() const; @@ -82,7 +82,7 @@ protected: // //========================================================================== -OPLMIDIDevice::OPLMIDIDevice(OPLMidiConfig *config) +OPLMIDIDevice::OPLMIDIDevice(const OPLMidiConfig *config) : SoftSynthMIDIDevice((int)OPL_SAMPLE_RATE), OPLmusicBlock(config->core, config->numchips) { FullPan = config->fullpan; @@ -314,7 +314,7 @@ FString OPLMIDIDevice::GetStats() return out; } -MIDIDevice* CreateOplMIDIDevice(OPLMidiConfig* config) +MIDIDevice* CreateOplMIDIDevice(const OPLMidiConfig* config) { return new OPLMIDIDevice(config); } diff --git a/src/sound/mididevices/music_opnmidi_mididevice.cpp b/src/sound/mididevices/music_opnmidi_mididevice.cpp index 1a08d6c722..1ca9c04354 100644 --- a/src/sound/mididevices/music_opnmidi_mididevice.cpp +++ b/src/sound/mididevices/music_opnmidi_mididevice.cpp @@ -44,7 +44,7 @@ class OPNMIDIDevice : public SoftSynthMIDIDevice { struct OPN2_MIDIPlayer *Renderer; public: - OPNMIDIDevice(const char *args); + OPNMIDIDevice(const OpnConfig *config); ~OPNMIDIDevice(); @@ -72,53 +72,6 @@ enum ME_PITCHWHEEL = 0xE0 }; -CUSTOM_CVAR(Int, opn_chips_count, 8, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) - { - MIDIDeviceChanged(-1, true); - } -} - -CUSTOM_CVAR(Int, opn_emulator_id, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) - { - MIDIDeviceChanged(-1, true); - } -} - -CUSTOM_CVAR(Bool, opn_run_at_pcm_rate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) - { - MIDIDeviceChanged(-1, true); - } -} - -CUSTOM_CVAR(Bool, opn_fullpan, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) - { - MIDIDeviceChanged(-1, true); - } -} - -CUSTOM_CVAR(Bool, opn_use_custom_bank, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) - { - MIDIDeviceChanged(-1, true); - } -} - -CUSTOM_CVAR(String, opn_custom_bank, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (opn_use_custom_bank && currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) - { - MIDIDeviceChanged(-1, true); - } -} //========================================================================== // @@ -126,27 +79,30 @@ CUSTOM_CVAR(String, opn_custom_bank, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // //========================================================================== -OPNMIDIDevice::OPNMIDIDevice(const char *args) +OPNMIDIDevice::OPNMIDIDevice(const OpnConfig *config) :SoftSynthMIDIDevice(44100) { Renderer = opn2_init(44100); // todo: make it configurable if (Renderer != nullptr) { - if (!LoadCustomBank(opn_custom_bank)) + if (!LoadCustomBank(config->opn_custom_bank.c_str())) { - int lump = Wads.CheckNumForFullName("xg.wopn"); - if (lump < 0) + if(config->default_bank.size() == 0) { - I_Error("No OPN bank found"); + opn2_close(Renderer); + throw std::runtime_error("No OPN bank found"); } - FMemLump data = Wads.ReadLump(lump); - opn2_openBankData(Renderer, data.GetMem(), (long)data.GetSize()); + opn2_openBankData(Renderer, config->default_bank.data(), (long)config->default_bank.size()); } - opn2_switchEmulator(Renderer, (int)opn_emulator_id); - opn2_setRunAtPcmRate(Renderer, (int)opn_run_at_pcm_rate); - opn2_setNumChips(Renderer, opn_chips_count); - opn2_setSoftPanEnabled(Renderer, (int)opn_fullpan); + opn2_switchEmulator(Renderer, (int)config->opn_emulator_id); + opn2_setRunAtPcmRate(Renderer, (int)config->opn_run_at_pcm_rate); + opn2_setNumChips(Renderer, config->opn_chips_count); + opn2_setSoftPanEnabled(Renderer, (int)config->opn_fullpan); + } + else + { + throw std::runtime_error("Unable to create OPN renderer."); } } @@ -177,12 +133,8 @@ OPNMIDIDevice::~OPNMIDIDevice() int OPNMIDIDevice::LoadCustomBank(const char *bankfile) { - if(!opn_use_custom_bank) + if(!bankfile || !*bankfile) return 0; - auto info = sfmanager.FindSoundFont(bankfile, SF_WOPN); - if(info == nullptr) - return 0; - bankfile = info->mFilename.GetChars(); return (opn2_openBankFile(Renderer, bankfile) == 0); } @@ -283,9 +235,9 @@ void OPNMIDIDevice::ComputeOutput(float *buffer, int len) // //========================================================================== -MIDIDevice *CreateOPNMIDIDevice(const char *args) +MIDIDevice *CreateOPNMIDIDevice(const OpnConfig *config) { - return new OPNMIDIDevice(args); + return new OPNMIDIDevice(config); } diff --git a/src/sound/music/i_musicinterns.h b/src/sound/music/i_musicinterns.h index 2d0beeaf99..ab4ac488d0 100644 --- a/src/sound/music/i_musicinterns.h +++ b/src/sound/music/i_musicinterns.h @@ -35,15 +35,14 @@ struct ADLConfig int adl_volume_model = 3; // DMX bool adl_run_at_pcm_rate = 0; bool adl_fullpan = 1; - bool adl_use_custom_bank = 0; - const char *adl_custom_bank = nullptr; + std::string adl_custom_bank; }; extern ADLConfig adlConfig; struct FluidConfig { - const char* fluid_lib = nullptr; + std::string fluid_lib; std::vector fluid_patchset; bool fluid_reverb = false; bool fluid_chorus = false; @@ -73,6 +72,18 @@ struct OPLMidiConfig 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; +}; + + + extern OPLMidiConfig oplMidiConfig; @@ -351,11 +362,20 @@ 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); + // Data interface int BuildFluidPatchSetList(const char* patches, bool systemfallback); +void SetAdlCustomBank(const char *Args); void LoadGenMidi(); int getOPLCore(const char* args); +void SetOpnCustomBank(const char *Args); // Module played via foo_dumb ----------------------------------------------- diff --git a/src/sound/musicformats/music_midistream.cpp b/src/sound/musicformats/music_midistream.cpp index 75a345e284..4bdc133d60 100644 --- a/src/sound/musicformats/music_midistream.cpp +++ b/src/sound/musicformats/music_midistream.cpp @@ -50,14 +50,9 @@ #ifdef _WIN32 MIDIDevice *CreateWinMIDIDevice(int mididevice); #endif -MIDIDevice* CreateFluidSynthMIDIDevice(int samplerate, FluidConfig* config, int (*printfunc)(const char*, ...)); MIDIDevice *CreateTimidityMIDIDevice(const char *args, int samplerate); MIDIDevice *CreateTimidityPPMIDIDevice(const char *args, int samplerate); -MIDIDevice *CreateADLMIDIDevice(const char *args, const ADLConfig* config); -MIDIDevice *CreateOPNMIDIDevice(const char *args); MIDIDevice *CreateWildMIDIDevice(const char *args, int samplerate); -MIDIDevice* CreateOplMIDIDevice(OPLMidiConfig* config); -int BuildFluidPatchSetList(const char* patches, bool systemfallback); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -213,10 +208,12 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate) break; case MDEV_ADL: - dev = CreateADLMIDIDevice(Args, &adlConfig); + SetAdlCustomBank(Args); + dev = CreateADLMIDIDevice(&adlConfig); break; case MDEV_OPN: + SetOpnCustomBank(Args); dev = CreateOPNMIDIDevice(Args); break; From ed7b73d8cb58960785b7518d55dd1403c6b4c86f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 27 Sep 2019 02:31:27 +0200 Subject: [PATCH 2/7] - work on GUS MIDI device plus some cleanup This is not tested yet! --- src/sound/mididevices/midi_cvars.cpp | 258 ++++++++++++------ .../music_fluidsynth_mididevice.cpp | 4 +- .../mididevices/music_timidity_mididevice.cpp | 189 ++++--------- src/sound/music/i_music.cpp | 1 - src/sound/music/i_musicinterns.h | 45 ++- src/sound/musicformats/music_midistream.cpp | 17 +- src/sound/musicformats/music_opl.cpp | 14 +- 7 files changed, 284 insertions(+), 244 deletions(-) diff --git a/src/sound/mididevices/midi_cvars.cpp b/src/sound/mididevices/midi_cvars.cpp index 7120df3cf4..77570fc3b3 100644 --- a/src/sound/mididevices/midi_cvars.cpp +++ b/src/sound/mididevices/midi_cvars.cpp @@ -38,6 +38,10 @@ #include "adlmidi.h" #include "cmdlib.h" #include "doomerrors.h" +#include "timidity/timidity.h" +#include "timidity/playmidi.h" +#include "timidity/instrum.h" +#include "v_text.h" // do this without including windows.h for this one single prototype #ifdef _WIN32 @@ -56,6 +60,7 @@ ADLConfig adlConfig; FluidConfig fluidConfig; OPLMidiConfig oplMidiConfig; OpnConfig opnConfig; +GUSConfig gusConfig; CUSTOM_CVAR(Int, adl_chips_count, 6, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { @@ -97,11 +102,11 @@ CUSTOM_CVAR(String, adl_custom_bank, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) if (adl_use_custom_bank) CheckRestart(MDEV_ADL); } -void SetAdlCustomBank(const char *Args) +void ADL_SetupConfig(ADLConfig *config, const char *Args) { //Resolve the path here, so that the renderer does not have to do the work itself and only needs to process final names. const char *bank = Args && *Args? Args : adl_use_custom_bank? *adl_custom_bank : nullptr; - adlConfig.adl_bank = adl_bank; + config->adl_bank = adl_bank; if (bank && *bank) { auto info = sfmanager.FindSoundFont(bank, SF_WOPL); @@ -109,13 +114,13 @@ void SetAdlCustomBank(const char *Args) { if (*bank >= '0' && *bank <= '9') { - adlConfig.adl_bank = (int)strtoll(bank, nullptr, 10); + config->adl_bank = (int)strtoll(bank, nullptr, 10); } - adlConfig.adl_custom_bank = nullptr; + config->adl_custom_bank = nullptr; } else { - adlConfig.adl_custom_bank = info->mFilename; + config->adl_custom_bank = info->mFilename; } } } @@ -137,13 +142,13 @@ CUSTOM_CVAR(String, fluid_lib, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) fluidConfig.fluid_lib = self; // only takes effect for next song. } -int BuildFluidPatchSetList(const char* patches, bool systemfallback) +void Fluid_SetupConfig(FluidConfig *config, const char* patches, bool systemfallback) { fluidConfig.fluid_patchset.clear(); //Resolve the paths here, the renderer will only get a final list of file names. auto info = sfmanager.FindSoundFont(patches, SF_SF2); if (info != nullptr) patches = info->mFilename.GetChars(); - + int count; char* wpatches = strdup(patches); char* tok; @@ -152,48 +157,47 @@ int BuildFluidPatchSetList(const char* patches, bool systemfallback) #else const char* const delim = ":"; #endif - - if (wpatches == NULL) + + if (wpatches != NULL) { - return 0; - } - tok = strtok(wpatches, delim); - count = 0; - while (tok != NULL) - { - FString path; + tok = strtok(wpatches, delim); + count = 0; + while (tok != NULL) + { + FString path; #ifdef _WIN32 - // If the path does not contain any path separators, automatically - // prepend $PROGDIR to the path. - if (strcspn(tok, ":/\\") == strlen(tok)) - { - path << "$PROGDIR/" << tok; - path = NicePath(path); - } - else + // If the path does not contain any path separators, automatically + // prepend $PROGDIR to the path. + if (strcspn(tok, ":/\\") == strlen(tok)) + { + path << "$PROGDIR/" << tok; + path = NicePath(path); + } + else #endif - { - path = NicePath(tok); + { + path = NicePath(tok); + } + if (FileExists(path)) + { + config->fluid_patchset.push_back(path.GetChars()); + } + else + { + Printf("Could not find patch set %s.\n", tok); + } + tok = strtok(NULL, delim); } - if (FileExists(path)) - { - fluidConfig.fluid_patchset.push_back(path.GetChars()); - } - else - { - Printf("Could not find patch set %s.\n", tok); - } - tok = strtok(NULL, delim); + free(wpatches); + if (config->fluid_patchset.size() > 0) return; } - free(wpatches); - if (fluidConfig.fluid_patchset.size() > 0) return 1; if (systemfallback) { // The following will only be used if no soundfont at all is provided, i.e. even the standard one coming with GZDoom is missing. #ifdef __unix__ // This is the standard location on Ubuntu. - return BuildFluidPatchSetList("/usr/share/sounds/sf2/FluidR3_GS.sf2:/usr/share/sounds/sf2/FluidR3_GM.sf2", false); + Fluid_SetupConfig(config, "/usr/share/sounds/sf2/FluidR3_GS.sf2:/usr/share/sounds/sf2/FluidR3_GM.sf2", false); #endif #ifdef _WIN32 // On Windows, look for the 4 megabyte patch set installed by Creative's drivers as a default. @@ -204,22 +208,21 @@ int BuildFluidPatchSetList(const char* patches, bool systemfallback) strcat(sysdir, "\\CT4MGM.SF2"); if (FileExists(sysdir)) { - fluidConfig.fluid_patchset.push_back(sysdir); + config->fluid_patchset.push_back(sysdir); return 1; } // Try again with CT2MGM.SF2 sysdir[filepart + 3] = '2'; if (FileExists(sysdir)) { - fluidConfig.fluid_patchset.push_back(sysdir); - return 1; + config->fluid_patchset.push_back(sysdir); + return; } } #endif } - return 0; } CUSTOM_CVAR(String, fluid_patchset, "gzdoom", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) @@ -458,10 +461,7 @@ CUSTOM_CVAR(Int, opl_numchips, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CUSTOM_CVAR(Int, opl_core, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPL) - { - MIDIDeviceChanged(-1, true); - } + CheckRestart(MDEV_OPL); } CUSTOM_CVAR(Bool, opl_fullpan, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) @@ -469,7 +469,7 @@ CUSTOM_CVAR(Bool, opl_fullpan, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) oplMidiConfig.fullpan = self; } -void LoadGenMidi() +void OPL_SetupConfig(OPLMidiConfig *config, const char *args) { // The OPL renderer should not care about where this comes from. // Note: No I_Error here - this needs to be consistent with the rest of the music code. @@ -481,72 +481,54 @@ void LoadGenMidi() data.Read(filehdr, 8); if (memcmp(filehdr, "#OPL_II#", 8)) throw std::runtime_error("Corrupt GENMIDI lump"); data.Read(oplMidiConfig.OPLinstruments, sizeof(GenMidiInstrument) * GENMIDI_NUM_TOTAL); + + config->core = opl_core; + if (args != NULL && *args >= '0' && *args < '4') config->core = *args - '0'; } -int getOPLCore(const char* args) -{ - int current_opl_core = opl_core; - if (args != NULL && *args >= '0' && *args < '4') current_opl_core = *args - '0'; - return current_opl_core; -} - - - - CUSTOM_CVAR(Int, opn_chips_count, 8, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { opnConfig.opn_chips_count = self; - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) - { - MIDIDeviceChanged(-1, true); - } + CheckRestart(MDEV_OPN); } CUSTOM_CVAR(Int, opn_emulator_id, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { opnConfig.opn_emulator_id = self; - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) - { - MIDIDeviceChanged(-1, true); - } + CheckRestart(MDEV_OPN); + } CUSTOM_CVAR(Bool, opn_run_at_pcm_rate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { opnConfig.opn_run_at_pcm_rate = self; - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) - { - MIDIDeviceChanged(-1, true); - } + CheckRestart(MDEV_OPN); + } CUSTOM_CVAR(Bool, opn_fullpan, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { opnConfig.opn_fullpan = self; - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) - { - MIDIDeviceChanged(-1, true); - } + CheckRestart(MDEV_OPN); + } CUSTOM_CVAR(Bool, opn_use_custom_bank, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) - { - MIDIDeviceChanged(-1, true); - } + CheckRestart(MDEV_OPN); + } CUSTOM_CVAR(String, opn_custom_bank, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { - if (opn_use_custom_bank && currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) + if (opn_use_custom_bank) { - MIDIDeviceChanged(-1, true); + CheckRestart(MDEV_OPN); } } -void SetOpnCustomBank(const char *Args) +void OPN_SetupConfig(OpnConfig *config, const char *Args) { //Resolve the path here, so that the renderer does not have to do the work itself and only needs to process final names. const char *bank = Args && *Args? Args : opn_use_custom_bank? *opn_custom_bank : nullptr; @@ -555,21 +537,127 @@ void SetOpnCustomBank(const char *Args) auto info = sfmanager.FindSoundFont(bank, SF_WOPN); if (info == nullptr) { - opnConfig.opn_custom_bank = ""; + config->opn_custom_bank = ""; } else { - opnConfig.opn_custom_bank = info->mFilename; + config->opn_custom_bank = info->mFilename; } } int lump = Wads.CheckNumForFullName("xg.wopn"); if (lump < 0) { - opnConfig.default_bank.resize(0); + config->default_bank.resize(0); return; } FMemLump data = Wads.ReadLump(lump); - opnConfig.default_bank.resize(data.GetSize()); - memcpy(opnConfig.default_bank.data(), data.GetMem(), data.GetSize()); + config->default_bank.resize(data.GetSize()); + memcpy(config->default_bank.data(), data.GetMem(), data.GetSize()); } + + +// CVARS for this device - all of them require a device reset -------------------------------------------- + + + +CUSTOM_CVAR(String, midi_config, "gzdoom", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + CheckRestart(MDEV_GUS); +} + +CUSTOM_CVAR(Bool, midi_dmxgus, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // This was 'true' but since it requires special setup that's not such a good idea. +{ + CheckRestart(MDEV_GUS); +} + +CUSTOM_CVAR(String, gus_patchdir, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + CheckRestart(MDEV_GUS); +} + +CUSTOM_CVAR(Int, midi_voices, 32, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + gusConfig.midi_voices = self; + CheckRestart(MDEV_GUS); +} + +CUSTOM_CVAR(Int, gus_memsize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + gusConfig.gus_memsize = self; + CheckRestart(MDEV_GUS); +} + +//========================================================================== +// +// Error printing override to redirect to the internal console instead of stdout. +// +//========================================================================== + +static void gus_printfunc(int type, int verbosity_level, const char* fmt, ...) +{ + if (verbosity_level >= Timidity::VERB_DEBUG) return; // Don't waste time on diagnostics. + + va_list args; + va_start(args, fmt); + FString msg; + msg.VFormat(fmt, args); + va_end(args); + + switch (type) + { + case Timidity::CMSG_ERROR: + Printf(TEXTCOLOR_RED "%s\n", msg.GetChars()); + break; + + case Timidity::CMSG_WARNING: + Printf(TEXTCOLOR_YELLOW "%s\n", msg.GetChars()); + break; + + case Timidity::CMSG_INFO: + DPrintf(DMSG_SPAMMY, "%s\n", msg.GetChars()); + break; + } +} + +//========================================================================== +// +// Sets up the date to load the instruments for the GUS device. +// The actual instrument loader is part of the device. +// +//========================================================================== + +bool GUS_SetupConfig(GUSConfig *config, const char *args) +{ + config->errorfunc = gus_printfunc; + if ((midi_dmxgus && *args == 0) || !stricmp(args, "DMXGUS")) + { + if (stricmp(config->loadedConfig.c_str(), "DMXGUS") == 0) return false; // aleady loaded + int lump = Wads.CheckNumForName("DMXGUS"); + if (lump == -1) lump = Wads.CheckNumForName("DMXGUSC"); + if (lump >= 0) + { + auto data = Wads.OpenLumpReader(lump); + if (data.GetLength() > 0) + { + config->dmxgus.resize(data.GetLength()); + data.Read(config->dmxgus.data(), data.GetLength()); + return true; + } + } + } + if (*args == 0) args = midi_config; + if (stricmp(config->loadedConfig.c_str(), args) == 0) return false; // aleady loaded + + auto reader = sfmanager.OpenSoundFont(args, SF_GUS | SF_SF2); + if (reader == nullptr) + { + char error[80]; + snprintf(error, 80, "GUS: %s: Unable to load sound font\n",args); + throw std::runtime_error(error); + } + config->reader = reader; + config->readerName = args; + return true; +} + diff --git a/src/sound/mididevices/music_fluidsynth_mididevice.cpp b/src/sound/mididevices/music_fluidsynth_mididevice.cpp index f0c1c04496..5c297cf229 100644 --- a/src/sound/mididevices/music_fluidsynth_mididevice.cpp +++ b/src/sound/mididevices/music_fluidsynth_mididevice.cpp @@ -65,7 +65,7 @@ protected: void HandleEvent(int status, int parm1, int parm2); void HandleLongEvent(const uint8_t *data, int len); void ComputeOutput(float *buffer, int len); - int LoadPatchSets(FluidConfig *config); + int LoadPatchSets(const FluidConfig *config); fluid_settings_t *FluidSettings; fluid_synth_t *FluidSynth; @@ -348,7 +348,7 @@ void FluidSynthMIDIDevice::ComputeOutput(float *buffer, int len) // //========================================================================== -int FluidSynthMIDIDevice::LoadPatchSets(FluidConfig *config) +int FluidSynthMIDIDevice::LoadPatchSets(const FluidConfig *config) { int count = 0; for (auto& file : config->fluid_patchset) diff --git a/src/sound/mididevices/music_timidity_mididevice.cpp b/src/sound/mididevices/music_timidity_mididevice.cpp index c5a9749fa4..f11153319e 100644 --- a/src/sound/mididevices/music_timidity_mididevice.cpp +++ b/src/sound/mididevices/music_timidity_mididevice.cpp @@ -56,72 +56,6 @@ // PRIVATE DATA DEFINITIONS ------------------------------------------------ -// CVARS for this device - all of them require a device reset -------------------------------------------- - -static void CheckRestart() -{ - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_GUS) - { - MIDIDeviceChanged(-1, true); - } -} - -CUSTOM_CVAR(String, midi_config, "gzdoom", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - CheckRestart(); -} - -CUSTOM_CVAR(Bool, midi_dmxgus, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // This was 'true' but since it requires special setup that's not such a good idea. -{ - CheckRestart(); -} - -CUSTOM_CVAR(String, gus_patchdir, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - CheckRestart(); -} - -CUSTOM_CVAR(Int, midi_voices, 32, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - CheckRestart(); -} - -CUSTOM_CVAR(Int, gus_memsize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - CheckRestart(); -} - -//========================================================================== -// -// Error printing override to redirect to the internal console instead of stdout. -// -//========================================================================== - -static void gzdoom_ctl_cmsg(int type, int verbosity_level, const char* fmt, ...) -{ - if (verbosity_level >= Timidity::VERB_DEBUG) return; // Don't waste time on diagnostics. - - va_list args; - va_start(args, fmt); - FString msg; - msg.VFormat(fmt, args); - va_end(args); - - switch (type) - { - case Timidity::CMSG_ERROR: - Printf(TEXTCOLOR_RED "%s\n", msg.GetChars()); - break; - - case Timidity::CMSG_WARNING: - Printf(TEXTCOLOR_YELLOW "%s\n", msg.GetChars()); - break; - - case Timidity::CMSG_INFO: - DPrintf(DMSG_SPAMMY, "%s\n", msg.GetChars()); - break; - } -} //========================================================================== // @@ -133,8 +67,9 @@ namespace Timidity { struct Renderer; } class TimidityMIDIDevice : public SoftSynthMIDIDevice { + void LoadInstruments(GUSConfig *config); public: - TimidityMIDIDevice(const char *args, int samplerate); + TimidityMIDIDevice(GUSConfig *config, int samplerate); ~TimidityMIDIDevice(); int Open(MidiCallback, void *userdata); @@ -147,78 +82,69 @@ protected: void HandleEvent(int status, int parm1, int parm2); void HandleLongEvent(const uint8_t *data, int len); void ComputeOutput(float *buffer, int len); - void LoadConfig(const char *); }; -// DATA DEFINITIONS ------------------------------------------------- - -static FString currentConfig; -static Timidity::Instruments* instruments; - // CODE -------------------------------------------------------------------- -void TimidityMIDIDevice::LoadConfig(const char *config) + +void TimidityMIDIDevice::LoadInstruments(GUSConfig *config) { - if ((midi_dmxgus && *config == 0) || !stricmp(config, "DMXGUS")) + if (config->dmxgus.size()) { - if (currentConfig.CompareNoCase("DMXGUS") == 0) return; // aleady loaded - int lump = Wads.CheckNumForName("DMXGUS"); - if (lump == -1) lump = Wads.CheckNumForName("DMXGUSC"); - if (lump >= 0) + // Check if we got some GUS data before using it. + FString ultradir = getenv("ULTRADIR"); + if (ultradir.IsNotEmpty() || config->gus_patchdir.length() != 0) { - auto data = Wads.OpenLumpReader(lump); - if (data.GetLength() > 0) + FileReader fr; + fr.OpenMemory((const char *)config->dmxgus.data(), (long)config->dmxgus.size()); + auto psreader = new FPatchSetReader(fr); + + // The GUS put its patches in %ULTRADIR%/MIDI so we can try that + if (ultradir.IsNotEmpty()) { - // Check if we got some GUS data before using it. - FString ultradir = getenv("ULTRADIR"); - if (ultradir.IsNotEmpty() || *(*gus_patchdir) != 0) - { - - auto psreader = new FPatchSetReader(data); - - // The GUS put its patches in %ULTRADIR%/MIDI so we can try that - if (ultradir.IsNotEmpty()) - { - ultradir += "/midi"; - psreader->AddPath(ultradir); - } - if (instruments) delete instruments; - instruments = new Timidity::Instruments(psreader); - - // Load DMXGUS lump and patches from gus_patchdir - if (*(*gus_patchdir) != 0) psreader->AddPath(gus_patchdir); - - if (instruments->LoadDMXGUS(gus_memsize) < 0) - { - delete instruments; - instruments = nullptr; - I_Error("Unable to initialize instruments for GUS MIDI device"); - } - currentConfig = "DMXGUS"; - } + ultradir += "/midi"; + psreader->timidity_add_path(ultradir); + } + // Load DMXGUS lump and patches from gus_patchdir + 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; + + delete psreader; + config->dmxgus.clear(); + + if (success) + { + config->loadedConfig = "DMXGUS"; + return; } } + config->loadedConfig = ""; + config->instruments.reset(); + throw std::runtime_error("Unable to initialize DMXGUS for GUS MIDI device"); } - if (*config == 0) config = midi_config; - if (currentConfig.CompareNoCase(config) == 0) return; - if (instruments) delete instruments; - instruments = nullptr; - - auto reader = sfmanager.OpenSoundFont(config, SF_GUS | SF_SF2); - if (reader == nullptr) + else if (config->reader) { - I_Error("GUS: %s: Unable to load sound font\n",config); + config->loadedConfig = config->readerName; + config->instruments.reset(new Timidity::Instruments(config->reader)); + bool err = config->instruments->LoadConfig() < 0; + delete config->reader; + config->reader = nullptr; + + if (err) + { + config->instruments.reset(); + config->loadedConfig = ""; + throw std::runtime_error("Unable to initialize instruments for GUS MIDI device"); + } } - - instruments = new Timidity::Instruments(reader); - if (instruments->LoadConfig() < 0) + else if (config->instruments == nullptr) { - delete instruments; - instruments = nullptr; - I_Error("Unable to initialize instruments for GUS MIDI device"); + throw std::runtime_error("No instruments set for GUS device"); } - currentConfig = config; + } //========================================================================== @@ -227,12 +153,11 @@ void TimidityMIDIDevice::LoadConfig(const char *config) // //========================================================================== -TimidityMIDIDevice::TimidityMIDIDevice(const char *args, int samplerate) +TimidityMIDIDevice::TimidityMIDIDevice(GUSConfig *config, int samplerate) : SoftSynthMIDIDevice(samplerate, 11025, 65535) { - LoadConfig(args); - Timidity::printMessage = gzdoom_ctl_cmsg; - Renderer = new Timidity::Renderer((float)SampleRate, midi_voices, instruments); + LoadInstruments(config); + Renderer = new Timidity::Renderer((float)SampleRate, config->midi_voices, config->instruments.get()); } //========================================================================== @@ -328,13 +253,7 @@ void TimidityMIDIDevice::ComputeOutput(float *buffer, int len) // //========================================================================== -MIDIDevice *CreateTimidityMIDIDevice(const char *args, int samplerate) +MIDIDevice *CreateTimidityMIDIDevice(GUSConfig *config, int samplerate) { - return new TimidityMIDIDevice(args, samplerate); -} - -void Timidity_Shutdown() -{ - if (instruments) delete instruments; - instruments = nullptr; + return new TimidityMIDIDevice(config, samplerate); } diff --git a/src/sound/music/i_music.cpp b/src/sound/music/i_music.cpp index 1c74f1cb0a..f01724db1c 100644 --- a/src/sound/music/i_music.cpp +++ b/src/sound/music/i_music.cpp @@ -160,7 +160,6 @@ void I_ShutdownMusic(bool onexit) } if (onexit) { - Timidity_Shutdown(); WildMidi_Shutdown(); TimidityPP_Shutdown(); dumb_exit(); diff --git a/src/sound/music/i_musicinterns.h b/src/sound/music/i_musicinterns.h index ab4ac488d0..682928a1e1 100644 --- a/src/sound/music/i_musicinterns.h +++ b/src/sound/music/i_musicinterns.h @@ -72,6 +72,8 @@ struct OPLMidiConfig struct GenMidiInstrument OPLinstruments[GENMIDI_NUM_TOTAL]; }; +extern OPLMidiConfig oplMidiConfig; + struct OpnConfig { int opn_chips_count = 8; @@ -82,10 +84,34 @@ struct OpnConfig std::vector default_bank; }; +extern OpnConfig opnConfig; +namespace Timidity +{ + class Instruments; + class SoundFontReaderInterface; +} -extern OPLMidiConfig oplMidiConfig; +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; class MIDIStreamer; @@ -125,7 +151,6 @@ public: -void Timidity_Shutdown(); void TimidityPP_Shutdown(); void WildMidi_Shutdown (); @@ -324,7 +349,7 @@ public: void Play (bool looping, int subsong); bool IsPlaying (); bool IsValid () const; - void ResetChips (); + void ChangeSettingInt (const char *, int) override; protected: @@ -368,14 +393,18 @@ MIDIDevice *CreateFluidSynthMIDIDevice(int samplerate, const FluidConfig* config MIDIDevice *CreateADLMIDIDevice(const ADLConfig* config); MIDIDevice *CreateOPNMIDIDevice(const OpnConfig *args); MIDIDevice *CreateOplMIDIDevice(const OPLMidiConfig* config); +MIDIDevice *CreateTimidityMIDIDevice(GUSConfig *config, int samplerate); + +MIDIDevice *CreateTimidityPPMIDIDevice(const char *args, int samplerate); +MIDIDevice *CreateWildMIDIDevice(const char *args, int samplerate); // Data interface -int BuildFluidPatchSetList(const char* patches, bool systemfallback); -void SetAdlCustomBank(const char *Args); -void LoadGenMidi(); -int getOPLCore(const char* args); -void SetOpnCustomBank(const char *Args); +void Fluid_SetupConfig(FluidConfig *config, const char* patches, bool systemfallback); +void ADL_SetupConfig(ADLConfig *config, const char *Args); +void OPL_SetupConfig(OPLMidiConfig *config, const char *args); +void OPN_SetupConfig(OpnConfig *config, const char *Args); +bool GUS_SetupConfig(GUSConfig *config, const char *args); // Module played via foo_dumb ----------------------------------------------- diff --git a/src/sound/musicformats/music_midistream.cpp b/src/sound/musicformats/music_midistream.cpp index 4bdc133d60..88b281bfdd 100644 --- a/src/sound/musicformats/music_midistream.cpp +++ b/src/sound/musicformats/music_midistream.cpp @@ -50,9 +50,6 @@ #ifdef _WIN32 MIDIDevice *CreateWinMIDIDevice(int mididevice); #endif -MIDIDevice *CreateTimidityMIDIDevice(const char *args, int samplerate); -MIDIDevice *CreateTimidityPPMIDIDevice(const char *args, int samplerate); -MIDIDevice *CreateWildMIDIDevice(const char *args, int samplerate); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -204,17 +201,18 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate) switch (devtype) { case MDEV_GUS: - dev = CreateTimidityMIDIDevice(Args, samplerate); + GUS_SetupConfig(&gusConfig, Args); + dev = CreateTimidityMIDIDevice(&gusConfig, samplerate); break; case MDEV_ADL: - SetAdlCustomBank(Args); + ADL_SetupConfig(&adlConfig, Args); dev = CreateADLMIDIDevice(&adlConfig); break; case MDEV_OPN: - SetOpnCustomBank(Args); - dev = CreateOPNMIDIDevice(Args); + OPN_SetupConfig(&opnConfig, Args); + dev = CreateOPNMIDIDevice(&opnConfig); break; case MDEV_MMAPI: @@ -226,13 +224,12 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate) // Intentional fall-through for non-Windows systems. case MDEV_FLUIDSYNTH: - BuildFluidPatchSetList(Args, true); + Fluid_SetupConfig(&fluidConfig, Args, true); dev = CreateFluidSynthMIDIDevice(samplerate, &fluidConfig, Printf); break; case MDEV_OPL: - LoadGenMidi(); - oplMidiConfig.core = getOPLCore(Args); + OPL_SetupConfig(&oplMidiConfig, Args); dev = CreateOplMIDIDevice(&oplMidiConfig); break; diff --git a/src/sound/musicformats/music_opl.cpp b/src/sound/musicformats/music_opl.cpp index f501c9bf66..065fc1c8c9 100644 --- a/src/sound/musicformats/music_opl.cpp +++ b/src/sound/musicformats/music_opl.cpp @@ -37,8 +37,15 @@ static bool OPL_Active; EXTERN_CVAR (Int, opl_numchips) +EXTERN_CVAR(Int, opl_core) + +int getOPLCore(const char* args) +{ + int current_opl_core = opl_core; + if (args != NULL && *args >= '0' && *args < '4') current_opl_core = *args - '0'; + return current_opl_core; +} -int getOPLCore(const char* args); OPLMUSSong::OPLMUSSong (FileReader &reader, const char *args) { @@ -81,9 +88,10 @@ bool OPLMUSSong::IsValid () const return m_Stream != NULL; } -void OPLMUSSong::ResetChips () +void OPLMUSSong::ChangeSettingInt(const char * name, int val) { - Music->ResetChips (opl_numchips); + if (!strcmp(name, "opl.numchips")) + Music->ResetChips (val); } bool OPLMUSSong::IsPlaying () From 98a4a77f6bb52ef87fbcdfd543134b4621d61da8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 27 Sep 2019 19:43:32 +0200 Subject: [PATCH 3/7] - took make_release_only off those subprojects which contain code that still need debugging on occasion. For these performance on debug builds doesn't really matter anyway. --- libraries/oplsynth/CMakeLists.txt | 2 -- libraries/timidity/CMakeLists.txt | 2 -- libraries/timidityplus/CMakeLists.txt | 2 -- libraries/wildmidi/CMakeLists.txt | 2 -- 4 files changed, 8 deletions(-) diff --git a/libraries/oplsynth/CMakeLists.txt b/libraries/oplsynth/CMakeLists.txt index 7dc736e74d..043dd328cd 100644 --- a/libraries/oplsynth/CMakeLists.txt +++ b/libraries/oplsynth/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required( VERSION 2.8.7 ) -make_release_only() - if( ZD_CMAKE_COMPILER_IS_GNUC_COMPATIBLE ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter -fomit-frame-pointer" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" ) diff --git a/libraries/timidity/CMakeLists.txt b/libraries/timidity/CMakeLists.txt index 8d3b758ed1..a33f07afd0 100644 --- a/libraries/timidity/CMakeLists.txt +++ b/libraries/timidity/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required( VERSION 2.8.7 ) -make_release_only() - if( ZD_CMAKE_COMPILER_IS_GNUC_COMPATIBLE ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter -fomit-frame-pointer" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" ) diff --git a/libraries/timidityplus/CMakeLists.txt b/libraries/timidityplus/CMakeLists.txt index bf386b9af6..ee1bf062d0 100644 --- a/libraries/timidityplus/CMakeLists.txt +++ b/libraries/timidityplus/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required( VERSION 2.8.7 ) -make_release_only() - if( ZD_CMAKE_COMPILER_IS_GNUC_COMPATIBLE ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter -fomit-frame-pointer" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" ) diff --git a/libraries/wildmidi/CMakeLists.txt b/libraries/wildmidi/CMakeLists.txt index 97b7cf807c..e5b2138df2 100644 --- a/libraries/wildmidi/CMakeLists.txt +++ b/libraries/wildmidi/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required( VERSION 2.8.7 ) -make_release_only() - if( ZD_CMAKE_COMPILER_IS_GNUC_COMPATIBLE ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter -fomit-frame-pointer" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" ) From 1cb668e895e34c7264273ed0333653065052a6e1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 27 Sep 2019 20:08:03 +0200 Subject: [PATCH 4/7] - fixed the GUS MIDI device. The sound font reader may not be deleted because its ownership is transferred to the instrument set. The gus_patchdir variable was not transferred to the config struct. --- src/sound/mididevices/midi_cvars.cpp | 3 ++- src/sound/mididevices/music_timidity_mididevice.cpp | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sound/mididevices/midi_cvars.cpp b/src/sound/mididevices/midi_cvars.cpp index 77570fc3b3..4545869d0b 100644 --- a/src/sound/mididevices/midi_cvars.cpp +++ b/src/sound/mididevices/midi_cvars.cpp @@ -209,7 +209,7 @@ void Fluid_SetupConfig(FluidConfig *config, const char* patches, bool systemfall if (FileExists(sysdir)) { config->fluid_patchset.push_back(sysdir); - return 1; + return; } // Try again with CT2MGM.SF2 sysdir[filepart + 3] = '2'; @@ -573,6 +573,7 @@ CUSTOM_CVAR(Bool, midi_dmxgus, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // This CUSTOM_CVAR(String, gus_patchdir, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { + gusConfig.gus_patchdir = self; CheckRestart(MDEV_GUS); } diff --git a/src/sound/mididevices/music_timidity_mididevice.cpp b/src/sound/mididevices/music_timidity_mididevice.cpp index f11153319e..bd78881031 100644 --- a/src/sound/mididevices/music_timidity_mididevice.cpp +++ b/src/sound/mididevices/music_timidity_mididevice.cpp @@ -112,7 +112,6 @@ void TimidityMIDIDevice::LoadInstruments(GUSConfig *config) config->instruments.reset(new Timidity::Instruments(psreader)); bool success = config->instruments->LoadDMXGUS(config->gus_memsize) >= 0; - delete psreader; config->dmxgus.clear(); if (success) @@ -130,7 +129,6 @@ void TimidityMIDIDevice::LoadInstruments(GUSConfig *config) config->loadedConfig = config->readerName; config->instruments.reset(new Timidity::Instruments(config->reader)); bool err = config->instruments->LoadConfig() < 0; - delete config->reader; config->reader = nullptr; if (err) From 9aecabc88748cd8864d85b1f22203e75c4c5dd91 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 27 Sep 2019 20:40:19 +0200 Subject: [PATCH 5/7] - moved the last static variable - def_inst_name into the Instruments class. With this the GUS device should be fully reentrant. --- libraries/timidity/timidity.cpp | 6 ++---- libraries/timidity/timidity/instrum.h | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/timidity/timidity.cpp b/libraries/timidity/timidity.cpp index 12055c3032..cfc6f331be 100644 --- a/libraries/timidity/timidity.cpp +++ b/libraries/timidity/timidity.cpp @@ -38,8 +38,6 @@ namespace Timidity { -static std::string def_instr_name; - static long ParseCommandLine(const char* args, int* argc, char** argv) { int count; @@ -718,8 +716,8 @@ Renderer::Renderer(float sample_rate, int voices_, Instruments *inst) default_instrument = NULL; default_program = DEFAULT_PROGRAM; - if (def_instr_name.length() > 0) - set_default_instrument(def_instr_name.c_str()); + if (inst->def_instr_name.length() > 0) + set_default_instrument(inst->def_instr_name.c_str()); voices = std::max(voices_, 16); voice = new Voice[voices]; diff --git a/libraries/timidity/timidity/instrum.h b/libraries/timidity/timidity/instrum.h index 452327fbe0..cc14dba1e5 100644 --- a/libraries/timidity/timidity/instrum.h +++ b/libraries/timidity/timidity/instrum.h @@ -168,6 +168,7 @@ public: ToneBank* tonebank[MAXBANK] = {}; ToneBank* drumset[MAXBANK] = {}; FontFile* Fonts = nullptr; + std::string def_instr_name; Instruments(SoundFontReaderInterface* reader); ~Instruments(); From 890db1fbf9f0bccf7c44ca70cda73c77216f9f03 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 27 Sep 2019 22:19:00 +0200 Subject: [PATCH 6/7] - Timidity++ done. --- libraries/timidityplus/playmidi.cpp | 4 +- libraries/timidityplus/timiditypp/timidity.h | 2 +- src/sound/mididevices/midi_cvars.cpp | 207 +++++++++++++- .../mididevices/music_timidity_mididevice.cpp | 5 +- .../music_timiditypp_mididevice.cpp | 259 +++--------------- src/sound/music/i_musicinterns.h | 27 +- src/sound/musicformats/music_midistream.cpp | 3 +- 7 files changed, 272 insertions(+), 235 deletions(-) diff --git a/libraries/timidityplus/playmidi.cpp b/libraries/timidityplus/playmidi.cpp index be1f0a762f..04fb2741a5 100644 --- a/libraries/timidityplus/playmidi.cpp +++ b/libraries/timidityplus/playmidi.cpp @@ -41,7 +41,7 @@ namespace TimidityPlus { - std::mutex CvarCritSec; + std::mutex ConfigMutex; bool timidity_modulation_wheel = true; bool timidity_portamento = false; int timidity_reverb = 0; @@ -4997,7 +4997,7 @@ int Player::compute_data(float *buffer, int32_t count) { if (count == 0) return RC_OK; - std::lock_guard lock(CvarCritSec); + std::lock_guard lock(ConfigMutex); if (last_reverb_setting != timidity_reverb) { diff --git a/libraries/timidityplus/timiditypp/timidity.h b/libraries/timidityplus/timiditypp/timidity.h index 31db58ac46..0edce9f82a 100644 --- a/libraries/timidityplus/timiditypp/timidity.h +++ b/libraries/timidityplus/timiditypp/timidity.h @@ -125,7 +125,7 @@ namespace TimidityPlus { -extern std::mutex CvarCritSec; +extern std::mutex ConfigMutex; extern bool timidity_modulation_wheel; extern bool timidity_portamento; extern int timidity_reverb; diff --git a/src/sound/mididevices/midi_cvars.cpp b/src/sound/mididevices/midi_cvars.cpp index 4545869d0b..38069ffa64 100644 --- a/src/sound/mididevices/midi_cvars.cpp +++ b/src/sound/mididevices/midi_cvars.cpp @@ -41,6 +41,9 @@ #include "timidity/timidity.h" #include "timidity/playmidi.h" #include "timidity/instrum.h" +#include "timiditypp/controls.h" +#include "timiditypp/timidity.h" +#include "timiditypp/instrum.h" #include "v_text.h" // do this without including windows.h for this one single prototype @@ -61,6 +64,13 @@ FluidConfig fluidConfig; OPLMidiConfig oplMidiConfig; OpnConfig opnConfig; GUSConfig gusConfig; +TimidityConfig timidityConfig; + +//========================================================================== +// +// ADL Midi device +// +//========================================================================== CUSTOM_CVAR(Int, adl_chips_count, 6, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { @@ -132,6 +142,13 @@ CUSTOM_CVAR(Int, adl_volume_model, ADLMIDI_VolumeModel_DMX, CVAR_ARCHIVE | CVAR_ CheckRestart(MDEV_ADL); } +//========================================================================== +// +// Fluidsynth MIDI device +// +//========================================================================== + + #define FLUID_CHORUS_MOD_SINE 0 #define FLUID_CHORUS_MOD_TRIANGLE 1 #define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE @@ -439,7 +456,11 @@ CUSTOM_CVAR(Int, fluid_chorus_type, FLUID_CHORUS_DEFAULT_TYPE, CVAR_ARCHIVE|CVAR } - +//========================================================================== +// +// OPL MIDI device +// +//========================================================================== CUSTOM_CVAR(Int, opl_numchips, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { @@ -487,6 +508,13 @@ void OPL_SetupConfig(OPLMidiConfig *config, const char *args) } +//========================================================================== +// +// OPN MIDI device +// +//========================================================================== + + CUSTOM_CVAR(Int, opn_chips_count, 8, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { opnConfig.opn_chips_count = self; @@ -557,8 +585,11 @@ void OPN_SetupConfig(OpnConfig *config, const char *Args) } -// CVARS for this device - all of them require a device reset -------------------------------------------- - +//========================================================================== +// +// GUS MIDI device +// +//========================================================================== CUSTOM_CVAR(String, midi_config, "gzdoom", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) @@ -621,6 +652,12 @@ static void gus_printfunc(int type, int verbosity_level, const char* fmt, ...) } } +// make sure we can use the above function for the Timidity++ device as well. +static_assert(Timidity::CMSG_ERROR == TimidityPlus::CMSG_ERROR, "Timidity constant mismatch"); +static_assert(Timidity::CMSG_WARNING == TimidityPlus::CMSG_WARNING, "Timidity constant mismatch"); +static_assert(Timidity::CMSG_INFO == TimidityPlus::CMSG_INFO, "Timidity constant mismatch"); +static_assert(Timidity::VERB_DEBUG == TimidityPlus::VERB_DEBUG, "Timidity constant mismatch"); + //========================================================================== // // Sets up the date to load the instruments for the GUS device. @@ -662,3 +699,167 @@ bool GUS_SetupConfig(GUSConfig *config, const char *args) return true; } + +//========================================================================== +// +// CVar interface to configurable parameters +// +// Timidity++ uses a static global set of configuration variables +// THese can be changed while the synth is playing but need synchronization. +// +// Currently the synth is not fully reentrant due to this and a handful +// of other global variables. +// +//========================================================================== + +template void ChangeVarSync(T& var, T value) +{ + std::lock_guard lock(TimidityPlus::ConfigMutex); + var = value; +} + +CUSTOM_CVAR(Bool, timidity_modulation_wheel, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + ChangeVarSync(TimidityPlus::timidity_modulation_wheel, *self); +} + +CUSTOM_CVAR(Bool, timidity_portamento, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + ChangeVarSync(TimidityPlus::timidity_portamento, *self); +} +/* +* reverb=0 no reverb 0 +* reverb=1 old reverb 1 +* reverb=1,n set reverb level to n (-1 to -127) +* reverb=2 "global" old reverb 2 +* reverb=2,n set reverb level to n (-1 to -127) - 128 +* reverb=3 new reverb 3 +* reverb=3,n set reverb level to n (-1 to -127) - 256 +* reverb=4 "global" new reverb 4 +* reverb=4,n set reverb level to n (-1 to -127) - 384 +*/ +EXTERN_CVAR(Int, timidity_reverb_level) +EXTERN_CVAR(Int, timidity_reverb) + +static void SetReverb() +{ + int value = 0; + int mode = timidity_reverb; + int level = timidity_reverb_level; + + if (mode == 0 || level == 0) value = mode; + else value = (mode - 1) * -128 - level; + ChangeVarSync(TimidityPlus::timidity_reverb, value); +} + +CUSTOM_CVAR(Int, timidity_reverb, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0 || self > 4) self = 0; + else SetReverb(); +} + +CUSTOM_CVAR(Int, timidity_reverb_level, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0 || self > 127) self = 0; + else SetReverb(); +} + +CUSTOM_CVAR(Int, timidity_chorus, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + ChangeVarSync(TimidityPlus::timidity_chorus, *self); +} + +CUSTOM_CVAR(Bool, timidity_surround_chorus, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + ChangeVarSync(TimidityPlus::timidity_surround_chorus, *self); + CheckRestart(MDEV_TIMIDITY); +} + +CUSTOM_CVAR(Bool, timidity_channel_pressure, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + ChangeVarSync(TimidityPlus::timidity_channel_pressure, *self); +} + +CUSTOM_CVAR(Int, timidity_lpf_def, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + ChangeVarSync(TimidityPlus::timidity_lpf_def, *self); +} + +CUSTOM_CVAR(Bool, timidity_temper_control, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + ChangeVarSync(TimidityPlus::timidity_temper_control, *self); +} + +CUSTOM_CVAR(Bool, timidity_modulation_envelope, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + ChangeVarSync(TimidityPlus::timidity_modulation_envelope, *self); + CheckRestart(MDEV_TIMIDITY); +} + +CUSTOM_CVAR(Bool, timidity_overlap_voice_allow, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + ChangeVarSync(TimidityPlus::timidity_overlap_voice_allow, *self); +} + +CUSTOM_CVAR(Bool, timidity_drum_effect, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + ChangeVarSync(TimidityPlus::timidity_drum_effect, *self); +} + +CUSTOM_CVAR(Bool, timidity_pan_delay, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + ChangeVarSync(TimidityPlus::timidity_pan_delay, *self); +} + +CUSTOM_CVAR(Float, timidity_drum_power, 1.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) /* coef. of drum amplitude */ +{ + if (self < 0) self = 0; + else if (self > MAX_AMPLIFICATION / 100.f) self = MAX_AMPLIFICATION / 100.f; + ChangeVarSync(TimidityPlus::timidity_drum_power, *self); +} +CUSTOM_CVAR(Int, timidity_key_adjust, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < -24) self = -24; + else if (self > 24) self = 24; + ChangeVarSync(TimidityPlus::timidity_key_adjust, *self); +} +// For testing mainly. +CUSTOM_CVAR(Float, timidity_tempo_adjust, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0.25) self = 0.25; + else if (self > 10) self = 10; + ChangeVarSync(TimidityPlus::timidity_tempo_adjust, *self); +} + +CUSTOM_CVAR(Float, min_sustain_time, 5000, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0) self = 0; + ChangeVarSync(TimidityPlus::min_sustain_time, *self); +} + +// Config file to use +CUSTOM_CVAR(String, timidity_config, "gzdoom", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + CheckRestart(MDEV_TIMIDITY); +} + +CVAR(Int, timidity_frequency, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + + +bool Timidity_SetupConfig(TimidityConfig* config, const char* args) +{ + config->errorfunc = gus_printfunc; + if (*args == 0) args = timidity_config; + if (stricmp(config->loadedConfig.c_str(), args) == 0) return false; // aleady loaded + + auto reader = sfmanager.OpenSoundFont(args, SF_GUS | SF_SF2); + if (reader == nullptr) + { + char error[80]; + snprintf(error, 80, "Timidity++: %s: Unable to load sound font\n", args); + throw std::runtime_error(error); + } + config->reader = reader; + config->readerName = args; + return true; +} diff --git a/src/sound/mididevices/music_timidity_mididevice.cpp b/src/sound/mididevices/music_timidity_mididevice.cpp index bd78881031..e3e0a81501 100644 --- a/src/sound/mididevices/music_timidity_mididevice.cpp +++ b/src/sound/mididevices/music_timidity_mididevice.cpp @@ -78,6 +78,7 @@ public: protected: Timidity::Renderer *Renderer; + std::shared_ptr instruments; // The device needs to hold a reference to this while the renderer is in use. void HandleEvent(int status, int parm1, int parm2); void HandleLongEvent(const uint8_t *data, int len); @@ -142,7 +143,7 @@ void TimidityMIDIDevice::LoadInstruments(GUSConfig *config) { throw std::runtime_error("No instruments set for GUS device"); } - + instruments = config->instruments; } //========================================================================== @@ -155,7 +156,7 @@ TimidityMIDIDevice::TimidityMIDIDevice(GUSConfig *config, int samplerate) : SoftSynthMIDIDevice(samplerate, 11025, 65535) { LoadInstruments(config); - Renderer = new Timidity::Renderer((float)SampleRate, config->midi_voices, config->instruments.get()); + Renderer = new Timidity::Renderer((float)SampleRate, config->midi_voices, instruments.get()); } //========================================================================== diff --git a/src/sound/mididevices/music_timiditypp_mididevice.cpp b/src/sound/mididevices/music_timiditypp_mididevice.cpp index c07373733c..8ae874f45f 100644 --- a/src/sound/mididevices/music_timiditypp_mididevice.cpp +++ b/src/sound/mididevices/music_timiditypp_mididevice.cpp @@ -44,194 +44,19 @@ #include "timiditypp/playmidi.h" -//========================================================================== -// -// Error printing override to redirect to the internal console instead of stdout. -// -//========================================================================== - -static void gzdoom_ctl_cmsg(int type, int verbosity_level, const char* fmt, ...) -{ - if (verbosity_level >= TimidityPlus::VERB_DEBUG) return; // Don't waste time on diagnostics. - - va_list args; - va_start(args, fmt); - FString msg; - msg.VFormat(fmt, args); - va_end(args); - - switch (type) - { - case TimidityPlus::CMSG_ERROR: - Printf(TEXTCOLOR_RED "%s\n", msg.GetChars()); - break; - - case TimidityPlus::CMSG_WARNING: - Printf(TEXTCOLOR_YELLOW "%s\n", msg.GetChars()); - break; - - case TimidityPlus::CMSG_INFO: - DPrintf(DMSG_SPAMMY, "%s\n", msg.GetChars()); - break; - } -} - -//========================================================================== -// -// CVar interface to configurable parameters -// -//========================================================================== - -template void ChangeVarSync(T& var, T value) -{ - std::lock_guard lock(TimidityPlus::CvarCritSec); - var = value; -} - -CUSTOM_CVAR(Bool, timidity_modulation_wheel, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - ChangeVarSync(TimidityPlus::timidity_modulation_wheel, *self); -} - -CUSTOM_CVAR(Bool, timidity_portamento, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - ChangeVarSync(TimidityPlus::timidity_portamento, *self); -} -/* -* reverb=0 no reverb 0 -* reverb=1 old reverb 1 -* reverb=1,n set reverb level to n (-1 to -127) -* reverb=2 "global" old reverb 2 -* reverb=2,n set reverb level to n (-1 to -127) - 128 -* reverb=3 new reverb 3 -* reverb=3,n set reverb level to n (-1 to -127) - 256 -* reverb=4 "global" new reverb 4 -* reverb=4,n set reverb level to n (-1 to -127) - 384 -*/ -EXTERN_CVAR(Int, timidity_reverb_level) -EXTERN_CVAR(Int, timidity_reverb) - -static void SetReverb() -{ - int value = 0; - int mode = timidity_reverb; - int level = timidity_reverb_level; - - if (mode == 0 || level == 0) value = mode; - else value = (mode - 1) * -128 - level; - ChangeVarSync(TimidityPlus::timidity_reverb, value); -} - -CUSTOM_CVAR(Int, timidity_reverb, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (self < 0 || self > 4) self = 0; - else SetReverb(); -} - -CUSTOM_CVAR(Int, timidity_reverb_level, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (self < 0 || self > 127) self = 0; - else SetReverb(); -} - -CUSTOM_CVAR(Int, timidity_chorus, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - ChangeVarSync(TimidityPlus::timidity_chorus, *self); -} - -CUSTOM_CVAR(Bool, timidity_surround_chorus, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_TIMIDITY) - { - MIDIDeviceChanged(-1, true); - } - ChangeVarSync(TimidityPlus::timidity_surround_chorus, *self); -} - -CUSTOM_CVAR(Bool, timidity_channel_pressure, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - ChangeVarSync(TimidityPlus::timidity_channel_pressure, *self); -} - -CUSTOM_CVAR(Int, timidity_lpf_def, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - ChangeVarSync(TimidityPlus::timidity_lpf_def, *self); -} - -CUSTOM_CVAR(Bool, timidity_temper_control, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - ChangeVarSync(TimidityPlus::timidity_temper_control, *self); -} - -CUSTOM_CVAR(Bool, timidity_modulation_envelope, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - ChangeVarSync(TimidityPlus::timidity_modulation_envelope, *self); - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_TIMIDITY) - { - MIDIDeviceChanged(-1, true); - } -} - -CUSTOM_CVAR(Bool, timidity_overlap_voice_allow, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - ChangeVarSync(TimidityPlus::timidity_overlap_voice_allow, *self); -} - -CUSTOM_CVAR(Bool, timidity_drum_effect, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - ChangeVarSync(TimidityPlus::timidity_drum_effect, *self); -} - -CUSTOM_CVAR(Bool, timidity_pan_delay, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - ChangeVarSync(TimidityPlus::timidity_pan_delay, *self); -} - -CUSTOM_CVAR(Float, timidity_drum_power, 1.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) /* coef. of drum amplitude */ -{ - if (self < 0) self = 0; - else if (self > MAX_AMPLIFICATION / 100.f) self = MAX_AMPLIFICATION / 100.f; - ChangeVarSync(TimidityPlus::timidity_drum_power, *self); -} -CUSTOM_CVAR(Int, timidity_key_adjust, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (self < -24) self = -24; - else if (self > 24) self = 24; - ChangeVarSync(TimidityPlus::timidity_key_adjust, *self); -} -// For testing mainly. -CUSTOM_CVAR(Float, timidity_tempo_adjust, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (self < 0.25) self = 0.25; - else if (self > 10) self = 10; - ChangeVarSync(TimidityPlus::timidity_tempo_adjust, *self); -} - -CUSTOM_CVAR(Float, min_sustain_time, 5000, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (self < 0) self = 0; - ChangeVarSync(TimidityPlus::min_sustain_time, *self); -} class TimidityPPMIDIDevice : public SoftSynthMIDIDevice { - static TimidityPlus::Instruments *instruments; - static FString configName; - int sampletime; + std::shared_ptr instruments; public: - TimidityPPMIDIDevice(const char *args, int samplerate); + TimidityPPMIDIDevice(TimidityConfig *config, int samplerate); ~TimidityPPMIDIDevice(); int Open(MidiCallback, void *userdata); void PrecacheInstruments(const uint16_t *instruments, int count); //FString GetStats(); int GetDeviceType() const override { return MDEV_TIMIDITY; } - static void ClearInstruments() - { - if (instruments != nullptr) delete instruments; - instruments = nullptr; - } double test[3] = { 0, 0, 0 }; @@ -241,64 +66,50 @@ protected: void HandleEvent(int status, int parm1, int parm2); void HandleLongEvent(const uint8_t *data, int len); void ComputeOutput(float *buffer, int len); + void LoadInstruments(TimidityConfig* config); }; -TimidityPlus::Instruments *TimidityPPMIDIDevice::instruments; -FString TimidityPPMIDIDevice::configName; -// Config file to use -CUSTOM_CVAR(String, timidity_config, "gzdoom", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +//========================================================================== +// +// +// +//========================================================================== + +void TimidityPPMIDIDevice::LoadInstruments(TimidityConfig* config) { - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_TIMIDITY) + if (config->reader) { - MIDIDeviceChanged(-1, true); + config->loadedConfig = config->readerName; + config->instruments.reset(new TimidityPlus::Instruments()); + bool success = config->instruments->load(config->reader); + config->reader = nullptr; + + if (!success) + { + config->instruments.reset(); + config->loadedConfig = ""; + throw std::runtime_error("Unable to initialize instruments for Timidity++ MIDI device"); + } } + else if (config->instruments == nullptr) + { + throw std::runtime_error("No instruments set for Timidity++ device"); + } + instruments = config->instruments; } - -CVAR (Int, timidity_frequency, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) - //========================================================================== // // TimidityPPMIDIDevice Constructor // //========================================================================== -TimidityPPMIDIDevice::TimidityPPMIDIDevice(const char *args, int samplerate) - :SoftSynthMIDIDevice(samplerate <= 0? timidity_frequency : samplerate, 4000, 65000) +TimidityPPMIDIDevice::TimidityPPMIDIDevice(TimidityConfig *config, int samplerate) + :SoftSynthMIDIDevice(samplerate <= 0? config->samplerate : samplerate, 4000, 65000) { - if (args == NULL || *args == 0) args = timidity_config; - - Renderer = nullptr; - if (instruments != nullptr && configName.CompareNoCase(args)) // Only load instruments if they have changed from the last played song. - { - delete instruments; - instruments = nullptr; - } - TimidityPlus::printMessage = gzdoom_ctl_cmsg; TimidityPlus::set_playback_rate(SampleRate); - - if (instruments == nullptr) - { - auto sfreader = sfmanager.OpenSoundFont(args, SF_SF2 | SF_GUS); - if (sfreader != nullptr) - { - instruments = new TimidityPlus::Instruments; - if (!instruments->load(sfreader)) - { - delete instruments; - instruments = nullptr; - } - } - } - if (instruments != nullptr) - { - Renderer = new TimidityPlus::Player(instruments); - } - else - { - I_Error("Failed to load any MIDI patches"); - } - sampletime = 0; + LoadInstruments(config); + Renderer = new TimidityPlus::Player(instruments.get()); } //========================================================================== @@ -329,8 +140,6 @@ int TimidityPPMIDIDevice::Open(MidiCallback callback, void *userdata) { Renderer->playmidi_stream_init(); } - // No instruments loaded means we cannot play... - if (instruments == nullptr) return 0; return ret; } @@ -393,14 +202,14 @@ void TimidityPPMIDIDevice::ComputeOutput(float *buffer, int len) // //========================================================================== -MIDIDevice *CreateTimidityPPMIDIDevice(const char *args, int samplerate) +MIDIDevice *CreateTimidityPPMIDIDevice(TimidityConfig* config, int samplerate) { - return new TimidityPPMIDIDevice(args, samplerate); + return new TimidityPPMIDIDevice(config, samplerate); } + void TimidityPP_Shutdown() { - TimidityPPMIDIDevice::ClearInstruments(); TimidityPlus::free_gauss_table(); TimidityPlus::free_global_mblock(); } diff --git a/src/sound/music/i_musicinterns.h b/src/sound/music/i_musicinterns.h index 682928a1e1..4b88d320b9 100644 --- a/src/sound/music/i_musicinterns.h +++ b/src/sound/music/i_musicinterns.h @@ -113,6 +113,30 @@ struct GUSConfig extern GUSConfig gusConfig; +namespace TimidityPlus +{ + class Instruments; + class SoundFontReaderInterface; +} + +struct TimidityConfig +{ + int samplerate = 0; + 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; + class MIDIStreamer; typedef void(*MidiCallback)(void *); @@ -394,8 +418,8 @@ 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 *CreateTimidityPPMIDIDevice(const char *args, int samplerate); MIDIDevice *CreateWildMIDIDevice(const char *args, int samplerate); // Data interface @@ -405,6 +429,7 @@ void ADL_SetupConfig(ADLConfig *config, const char *Args); void OPL_SetupConfig(OPLMidiConfig *config, const char *args); void OPN_SetupConfig(OpnConfig *config, const char *Args); bool GUS_SetupConfig(GUSConfig *config, const char *args); +bool Timidity_SetupConfig(TimidityConfig* config, const char* args); // Module played via foo_dumb ----------------------------------------------- diff --git a/src/sound/musicformats/music_midistream.cpp b/src/sound/musicformats/music_midistream.cpp index 88b281bfdd..d21238dd0e 100644 --- a/src/sound/musicformats/music_midistream.cpp +++ b/src/sound/musicformats/music_midistream.cpp @@ -234,7 +234,8 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate) break; case MDEV_TIMIDITY: - dev = CreateTimidityPPMIDIDevice(Args, samplerate); + Timidity_SetupConfig(&timidityConfig, Args); + dev = CreateTimidityPPMIDIDevice(&timidityConfig, samplerate); break; case MDEV_WILDMIDI: From 61bc25d7817fa96e1dc908a041d39222111d4de4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 28 Sep 2019 00:10:39 +0200 Subject: [PATCH 7/7] - WildMidi also done. --- src/sound/mididevices/midi_cvars.cpp | 58 +++++++- .../music_softsynth_mididevice.cpp | 6 +- .../music_timiditypp_mididevice.cpp | 2 +- .../mididevices/music_wildmidi_mididevice.cpp | 126 ++++++++---------- .../mididevices/music_win_mididevice.cpp | 1 - src/sound/music/i_music.cpp | 6 +- src/sound/music/i_music.h | 1 - src/sound/music/i_musicinterns.h | 29 +++- src/sound/musicformats/music_midistream.cpp | 33 +---- 9 files changed, 140 insertions(+), 122 deletions(-) diff --git a/src/sound/mididevices/midi_cvars.cpp b/src/sound/mididevices/midi_cvars.cpp index 38069ffa64..4170fdc065 100644 --- a/src/sound/mididevices/midi_cvars.cpp +++ b/src/sound/mididevices/midi_cvars.cpp @@ -45,6 +45,7 @@ #include "timiditypp/timidity.h" #include "timiditypp/instrum.h" #include "v_text.h" +#include "c_console.h" // do this without including windows.h for this one single prototype #ifdef _WIN32 @@ -65,6 +66,7 @@ OPLMidiConfig oplMidiConfig; OpnConfig opnConfig; GUSConfig gusConfig; TimidityConfig timidityConfig; +WildMidiConfig wildMidiConfig; //========================================================================== // @@ -843,9 +845,6 @@ CUSTOM_CVAR(String, timidity_config, "gzdoom", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CheckRestart(MDEV_TIMIDITY); } -CVAR(Int, timidity_frequency, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) - - bool Timidity_SetupConfig(TimidityConfig* config, const char* args) { config->errorfunc = gus_printfunc; @@ -863,3 +862,56 @@ bool Timidity_SetupConfig(TimidityConfig* config, const char* args) config->readerName = args; return true; } + +//========================================================================== +// +// WildMidi +// +//========================================================================== + +CUSTOM_CVAR(String, wildmidi_config, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + CheckRestart(MDEV_WILDMIDI); +} + +CUSTOM_CVAR(Bool, wildmidi_reverb, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (currSong != NULL) + currSong->ChangeSettingInt("wildmidi.reverb", self ? WildMidi::WM_MO_REVERB : 0); + wildMidiConfig.reverb = self; +} + +CUSTOM_CVAR(Bool, wildmidi_enhanced_resampling, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (currSong != NULL) + currSong->ChangeSettingInt("wildmidi.resampling", self ? WildMidi::WM_MO_ENHANCED_RESAMPLING : 0); + wildMidiConfig.enhanced_resampling = self; +} + +static void wm_printfunc(const char* wmfmt, va_list args) +{ + Printf(TEXTCOLOR_RED); + VPrintf(PRINT_HIGH, wmfmt, args); +} + + +bool WildMidi_SetupConfig(WildMidiConfig* config, const char* args) +{ + config->errorfunc = wm_printfunc; + if (*args == 0) args = wildmidi_config; + if (stricmp(config->loadedConfig.c_str(), args) == 0) return false; // aleady loaded + + auto reader = sfmanager.OpenSoundFont(args, SF_GUS); + if (reader == nullptr) + { + char error[80]; + snprintf(error, 80, "WildMidi: %s: Unable to load sound font\n", args); + throw std::runtime_error(error); + } + config->reader = reader; + config->readerName = args; + config->reverb = wildmidi_reverb; + config->enhanced_resampling = wildmidi_enhanced_resampling; + return true; +} + diff --git a/src/sound/mididevices/music_softsynth_mididevice.cpp b/src/sound/mididevices/music_softsynth_mididevice.cpp index af1f1251c1..b1b3a22af7 100644 --- a/src/sound/mididevices/music_softsynth_mididevice.cpp +++ b/src/sound/mididevices/music_softsynth_mididevice.cpp @@ -54,7 +54,7 @@ // PUBLIC DATA DEFINITIONS ------------------------------------------------- -CVAR(Bool, synth_watch, false, 0) +//CVAR(Bool, synth_watch, false, 0) // CODE -------------------------------------------------------------------- @@ -72,7 +72,7 @@ SoftSynthMIDIDevice::SoftSynthMIDIDevice(int samplerate, int minrate, int maxrat Events = NULL; Started = false; SampleRate = samplerate; - if (SampleRate < minrate || SampleRate > maxrate) SampleRate = GSnd != NULL ? clamp((int)GSnd->GetOutputRate(), minrate, maxrate) : 44100; + if (SampleRate < minrate || SampleRate > maxrate) SampleRate = 44100; } //========================================================================== @@ -318,6 +318,7 @@ int SoftSynthMIDIDevice::PlayTick() int parm2 = (event[2] >> 16) & 0x7f; HandleEvent(status, parm1, parm2); +#if 0 if (synth_watch) { static const char *const commands[8] = @@ -339,6 +340,7 @@ int SoftSynthMIDIDevice::PlayTick() fputs(buffer, stderr); #endif } +#endif } // Advance to next event. diff --git a/src/sound/mididevices/music_timiditypp_mididevice.cpp b/src/sound/mididevices/music_timiditypp_mididevice.cpp index 8ae874f45f..1f9eb82fd9 100644 --- a/src/sound/mididevices/music_timiditypp_mididevice.cpp +++ b/src/sound/mididevices/music_timiditypp_mididevice.cpp @@ -105,7 +105,7 @@ void TimidityPPMIDIDevice::LoadInstruments(TimidityConfig* config) //========================================================================== TimidityPPMIDIDevice::TimidityPPMIDIDevice(TimidityConfig *config, int samplerate) - :SoftSynthMIDIDevice(samplerate <= 0? config->samplerate : samplerate, 4000, 65000) + :SoftSynthMIDIDevice(samplerate, 4000, 65000) { TimidityPlus::set_playback_rate(SampleRate); LoadInstruments(config); diff --git a/src/sound/mididevices/music_wildmidi_mididevice.cpp b/src/sound/mididevices/music_wildmidi_mididevice.cpp index ab88d2b9fa..5cd261812d 100644 --- a/src/sound/mididevices/music_wildmidi_mididevice.cpp +++ b/src/sound/mididevices/music_wildmidi_mididevice.cpp @@ -50,7 +50,7 @@ class WildMIDIDevice : public SoftSynthMIDIDevice { public: - WildMIDIDevice(const char *args, int samplerate); + WildMIDIDevice(WildMidiConfig* config, int samplerate); ~WildMIDIDevice(); int Open(MidiCallback, void *userdata); @@ -60,11 +60,14 @@ public: protected: WildMidi::Renderer *Renderer; + std::shared_ptr instruments; void HandleEvent(int status, int parm1, int parm2); void HandleLongEvent(const uint8_t *data, int len); void ComputeOutput(float *buffer, int len); - void WildMidiSetOption(int opt, int set); + void ChangeSettingInt(const char *opt, int set); + void LoadInstruments(WildMidiConfig* config); + }; @@ -80,44 +83,43 @@ protected: // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static FString CurrentConfig; - // PUBLIC DATA DEFINITIONS ------------------------------------------------- -CUSTOM_CVAR(String, wildmidi_config, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (currSong != nullptr && currSong->GetDeviceType() == MDEV_WILDMIDI) - { - MIDIDeviceChanged(-1, true); - } -} -CVAR(Int, wildmidi_frequency, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CUSTOM_CVAR(Bool, wildmidi_reverb, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) -{ - if (currSong != NULL) - currSong->WildMidiSetOption(WildMidi::WM_MO_REVERB, *self? WildMidi::WM_MO_REVERB:0); -} - -CUSTOM_CVAR(Bool, wildmidi_enhanced_resampling, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) -{ - if (currSong != NULL) - currSong->WildMidiSetOption(WildMidi::WM_MO_ENHANCED_RESAMPLING, *self? WildMidi::WM_MO_ENHANCED_RESAMPLING:0); -} - -static WildMidi::Instruments *instruments; - -void WildMidi_Shutdown() -{ - if (instruments) delete instruments; -} // CODE -------------------------------------------------------------------- -static void gzdoom_error_func(const char* wmfmt, va_list args) +//========================================================================== +// +// +// +//========================================================================== + +void WildMIDIDevice::LoadInstruments(WildMidiConfig* config) { - Printf(TEXTCOLOR_RED); - VPrintf(PRINT_HIGH, wmfmt, args); + if (config->reader) + { + config->loadedConfig = config->readerName; + config->instruments.reset(new WildMidi::Instruments(config->reader, SampleRate)); + bool success = config->instruments->LoadConfig(config->readerName.c_str()); + config->reader = nullptr; + + if (!success) + { + config->instruments.reset(); + config->loadedConfig = ""; + throw std::runtime_error("Unable to initialize instruments for WildMidi device"); + } + } + else if (config->instruments == nullptr) + { + throw std::runtime_error("No instruments set for WildMidi device"); + } + instruments = config->instruments; + if (instruments->LoadConfig(nullptr) < 0) + { + throw std::runtime_error("Unable to load instruments set for WildMidi device"); + } } //========================================================================== @@ -126,45 +128,17 @@ static void gzdoom_error_func(const char* wmfmt, va_list args) // //========================================================================== -WildMIDIDevice::WildMIDIDevice(const char *args, int samplerate) - :SoftSynthMIDIDevice(samplerate <= 0? wildmidi_frequency : samplerate, 11025, 65535) +WildMIDIDevice::WildMIDIDevice(WildMidiConfig *config, int samplerate) + :SoftSynthMIDIDevice(samplerate, 11025, 65535) { Renderer = NULL; - WildMidi::wm_error_func = gzdoom_error_func; + LoadInstruments(config); - if (args == NULL || *args == 0) args = wildmidi_config; - - if (instruments == nullptr || (CurrentConfig.CompareNoCase(args) != 0 || SampleRate != instruments->GetSampleRate())) - { - if (instruments) delete instruments; - instruments = nullptr; - CurrentConfig = ""; - - auto reader = sfmanager.OpenSoundFont(args, SF_GUS); - if (reader == nullptr) - { - I_Error("WildMidi: Unable to open sound font %s\n", args); - } - - instruments = new WildMidi::Instruments(reader, SampleRate); - if (instruments->LoadConfig(nullptr) < 0) - { - I_Error("WildMidi: Unable to load instruments for sound font %s\n", args); - } - CurrentConfig = args; - } - if (CurrentConfig.IsNotEmpty()) - { - Renderer = new WildMidi::Renderer(instruments); - int flags = 0; - if (wildmidi_enhanced_resampling) flags |= WildMidi::WM_MO_ENHANCED_RESAMPLING; - if (wildmidi_reverb) flags |= WildMidi::WM_MO_REVERB; - Renderer->SetOption(WildMidi::WM_MO_ENHANCED_RESAMPLING | WildMidi::WM_MO_REVERB, flags); - } - else - { - I_Error("Failed to load any MIDI patches"); - } + Renderer = new WildMidi::Renderer(instruments.get()); + int flags = 0; + if (config->enhanced_resampling) flags |= WildMidi::WM_MO_ENHANCED_RESAMPLING; + if (config->reverb) flags |= WildMidi::WM_MO_REVERB; + Renderer->SetOption(WildMidi::WM_MO_ENHANCED_RESAMPLING | WildMidi::WM_MO_REVERB, flags); } //========================================================================== @@ -180,7 +154,6 @@ WildMIDIDevice::~WildMIDIDevice() { delete Renderer; } - // Do not shut down the device so that it can be reused for the next song being played. } //========================================================================== @@ -277,9 +250,14 @@ FString WildMIDIDevice::GetStats() // //========================================================================== -void WildMIDIDevice::WildMidiSetOption(int opt, int set) +void WildMIDIDevice::ChangeSettingInt(const char *opt, int set) { - Renderer->SetOption(opt, set); + int option; + if (!stricmp(opt, "wildmidi.reverb")) option = WildMidi::WM_MO_REVERB; + else if (!stricmp(opt, "wildmidi.resampling")) option = WildMidi::WM_MO_ENHANCED_RESAMPLING; + else return; + int setit = option * int(set); + Renderer->SetOption(option, setit); } //========================================================================== @@ -288,8 +266,8 @@ void WildMIDIDevice::WildMidiSetOption(int opt, int set) // //========================================================================== -MIDIDevice *CreateWildMIDIDevice(const char *args, int samplerate) +MIDIDevice *CreateWildMIDIDevice(WildMidiConfig *config, int samplerate) { - return new WildMIDIDevice(args, samplerate); + return new WildMIDIDevice(config, samplerate); } diff --git a/src/sound/mididevices/music_win_mididevice.cpp b/src/sound/mididevices/music_win_mididevice.cpp index 94c8a0b36a..17ae00341b 100644 --- a/src/sound/mididevices/music_win_mididevice.cpp +++ b/src/sound/mididevices/music_win_mididevice.cpp @@ -98,7 +98,6 @@ public: //protected: static void CALLBACK CallbackFunc(HMIDIOUT, UINT, DWORD_PTR, DWORD, DWORD); - MIDIStreamer *Streamer; HMIDISTRM MidiOut; UINT DeviceID; DWORD SavedVolume; diff --git a/src/sound/music/i_music.cpp b/src/sound/music/i_music.cpp index f01724db1c..1043c2d4b1 100644 --- a/src/sound/music/i_music.cpp +++ b/src/sound/music/i_music.cpp @@ -160,7 +160,7 @@ void I_ShutdownMusic(bool onexit) } if (onexit) { - WildMidi_Shutdown(); + // free static data in the backends. TimidityPP_Shutdown(); dumb_exit(); } @@ -256,10 +256,6 @@ void MusInfo::ChangeSettingString(const char *, const char *) { } -void MusInfo::WildMidiSetOption(int opt, int set) -{ -} - FString MusInfo::GetStats() { return "No stats available for this song"; diff --git a/src/sound/music/i_music.h b/src/sound/music/i_music.h index 2d264e6d09..c78af7ec9b 100644 --- a/src/sound/music/i_music.h +++ b/src/sound/music/i_music.h @@ -82,7 +82,6 @@ public: virtual void ChangeSettingInt(const char *setting, int value); // FluidSynth settings virtual void ChangeSettingNum(const char *setting, double value); // " virtual void ChangeSettingString(const char *setting, const char *value); // " - virtual void WildMidiSetOption(int opt, int set); virtual void GMEDepthChanged(float val); void Start(bool loop, float rel_vol = -1.f, int subsong = 0); diff --git a/src/sound/music/i_musicinterns.h b/src/sound/music/i_musicinterns.h index 4b88d320b9..3231f17041 100644 --- a/src/sound/music/i_musicinterns.h +++ b/src/sound/music/i_musicinterns.h @@ -121,7 +121,6 @@ namespace TimidityPlus struct TimidityConfig { - int samplerate = 0; void (*errorfunc)(int type, int verbosity_level, const char* fmt, ...) = nullptr; TimidityPlus::SoundFontReaderInterface* reader; @@ -137,6 +136,25 @@ struct TimidityConfig 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; + class MIDIStreamer; typedef void(*MidiCallback)(void *); @@ -166,7 +184,6 @@ public: 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 void WildMidiSetOption(int opt, int set); virtual bool Preprocess(MIDIStreamer *song, bool looping); virtual FString GetStats(); virtual int GetDeviceType() const { return MDEV_DEFAULT; } @@ -176,7 +193,6 @@ public: void TimidityPP_Shutdown(); -void WildMidi_Shutdown (); // Base class for software synthesizer MIDI output devices ------------------ @@ -280,7 +296,6 @@ public: void ChangeSettingInt(const char *setting, int value) override; void ChangeSettingNum(const char *setting, double value) override; void ChangeSettingString(const char *setting, const char *value) override; - void WildMidiSetOption(int opt, int set) override; int ServiceEvent(); void SetMIDISource(MIDISource *_source); @@ -419,8 +434,11 @@ 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); -MIDIDevice *CreateWildMIDIDevice(const char *args, int samplerate); +#ifdef _WIN32 +MIDIDevice* CreateWinMIDIDevice(int mididevice); +#endif // Data interface @@ -430,6 +448,7 @@ void OPL_SetupConfig(OPLMidiConfig *config, const char *args); void OPN_SetupConfig(OpnConfig *config, const char *Args); bool GUS_SetupConfig(GUSConfig *config, const char *args); bool Timidity_SetupConfig(TimidityConfig* config, const char* args); +bool WildMidi_SetupConfig(WildMidiConfig* config, const char* args); // Module played via foo_dumb ----------------------------------------------- diff --git a/src/sound/musicformats/music_midistream.cpp b/src/sound/musicformats/music_midistream.cpp index d21238dd0e..49f16643db 100644 --- a/src/sound/musicformats/music_midistream.cpp +++ b/src/sound/musicformats/music_midistream.cpp @@ -47,9 +47,6 @@ // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- -#ifdef _WIN32 -MIDIDevice *CreateWinMIDIDevice(int mididevice); -#endif // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -239,7 +236,8 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate) break; case MDEV_WILDMIDI: - dev = CreateWildMIDIDevice(Args, samplerate); + WildMidi_SetupConfig(&wildMidiConfig, Args); + dev = CreateWildMIDIDevice(&wildMidiConfig, samplerate); break; default: @@ -303,7 +301,7 @@ void MIDIStreamer::Play(bool looping, int subsong) m_Looping = looping; source->SetMIDISubsong(subsong); devtype = SelectMIDIDevice(DeviceType); - MIDI = CreateMIDIDevice(devtype, 0); + MIDI = CreateMIDIDevice(devtype, (int)GSnd->GetOutputRate()); InitPlayback(); } @@ -596,21 +594,6 @@ void MIDIStreamer::ChangeSettingString(const char *setting, const char *value) } -//========================================================================== -// -// MIDIDeviceStreamer :: WildMidiSetOption -// -//========================================================================== - -void MIDIStreamer::WildMidiSetOption(int opt, int set) -{ - if (MIDI != NULL) - { - MIDI->WildMidiSetOption(opt, set); - } -} - - //========================================================================== // // MIDIStreamer :: OutputVolume @@ -1070,16 +1053,6 @@ void MIDIDevice::ChangeSettingString(const char *setting, const char *value) { } -//========================================================================== -// -// MIDIDevice :: WildMidiSetOption -// -//========================================================================== - -void MIDIDevice::WildMidiSetOption(int opt, int set) -{ -} - //========================================================================== // // MIDIDevice :: GetStats