diff --git a/libraries/oplsynth/CMakeLists.txt b/libraries/oplsynth/CMakeLists.txt index 7dc736e74..043dd328c 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 8d3b758ed..a33f07afd 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/timidity/timidity.cpp b/libraries/timidity/timidity.cpp index 12055c303..cfc6f331b 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 452327fbe..cc14dba1e 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(); diff --git a/libraries/timidityplus/CMakeLists.txt b/libraries/timidityplus/CMakeLists.txt index bf386b9af..ee1bf062d 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/timidityplus/playmidi.cpp b/libraries/timidityplus/playmidi.cpp index be1f0a762..04fb2741a 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 31db58ac4..0edce9f82 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/libraries/wildmidi/CMakeLists.txt b/libraries/wildmidi/CMakeLists.txt index 97b7cf807..e5b2138df 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" ) diff --git a/src/sound/mididevices/midi_cvars.cpp b/src/sound/mididevices/midi_cvars.cpp index 4bba9b091..2bee0d1b6 100644 --- a/src/sound/mididevices/midi_cvars.cpp +++ b/src/sound/mididevices/midi_cvars.cpp @@ -38,9 +38,18 @@ #include "adlmidi.h" #include "cmdlib.h" #include "doomerrors.h" +#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" +#include "c_console.h" #ifdef _WIN32 // do this without including windows.h for this one single prototype +#ifdef _WIN32 extern "C" unsigned __stdcall GetSystemDirectoryA(char* lpBuffer, unsigned uSize); #endif // _WIN32 @@ -55,7 +64,16 @@ static void CheckRestart(int devtype) ADLConfig adlConfig; FluidConfig fluidConfig; OPLMidiConfig oplMidiConfig; +OpnConfig opnConfig; +GUSConfig gusConfig; +TimidityConfig timidityConfig; +WildMidiConfig wildMidiConfig; +//========================================================================== +// +// ADL Midi device +// +//========================================================================== CUSTOM_CVAR(Int, adl_chips_count, 6, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { @@ -89,26 +107,51 @@ 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 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; + config->adl_bank = adl_bank; + if (bank && *bank) + { + auto info = sfmanager.FindSoundFont(bank, SF_WOPL); + if (info == nullptr) + { + if (*bank >= '0' && *bank <= '9') + { + config->adl_bank = (int)strtoll(bank, nullptr, 10); + } + config->adl_custom_bank = nullptr; + } + else + { + config->adl_custom_bank = info->mFilename; + } + } +} + + CUSTOM_CVAR(Int, adl_volume_model, ADLMIDI_VolumeModel_DMX, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { adlConfig.adl_volume_model = self; 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 @@ -119,13 +162,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; @@ -134,11 +177,9 @@ 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) @@ -159,7 +200,7 @@ int BuildFluidPatchSetList(const char* patches, bool systemfallback) } if (FileExists(path)) { - fluidConfig.fluid_patchset.push_back(path.GetChars()); + config->fluid_patchset.push_back(path.GetChars()); } else { @@ -168,14 +209,15 @@ int BuildFluidPatchSetList(const char* patches, bool systemfallback) tok = strtok(NULL, delim); } free(wpatches); - if (fluidConfig.fluid_patchset.size() > 0) return 1; + if (config->fluid_patchset.size() > 0) return; + } 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. @@ -186,22 +228,21 @@ int BuildFluidPatchSetList(const char* patches, bool systemfallback) strcat(sysdir, "\\CT4MGM.SF2"); if (FileExists(sysdir)) { - fluidConfig.fluid_patchset.push_back(sysdir); - return 1; + config->fluid_patchset.push_back(sysdir); + return; } // 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) @@ -418,7 +459,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) { @@ -440,10 +485,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) @@ -451,7 +493,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. @@ -463,11 +505,414 @@ 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) + +//========================================================================== +// +// OPN MIDI device +// +//========================================================================== + + +CUSTOM_CVAR(Int, opn_chips_count, 8, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { - int current_opl_core = opl_core; - if (args != NULL && *args >= '0' && *args < '4') current_opl_core = *args - '0'; - return current_opl_core; + opnConfig.opn_chips_count = self; + CheckRestart(MDEV_OPN); } + +CUSTOM_CVAR(Int, opn_emulator_id, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + opnConfig.opn_emulator_id = self; + CheckRestart(MDEV_OPN); + +} + +CUSTOM_CVAR(Bool, opn_run_at_pcm_rate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + opnConfig.opn_run_at_pcm_rate = self; + CheckRestart(MDEV_OPN); + +} + +CUSTOM_CVAR(Bool, opn_fullpan, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + opnConfig.opn_fullpan = self; + CheckRestart(MDEV_OPN); + +} + +CUSTOM_CVAR(Bool, opn_use_custom_bank, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + CheckRestart(MDEV_OPN); + +} + +CUSTOM_CVAR(String, opn_custom_bank, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (opn_use_custom_bank) + { + CheckRestart(MDEV_OPN); + } +} + +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; + if (bank && *bank) + { + auto info = sfmanager.FindSoundFont(bank, SF_WOPN); + if (info == nullptr) + { + config->opn_custom_bank = ""; + } + else + { + config->opn_custom_bank = info->mFilename; + } + } + + int lump = Wads.CheckNumForFullName("xg.wopn"); + if (lump < 0) + { + config->default_bank.resize(0); + return; + } + FMemLump data = Wads.ReadLump(lump); + config->default_bank.resize(data.GetSize()); + memcpy(config->default_bank.data(), data.GetMem(), data.GetSize()); +} + + +//========================================================================== +// +// GUS MIDI device +// +//========================================================================== + + +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) +{ + gusConfig.gus_patchdir = self; + 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; + } +} + +// 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. +// 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; +} + + +//========================================================================== +// +// 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); +} + +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; +} + +//========================================================================== +// +// 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_adlmidi_mididevice.cpp b/src/sound/mididevices/music_adlmidi_mididevice.cpp index 01fe9a373..27090446a 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 69493ee6e..5c297cf22 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); @@ -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; @@ -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"); } @@ -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) @@ -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 66074dd94..744581d6b 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 1a08d6c72..1ca9c0435 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/mididevices/music_softsynth_mididevice.cpp b/src/sound/mididevices/music_softsynth_mididevice.cpp index af1f1251c..b1b3a22af 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_timidity_mididevice.cpp b/src/sound/mididevices/music_timidity_mididevice.cpp index c5a9749fa..e3e0a8150 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); @@ -143,82 +78,72 @@ 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); 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; + + 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; + 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; + instruments = config->instruments; } //========================================================================== @@ -227,12 +152,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, instruments.get()); } //========================================================================== @@ -328,13 +252,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/mididevices/music_timiditypp_mididevice.cpp b/src/sound/mididevices/music_timiditypp_mididevice.cpp index c07373733..1f9eb82fd 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, 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/mididevices/music_wildmidi_mididevice.cpp b/src/sound/mididevices/music_wildmidi_mididevice.cpp index ab88d2b9f..5cd261812 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 94c8a0b36..17ae00341 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 1c74f1cb0..1043c2d4b 100644 --- a/src/sound/music/i_music.cpp +++ b/src/sound/music/i_music.cpp @@ -160,8 +160,7 @@ void I_ShutdownMusic(bool onexit) } if (onexit) { - Timidity_Shutdown(); - WildMidi_Shutdown(); + // free static data in the backends. TimidityPP_Shutdown(); dumb_exit(); } @@ -257,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 2d264e6d0..c78af7ec9 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 2d0beeaf9..3231f1704 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; @@ -75,6 +74,86 @@ struct OPLMidiConfig extern OPLMidiConfig oplMidiConfig; +struct OpnConfig +{ + int opn_chips_count = 8; + int opn_emulator_id = 0; + bool opn_run_at_pcm_rate = false; + bool opn_fullpan = 1; + std::string opn_custom_bank; + std::vector default_bank; +}; + +extern OpnConfig opnConfig; + +namespace Timidity +{ + class Instruments; + class SoundFontReaderInterface; +} + +struct GUSConfig +{ + // This one is a bit more complex because it also implements the instrument cache. + int midi_voices = 32; + int gus_memsize = 0; + void (*errorfunc)(int type, int verbosity_level, const char* fmt, ...) = nullptr; + + Timidity::SoundFontReaderInterface *reader; + std::string readerName; + std::vector dmxgus; // can contain the contents of a DMXGUS lump that may be used as the instrument set. In this case gus_patchdir must point to the location of the GUS data. + std::string gus_patchdir; + + // These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time. + // Thus, this config should always be stored globally to avoid this. + // If the last loaded instrument set is to be reused or the caller wants to manage them itself, both 'reader' and 'dmxgus' fields should be left empty. + std::string loadedConfig; + std::shared_ptr instruments; // this is held both by the config and the device +}; + +extern GUSConfig gusConfig; + +namespace TimidityPlus +{ + class Instruments; + class SoundFontReaderInterface; +} + +struct TimidityConfig +{ + void (*errorfunc)(int type, int verbosity_level, const char* fmt, ...) = nullptr; + + TimidityPlus::SoundFontReaderInterface* reader; + std::string readerName; + + // These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time. + // Thus, this config should always be stored globally to avoid this. + // If the last loaded instrument set is to be reused or the caller wants to manage them itself, 'reader' should be left empty. + std::string loadedConfig; + std::shared_ptr instruments; // this is held both by the config and the device + +}; + +extern TimidityConfig timidityConfig; + +struct WildMidiConfig +{ + bool reverb = false; + bool enhanced_resampling = true; + void (*errorfunc)(const char* wmfmt, va_list args) = nullptr; + + WildMidi::SoundFontReaderInterface* reader; + std::string readerName; + + // These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time. + // Thus, this config should always be stored globally to avoid this. + // If the last loaded instrument set is to be reused or the caller wants to manage them itself, 'reader' should be left empty. + std::string loadedConfig; + std::shared_ptr instruments; // this is held both by the config and the device + +}; + +extern WildMidiConfig wildMidiConfig; class MIDIStreamer; @@ -105,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; } @@ -114,9 +192,7 @@ public: -void Timidity_Shutdown(); void TimidityPP_Shutdown(); -void WildMidi_Shutdown (); // Base class for software synthesizer MIDI output devices ------------------ @@ -220,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); @@ -313,7 +388,7 @@ public: void Play (bool looping, int subsong); bool IsPlaying (); bool IsValid () const; - void ResetChips (); + void ChangeSettingInt (const char *, int) override; protected: @@ -351,11 +426,29 @@ public: CDDAFile (FileReader &reader); }; +// MIDI devices + +MIDIDevice *CreateFluidSynthMIDIDevice(int samplerate, const FluidConfig* config, int (*printfunc)(const char*, ...)); +MIDIDevice *CreateADLMIDIDevice(const ADLConfig* config); +MIDIDevice *CreateOPNMIDIDevice(const OpnConfig *args); +MIDIDevice *CreateOplMIDIDevice(const OPLMidiConfig* config); +MIDIDevice *CreateTimidityMIDIDevice(GUSConfig *config, int samplerate); +MIDIDevice *CreateTimidityPPMIDIDevice(TimidityConfig *config, int samplerate); +MIDIDevice *CreateWildMIDIDevice(WildMidiConfig *config, int samplerate); + +#ifdef _WIN32 +MIDIDevice* CreateWinMIDIDevice(int mididevice); +#endif + // Data interface -int BuildFluidPatchSetList(const char* patches, bool systemfallback); -void LoadGenMidi(); -int getOPLCore(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); +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 75a345e28..49f16643d 100644 --- a/src/sound/musicformats/music_midistream.cpp +++ b/src/sound/musicformats/music_midistream.cpp @@ -47,17 +47,6 @@ // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- -#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 ---------------------------------------------- @@ -209,15 +198,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: - dev = CreateADLMIDIDevice(Args, &adlConfig); + ADL_SetupConfig(&adlConfig, Args); + dev = CreateADLMIDIDevice(&adlConfig); break; case MDEV_OPN: - dev = CreateOPNMIDIDevice(Args); + OPN_SetupConfig(&opnConfig, Args); + dev = CreateOPNMIDIDevice(&opnConfig); break; case MDEV_MMAPI: @@ -229,22 +221,23 @@ 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; case MDEV_TIMIDITY: - dev = CreateTimidityPPMIDIDevice(Args, samplerate); + Timidity_SetupConfig(&timidityConfig, Args); + dev = CreateTimidityPPMIDIDevice(&timidityConfig, samplerate); break; case MDEV_WILDMIDI: - dev = CreateWildMIDIDevice(Args, samplerate); + WildMidi_SetupConfig(&wildMidiConfig, Args); + dev = CreateWildMIDIDevice(&wildMidiConfig, samplerate); break; default: @@ -308,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(); } @@ -601,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 @@ -1075,16 +1053,6 @@ void MIDIDevice::ChangeSettingString(const char *setting, const char *value) { } -//========================================================================== -// -// MIDIDevice :: WildMidiSetOption -// -//========================================================================== - -void MIDIDevice::WildMidiSetOption(int opt, int set) -{ -} - //========================================================================== // // MIDIDevice :: GetStats diff --git a/src/sound/musicformats/music_opl.cpp b/src/sound/musicformats/music_opl.cpp index f501c9bf6..065fc1c8c 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 ()