- Timidity++ done.

This commit is contained in:
Christoph Oelckers 2019-09-27 22:19:00 +02:00
parent 9aecabc887
commit 890db1fbf9
7 changed files with 272 additions and 235 deletions

View file

@ -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<std::mutex> lock(CvarCritSec);
std::lock_guard<std::mutex> lock(ConfigMutex);
if (last_reverb_setting != timidity_reverb)
{

View file

@ -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;

View file

@ -41,6 +41,9 @@
#include "timidity/timidity.h"
#include "timidity/playmidi.h"
#include "timidity/instrum.h"
#include "timiditypp/controls.h"
#include "timiditypp/timidity.h"
#include "timiditypp/instrum.h"
#include "v_text.h"
// do this without including windows.h for this one single prototype
@ -61,6 +64,13 @@ FluidConfig fluidConfig;
OPLMidiConfig oplMidiConfig;
OpnConfig opnConfig;
GUSConfig gusConfig;
TimidityConfig timidityConfig;
//==========================================================================
//
// ADL Midi device
//
//==========================================================================
CUSTOM_CVAR(Int, adl_chips_count, 6, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
@ -132,6 +142,13 @@ CUSTOM_CVAR(Int, adl_volume_model, ADLMIDI_VolumeModel_DMX, CVAR_ARCHIVE | CVAR_
CheckRestart(MDEV_ADL);
}
//==========================================================================
//
// Fluidsynth MIDI device
//
//==========================================================================
#define FLUID_CHORUS_MOD_SINE 0
#define FLUID_CHORUS_MOD_TRIANGLE 1
#define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE
@ -439,7 +456,11 @@ CUSTOM_CVAR(Int, fluid_chorus_type, FLUID_CHORUS_DEFAULT_TYPE, CVAR_ARCHIVE|CVAR
}
//==========================================================================
//
// OPL MIDI device
//
//==========================================================================
CUSTOM_CVAR(Int, opl_numchips, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
@ -487,6 +508,13 @@ void OPL_SetupConfig(OPLMidiConfig *config, const char *args)
}
//==========================================================================
//
// OPN MIDI device
//
//==========================================================================
CUSTOM_CVAR(Int, opn_chips_count, 8, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
opnConfig.opn_chips_count = self;
@ -557,8 +585,11 @@ void OPN_SetupConfig(OpnConfig *config, const char *Args)
}
// CVARS for this device - all of them require a device reset --------------------------------------------
//==========================================================================
//
// GUS MIDI device
//
//==========================================================================
CUSTOM_CVAR(String, midi_config, "gzdoom", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
@ -621,6 +652,12 @@ static void gus_printfunc(int type, int verbosity_level, const char* fmt, ...)
}
}
// make sure we can use the above function for the Timidity++ device as well.
static_assert(Timidity::CMSG_ERROR == TimidityPlus::CMSG_ERROR, "Timidity constant mismatch");
static_assert(Timidity::CMSG_WARNING == TimidityPlus::CMSG_WARNING, "Timidity constant mismatch");
static_assert(Timidity::CMSG_INFO == TimidityPlus::CMSG_INFO, "Timidity constant mismatch");
static_assert(Timidity::VERB_DEBUG == TimidityPlus::VERB_DEBUG, "Timidity constant mismatch");
//==========================================================================
//
// Sets up the date to load the instruments for the GUS device.
@ -662,3 +699,167 @@ bool GUS_SetupConfig(GUSConfig *config, const char *args)
return true;
}
//==========================================================================
//
// CVar interface to configurable parameters
//
// Timidity++ uses a static global set of configuration variables
// THese can be changed while the synth is playing but need synchronization.
//
// Currently the synth is not fully reentrant due to this and a handful
// of other global variables.
//
//==========================================================================
template<class T> void ChangeVarSync(T& var, T value)
{
std::lock_guard<std::mutex> lock(TimidityPlus::ConfigMutex);
var = value;
}
CUSTOM_CVAR(Bool, timidity_modulation_wheel, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
ChangeVarSync(TimidityPlus::timidity_modulation_wheel, *self);
}
CUSTOM_CVAR(Bool, timidity_portamento, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
ChangeVarSync(TimidityPlus::timidity_portamento, *self);
}
/*
* reverb=0 no reverb 0
* reverb=1 old reverb 1
* reverb=1,n set reverb level to n (-1 to -127)
* reverb=2 "global" old reverb 2
* reverb=2,n set reverb level to n (-1 to -127) - 128
* reverb=3 new reverb 3
* reverb=3,n set reverb level to n (-1 to -127) - 256
* reverb=4 "global" new reverb 4
* reverb=4,n set reverb level to n (-1 to -127) - 384
*/
EXTERN_CVAR(Int, timidity_reverb_level)
EXTERN_CVAR(Int, timidity_reverb)
static void SetReverb()
{
int value = 0;
int mode = timidity_reverb;
int level = timidity_reverb_level;
if (mode == 0 || level == 0) value = mode;
else value = (mode - 1) * -128 - level;
ChangeVarSync(TimidityPlus::timidity_reverb, value);
}
CUSTOM_CVAR(Int, timidity_reverb, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 0 || self > 4) self = 0;
else SetReverb();
}
CUSTOM_CVAR(Int, timidity_reverb_level, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 0 || self > 127) self = 0;
else SetReverb();
}
CUSTOM_CVAR(Int, timidity_chorus, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
ChangeVarSync(TimidityPlus::timidity_chorus, *self);
}
CUSTOM_CVAR(Bool, timidity_surround_chorus, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
ChangeVarSync(TimidityPlus::timidity_surround_chorus, *self);
CheckRestart(MDEV_TIMIDITY);
}
CUSTOM_CVAR(Bool, timidity_channel_pressure, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
ChangeVarSync(TimidityPlus::timidity_channel_pressure, *self);
}
CUSTOM_CVAR(Int, timidity_lpf_def, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
ChangeVarSync(TimidityPlus::timidity_lpf_def, *self);
}
CUSTOM_CVAR(Bool, timidity_temper_control, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
ChangeVarSync(TimidityPlus::timidity_temper_control, *self);
}
CUSTOM_CVAR(Bool, timidity_modulation_envelope, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
ChangeVarSync(TimidityPlus::timidity_modulation_envelope, *self);
CheckRestart(MDEV_TIMIDITY);
}
CUSTOM_CVAR(Bool, timidity_overlap_voice_allow, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
ChangeVarSync(TimidityPlus::timidity_overlap_voice_allow, *self);
}
CUSTOM_CVAR(Bool, timidity_drum_effect, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
ChangeVarSync(TimidityPlus::timidity_drum_effect, *self);
}
CUSTOM_CVAR(Bool, timidity_pan_delay, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
ChangeVarSync(TimidityPlus::timidity_pan_delay, *self);
}
CUSTOM_CVAR(Float, timidity_drum_power, 1.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) /* coef. of drum amplitude */
{
if (self < 0) self = 0;
else if (self > MAX_AMPLIFICATION / 100.f) self = MAX_AMPLIFICATION / 100.f;
ChangeVarSync(TimidityPlus::timidity_drum_power, *self);
}
CUSTOM_CVAR(Int, timidity_key_adjust, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < -24) self = -24;
else if (self > 24) self = 24;
ChangeVarSync(TimidityPlus::timidity_key_adjust, *self);
}
// For testing mainly.
CUSTOM_CVAR(Float, timidity_tempo_adjust, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 0.25) self = 0.25;
else if (self > 10) self = 10;
ChangeVarSync(TimidityPlus::timidity_tempo_adjust, *self);
}
CUSTOM_CVAR(Float, min_sustain_time, 5000, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 0) self = 0;
ChangeVarSync(TimidityPlus::min_sustain_time, *self);
}
// Config file to use
CUSTOM_CVAR(String, timidity_config, "gzdoom", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
CheckRestart(MDEV_TIMIDITY);
}
CVAR(Int, timidity_frequency, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
bool Timidity_SetupConfig(TimidityConfig* config, const char* args)
{
config->errorfunc = gus_printfunc;
if (*args == 0) args = timidity_config;
if (stricmp(config->loadedConfig.c_str(), args) == 0) return false; // aleady loaded
auto reader = sfmanager.OpenSoundFont(args, SF_GUS | SF_SF2);
if (reader == nullptr)
{
char error[80];
snprintf(error, 80, "Timidity++: %s: Unable to load sound font\n", args);
throw std::runtime_error(error);
}
config->reader = reader;
config->readerName = args;
return true;
}

View file

@ -78,6 +78,7 @@ public:
protected:
Timidity::Renderer *Renderer;
std::shared_ptr<Timidity::Instruments> instruments; // The device needs to hold a reference to this while the renderer is in use.
void HandleEvent(int status, int parm1, int parm2);
void HandleLongEvent(const uint8_t *data, int len);
@ -142,7 +143,7 @@ void TimidityMIDIDevice::LoadInstruments(GUSConfig *config)
{
throw std::runtime_error("No instruments set for GUS device");
}
instruments = config->instruments;
}
//==========================================================================
@ -155,7 +156,7 @@ TimidityMIDIDevice::TimidityMIDIDevice(GUSConfig *config, int samplerate)
: SoftSynthMIDIDevice(samplerate, 11025, 65535)
{
LoadInstruments(config);
Renderer = new Timidity::Renderer((float)SampleRate, config->midi_voices, config->instruments.get());
Renderer = new Timidity::Renderer((float)SampleRate, config->midi_voices, instruments.get());
}
//==========================================================================

