From 08f3a349e886766426e131303c2f16a09dbb1bf4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 27 Sep 2019 02:31:27 +0200 Subject: [PATCH] - 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 283cf41cf..f475527d7 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 f0c1c0449..5c297cf22 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 d15a6cac2..ce9b81510 100644 --- a/src/sound/mididevices/music_timidity_mididevice.cpp +++ b/src/sound/mididevices/music_timidity_mididevice.cpp @@ -60,72 +60,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; - } -} //========================================================================== // @@ -137,8 +71,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); @@ -151,78 +86,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; + } //========================================================================== @@ -231,12 +157,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()); } //========================================================================== @@ -332,13 +257,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 a25cd652f..aa66c53fd 100644 --- a/src/sound/music/i_music.cpp +++ b/src/sound/music/i_music.cpp @@ -176,7 +176,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 ab4ac488d..682928a1e 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 936915fdf..fc8c1a876 100644 --- a/src/sound/musicformats/music_midistream.cpp +++ b/src/sound/musicformats/music_midistream.cpp @@ -52,9 +52,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 ---------------------------------------------- @@ -206,17 +203,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: @@ -228,13 +226,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 87bd74dbf..ddc81211f 100644 --- a/src/sound/musicformats/music_opl.cpp +++ b/src/sound/musicformats/music_opl.cpp @@ -38,8 +38,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) { @@ -82,9 +89,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 ()