mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-29 15:32:54 +00:00
- work on GUS MIDI device plus some cleanup
This is not tested yet!
This commit is contained in:
parent
b3b870d67e
commit
ed7b73d8cb
7 changed files with 284 additions and 244 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -56,72 +56,6 @@
|
|||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
// CVARS for this device - all of them require a device reset --------------------------------------------
|
||||
|
||||
static void CheckRestart()
|
||||
{
|
||||
if (currSong != nullptr && currSong->GetDeviceType() == MDEV_GUS)
|
||||
{
|
||||
MIDIDeviceChanged(-1, true);
|
||||
}
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(String, midi_config, "gzdoom", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
CheckRestart();
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Bool, midi_dmxgus, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // This was 'true' but since it requires special setup that's not such a good idea.
|
||||
{
|
||||
CheckRestart();
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(String, gus_patchdir, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
CheckRestart();
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, midi_voices, 32, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
CheckRestart();
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, gus_memsize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
CheckRestart();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Error printing override to redirect to the internal console instead of stdout.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void gzdoom_ctl_cmsg(int type, int verbosity_level, const char* fmt, ...)
|
||||
{
|
||||
if (verbosity_level >= Timidity::VERB_DEBUG) return; // Don't waste time on diagnostics.
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
FString msg;
|
||||
msg.VFormat(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Timidity::CMSG_ERROR:
|
||||
Printf(TEXTCOLOR_RED "%s\n", msg.GetChars());
|
||||
break;
|
||||
|
||||
case Timidity::CMSG_WARNING:
|
||||
Printf(TEXTCOLOR_YELLOW "%s\n", msg.GetChars());
|
||||
break;
|
||||
|
||||
case Timidity::CMSG_INFO:
|
||||
DPrintf(DMSG_SPAMMY, "%s\n", msg.GetChars());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
@ -133,8 +67,9 @@ namespace Timidity { struct Renderer; }
|
|||
|
||||
class TimidityMIDIDevice : public SoftSynthMIDIDevice
|
||||
{
|
||||
void LoadInstruments(GUSConfig *config);
|
||||
public:
|
||||
TimidityMIDIDevice(const char *args, int samplerate);
|
||||
TimidityMIDIDevice(GUSConfig *config, int samplerate);
|
||||
~TimidityMIDIDevice();
|
||||
|
||||
int Open(MidiCallback, void *userdata);
|
||||
|
@ -147,78 +82,69 @@ protected:
|
|||
void HandleEvent(int status, int parm1, int parm2);
|
||||
void HandleLongEvent(const uint8_t *data, int len);
|
||||
void ComputeOutput(float *buffer, int len);
|
||||
void LoadConfig(const char *);
|
||||
};
|
||||
|
||||
|
||||
// DATA DEFINITIONS -------------------------------------------------
|
||||
|
||||
static FString currentConfig;
|
||||
static Timidity::Instruments* instruments;
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
void TimidityMIDIDevice::LoadConfig(const char *config)
|
||||
|
||||
void TimidityMIDIDevice::LoadInstruments(GUSConfig *config)
|
||||
{
|
||||
if ((midi_dmxgus && *config == 0) || !stricmp(config, "DMXGUS"))
|
||||
if (config->dmxgus.size())
|
||||
{
|
||||
if (currentConfig.CompareNoCase("DMXGUS") == 0) return; // aleady loaded
|
||||
int lump = Wads.CheckNumForName("DMXGUS");
|
||||
if (lump == -1) lump = Wads.CheckNumForName("DMXGUSC");
|
||||
if (lump >= 0)
|
||||
// Check if we got some GUS data before using it.
|
||||
FString ultradir = getenv("ULTRADIR");
|
||||
if (ultradir.IsNotEmpty() || config->gus_patchdir.length() != 0)
|
||||
{
|
||||
auto data = Wads.OpenLumpReader(lump);
|
||||
if (data.GetLength() > 0)
|
||||
FileReader fr;
|
||||
fr.OpenMemory((const char *)config->dmxgus.data(), (long)config->dmxgus.size());
|
||||
auto psreader = new FPatchSetReader(fr);
|
||||
|
||||
// The GUS put its patches in %ULTRADIR%/MIDI so we can try that
|
||||
if (ultradir.IsNotEmpty())
|
||||
{
|
||||
// Check if we got some GUS data before using it.
|
||||
FString ultradir = getenv("ULTRADIR");
|
||||
if (ultradir.IsNotEmpty() || *(*gus_patchdir) != 0)
|
||||
{
|
||||
|
||||
auto psreader = new FPatchSetReader(data);
|
||||
|
||||
// The GUS put its patches in %ULTRADIR%/MIDI so we can try that
|
||||
if (ultradir.IsNotEmpty())
|
||||
{
|
||||
ultradir += "/midi";
|
||||
psreader->AddPath(ultradir);
|
||||
}
|
||||
if (instruments) delete instruments;
|
||||
instruments = new Timidity::Instruments(psreader);
|
||||
|
||||
// Load DMXGUS lump and patches from gus_patchdir
|
||||
if (*(*gus_patchdir) != 0) psreader->AddPath(gus_patchdir);
|
||||
|
||||
if (instruments->LoadDMXGUS(gus_memsize) < 0)
|
||||
{
|
||||
delete instruments;
|
||||
instruments = nullptr;
|
||||
I_Error("Unable to initialize instruments for GUS MIDI device");
|
||||
}
|
||||
currentConfig = "DMXGUS";
|
||||
}
|
||||
ultradir += "/midi";
|
||||
psreader->timidity_add_path(ultradir);
|
||||
}
|
||||
// Load DMXGUS lump and patches from gus_patchdir
|
||||
if (config->gus_patchdir.length() != 0) psreader->timidity_add_path(config->gus_patchdir.c_str());
|
||||
|
||||
config->instruments.reset(new Timidity::Instruments(psreader));
|
||||
bool success = config->instruments->LoadDMXGUS(config->gus_memsize) >= 0;
|
||||
|
||||
delete psreader;
|
||||
config->dmxgus.clear();
|
||||
|
||||
if (success)
|
||||
{
|
||||
config->loadedConfig = "DMXGUS";
|
||||
return;
|
||||
}
|
||||
}
|
||||
config->loadedConfig = "";
|
||||
config->instruments.reset();
|
||||
throw std::runtime_error("Unable to initialize DMXGUS for GUS MIDI device");
|
||||
}
|
||||
if (*config == 0) config = midi_config;
|
||||
if (currentConfig.CompareNoCase(config) == 0) return;
|
||||
if (instruments) delete instruments;
|
||||
instruments = nullptr;
|
||||
|
||||
auto reader = sfmanager.OpenSoundFont(config, SF_GUS | SF_SF2);
|
||||
if (reader == nullptr)
|
||||
else if (config->reader)
|
||||
{
|
||||
I_Error("GUS: %s: Unable to load sound font\n",config);
|
||||
config->loadedConfig = config->readerName;
|
||||
config->instruments.reset(new Timidity::Instruments(config->reader));
|
||||
bool err = config->instruments->LoadConfig() < 0;
|
||||
delete config->reader;
|
||||
config->reader = nullptr;
|
||||
|
||||
if (err)
|
||||
{
|
||||
config->instruments.reset();
|
||||
config->loadedConfig = "";
|
||||
throw std::runtime_error("Unable to initialize instruments for GUS MIDI device");
|
||||
}
|
||||
}
|
||||
|
||||
instruments = new Timidity::Instruments(reader);
|
||||
if (instruments->LoadConfig() < 0)
|
||||
else if (config->instruments == nullptr)
|
||||
{
|
||||
delete instruments;
|
||||
instruments = nullptr;
|
||||
I_Error("Unable to initialize instruments for GUS MIDI device");
|
||||
throw std::runtime_error("No instruments set for GUS device");
|
||||
}
|
||||
currentConfig = config;
|
||||
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -227,12 +153,11 @@ void TimidityMIDIDevice::LoadConfig(const char *config)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
TimidityMIDIDevice::TimidityMIDIDevice(const char *args, int samplerate)
|
||||
TimidityMIDIDevice::TimidityMIDIDevice(GUSConfig *config, int samplerate)
|
||||
: SoftSynthMIDIDevice(samplerate, 11025, 65535)
|
||||
{
|
||||
LoadConfig(args);
|
||||
Timidity::printMessage = gzdoom_ctl_cmsg;
|
||||
Renderer = new Timidity::Renderer((float)SampleRate, midi_voices, instruments);
|
||||
LoadInstruments(config);
|
||||
Renderer = new Timidity::Renderer((float)SampleRate, config->midi_voices, config->instruments.get());
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -328,13 +253,7 @@ void TimidityMIDIDevice::ComputeOutput(float *buffer, int len)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
MIDIDevice *CreateTimidityMIDIDevice(const char *args, int samplerate)
|
||||
MIDIDevice *CreateTimidityMIDIDevice(GUSConfig *config, int samplerate)
|
||||
{
|
||||
return new TimidityMIDIDevice(args, samplerate);
|
||||
}
|
||||
|
||||
void Timidity_Shutdown()
|
||||
{
|
||||
if (instruments) delete instruments;
|
||||
instruments = nullptr;
|
||||
return new TimidityMIDIDevice(config, samplerate);
|
||||
}
|
||||
|
|
|
@ -160,7 +160,6 @@ void I_ShutdownMusic(bool onexit)
|
|||
}
|
||||
if (onexit)
|
||||
{
|
||||
Timidity_Shutdown();
|
||||
WildMidi_Shutdown();
|
||||
TimidityPP_Shutdown();
|
||||
dumb_exit();
|
||||
|
|
|
@ -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<uint8_t> 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<uint8_t> 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<Timidity::Instruments> 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 -----------------------------------------------
|
||||
|
||||
|
|
|
@ -50,9 +50,6 @@
|
|||
#ifdef _WIN32
|
||||
MIDIDevice *CreateWinMIDIDevice(int mididevice);
|
||||
#endif
|
||||
MIDIDevice *CreateTimidityMIDIDevice(const char *args, int samplerate);
|
||||
MIDIDevice *CreateTimidityPPMIDIDevice(const char *args, int samplerate);
|
||||
MIDIDevice *CreateWildMIDIDevice(const char *args, int samplerate);
|
||||
|
||||
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
||||
|
||||
|
@ -204,17 +201,18 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate)
|
|||
switch (devtype)
|
||||
{
|
||||
case MDEV_GUS:
|
||||
dev = CreateTimidityMIDIDevice(Args, samplerate);
|
||||
GUS_SetupConfig(&gusConfig, Args);
|
||||
dev = CreateTimidityMIDIDevice(&gusConfig, samplerate);
|
||||
break;
|
||||
|
||||
case MDEV_ADL:
|
||||
SetAdlCustomBank(Args);
|
||||
ADL_SetupConfig(&adlConfig, Args);
|
||||
dev = CreateADLMIDIDevice(&adlConfig);
|
||||
break;
|
||||
|
||||
case MDEV_OPN:
|
||||
SetOpnCustomBank(Args);
|
||||
dev = CreateOPNMIDIDevice(Args);
|
||||
OPN_SetupConfig(&opnConfig, Args);
|
||||
dev = CreateOPNMIDIDevice(&opnConfig);
|
||||
break;
|
||||
|
||||
case MDEV_MMAPI:
|
||||
|
@ -226,13 +224,12 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate)
|
|||
// Intentional fall-through for non-Windows systems.
|
||||
|
||||
case MDEV_FLUIDSYNTH:
|
||||
BuildFluidPatchSetList(Args, true);
|
||||
Fluid_SetupConfig(&fluidConfig, Args, true);
|
||||
dev = CreateFluidSynthMIDIDevice(samplerate, &fluidConfig, Printf);
|
||||
break;
|
||||
|
||||
case MDEV_OPL:
|
||||
LoadGenMidi();
|
||||
oplMidiConfig.core = getOPLCore(Args);
|
||||
OPL_SetupConfig(&oplMidiConfig, Args);
|
||||
dev = CreateOplMIDIDevice(&oplMidiConfig);
|
||||
break;
|
||||
|
||||
|
|
|
@ -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 ()
|
||||
|
|
Loading…
Reference in a new issue