View file

@ -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<class T> void ChangeVarSync(T& var, T value)
{
std::lock_guard<std::mutex> 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<TimidityPlus::Instruments> instruments;
public:
TimidityPPMIDIDevice(const char *args, int samplerate);
TimidityPPMIDIDevice(TimidityConfig *config, int samplerate);
~TimidityPPMIDIDevice();
int Open(MidiCallback, void *userdata);
void PrecacheInstruments(const uint16_t *instruments, int count);
//FString GetStats();
int GetDeviceType() const override { return MDEV_TIMIDITY; }
static void ClearInstruments()
{
if (instruments != nullptr) delete instruments;
instruments = nullptr;
}
double test[3] = { 0, 0, 0 };
@ -241,64 +66,50 @@ protected:
void HandleEvent(int status, int parm1, int parm2);
void HandleLongEvent(const uint8_t *data, int len);
void ComputeOutput(float *buffer, int len);
void LoadInstruments(TimidityConfig* config);
};
TimidityPlus::Instruments *TimidityPPMIDIDevice::instruments;
FString TimidityPPMIDIDevice::configName;
// Config file to use
CUSTOM_CVAR(String, timidity_config, "gzdoom", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
//==========================================================================
//
//
//
//==========================================================================
void TimidityPPMIDIDevice::LoadInstruments(TimidityConfig* config)
{
if (currSong != nullptr && currSong->GetDeviceType() == MDEV_TIMIDITY)
if (config->reader)
{
MIDIDeviceChanged(-1, true);
config->loadedConfig = config->readerName;
config->instruments.reset(new TimidityPlus::Instruments());
bool success = config->instruments->load(config->reader);
config->reader = nullptr;
if (!success)
{
config->instruments.reset();
config->loadedConfig = "";
throw std::runtime_error("Unable to initialize instruments for Timidity++ MIDI device");
}
}
else if (config->instruments == nullptr)
{
throw std::runtime_error("No instruments set for Timidity++ device");
}
instruments = config->instruments;
}
CVAR (Int, timidity_frequency, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
//==========================================================================
//
// TimidityPPMIDIDevice Constructor
//
//==========================================================================
TimidityPPMIDIDevice::TimidityPPMIDIDevice(const char *args, int samplerate)
:SoftSynthMIDIDevice(samplerate <= 0? timidity_frequency : samplerate, 4000, 65000)
TimidityPPMIDIDevice::TimidityPPMIDIDevice(TimidityConfig *config, int samplerate)
:SoftSynthMIDIDevice(samplerate <= 0? config->samplerate : samplerate, 4000, 65000)
{
if (args == NULL || *args == 0) args = timidity_config;
Renderer = nullptr;
if (instruments != nullptr && configName.CompareNoCase(args)) // Only load instruments if they have changed from the last played song.
{
delete instruments;
instruments = nullptr;
}
TimidityPlus::printMessage = gzdoom_ctl_cmsg;
TimidityPlus::set_playback_rate(SampleRate);
if (instruments == nullptr)
{
auto sfreader = sfmanager.OpenSoundFont(args, SF_SF2 | SF_GUS);
if (sfreader != nullptr)
{
instruments = new TimidityPlus::Instruments;
if (!instruments->load(sfreader))
{
delete instruments;
instruments = nullptr;
}
}
}
if (instruments != nullptr)
{
Renderer = new TimidityPlus::Player(instruments);
}
else
{
I_Error("Failed to load any MIDI patches");
}
sampletime = 0;
LoadInstruments(config);
Renderer = new TimidityPlus::Player(instruments.get());
}
//==========================================================================
@ -329,8 +140,6 @@ int TimidityPPMIDIDevice::Open(MidiCallback callback, void *userdata)
{
Renderer->playmidi_stream_init();
}
// No instruments loaded means we cannot play...
if (instruments == nullptr) return 0;
return ret;
}
@ -393,14 +202,14 @@ void TimidityPPMIDIDevice::ComputeOutput(float *buffer, int len)
//
//==========================================================================
MIDIDevice *CreateTimidityPPMIDIDevice(const char *args, int samplerate)
MIDIDevice *CreateTimidityPPMIDIDevice(TimidityConfig* config, int samplerate)
{
return new TimidityPPMIDIDevice(args, samplerate);
return new TimidityPPMIDIDevice(config, samplerate);
}
void TimidityPP_Shutdown()
{
TimidityPPMIDIDevice::ClearInstruments();
TimidityPlus::free_gauss_table();
TimidityPlus::free_global_mblock();
}

View file

@ -113,6 +113,30 @@ struct GUSConfig
extern GUSConfig gusConfig;
namespace TimidityPlus
{
class Instruments;
class SoundFontReaderInterface;
}
struct TimidityConfig
{
int samplerate = 0;
void (*errorfunc)(int type, int verbosity_level, const char* fmt, ...) = nullptr;
TimidityPlus::SoundFontReaderInterface* reader;
std::string readerName;
// These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time.
// Thus, this config should always be stored globally to avoid this.
// If the last loaded instrument set is to be reused or the caller wants to manage them itself, 'reader' should be left empty.
std::string loadedConfig;
std::shared_ptr<TimidityPlus::Instruments> instruments; // this is held both by the config and the device
};
extern TimidityConfig timidityConfig;
class MIDIStreamer;
typedef void(*MidiCallback)(void *);
@ -394,8 +418,8 @@ MIDIDevice *CreateADLMIDIDevice(const ADLConfig* config);
MIDIDevice *CreateOPNMIDIDevice(const OpnConfig *args);
MIDIDevice *CreateOplMIDIDevice(const OPLMidiConfig* config);
MIDIDevice *CreateTimidityMIDIDevice(GUSConfig *config, int samplerate);
MIDIDevice *CreateTimidityPPMIDIDevice(TimidityConfig *config, int samplerate);
MIDIDevice *CreateTimidityPPMIDIDevice(const char *args, int samplerate);
MIDIDevice *CreateWildMIDIDevice(const char *args, int samplerate);
// Data interface
@ -405,6 +429,7 @@ void ADL_SetupConfig(ADLConfig *config, const char *Args);
void OPL_SetupConfig(OPLMidiConfig *config, const char *args);
void OPN_SetupConfig(OpnConfig *config, const char *Args);
bool GUS_SetupConfig(GUSConfig *config, const char *args);
bool Timidity_SetupConfig(TimidityConfig* config, const char* args);
// Module played via foo_dumb -----------------------------------------------

View file

@ -234,7 +234,8 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate)
break;
case MDEV_TIMIDITY:
dev = CreateTimidityPPMIDIDevice(Args, samplerate);
Timidity_SetupConfig(&timidityConfig, Args);
dev = CreateTimidityPPMIDIDevice(&timidityConfig, samplerate);
break;
case MDEV_WILDMIDI: