Further improved DOSBox patches:

- definitions of newly added options moved to a separate file src/mt32options.h to facilitate merge
- types of many options changed to more appropriate
- introduced two options mt32.chunk and mt32.prebuffer to control rendering in threading mode
- fixed memory leak when the synth fails to open
This commit is contained in:
sergm
2014-12-02 21:53:11 +02:00
parent c58ebaadfe
commit 2c92f9a5a2
2 changed files with 240 additions and 202 deletions

View File

@@ -1,6 +1,6 @@
commit c941deb824acd8c5f40e420ed077015f0d3f38e8
commit 826a8c8b41a9c37a1de358321636510027a28469
Author: sergm <sergm@bigmir.net>
Date: Tue Dec 2 09:18:57 2014 +0200
Date: Tue Dec 2 21:43:35 2014 +0200
mt32emu patch
@@ -15,7 +15,7 @@ index 184469e..286638d 100644
-
+LIBS += -lmt32emu
diff --git a/src/dosbox.cpp b/src/dosbox.cpp
index a4bf564..6d7f1e8 100644
index a4bf564..ad65fe6 100644
--- a/src/dosbox.cpp
+++ b/src/dosbox.cpp
@@ -467,7 +467,7 @@ void DOSBOX_Init(void) {
@@ -27,91 +27,11 @@ index a4bf564..6d7f1e8 100644
Pstring = secprop->Add_string("mpu401",Property::Changeable::WhenIdle,"intelligent");
Pstring->Set_values(mputypes);
Pstring->Set_help("Type of MPU-401 to emulate.");
@@ -480,6 +480,88 @@ void DOSBOX_Init(void) {
@@ -480,6 +480,8 @@ void DOSBOX_Init(void) {
Pstring->Set_help("Special configuration options for the device driver. This is usually the id of the device you want to use.\n"
" See the README/Manual for more details.");
+ Pstring = secprop->Add_string("mt32.romdir",Property::Changeable::WhenIdle,"");
+ Pstring->Set_help("Name of the directory where MT-32 Control and PCM ROM files can be found. Emulation requires these files to work.\n"
+ " Accepted file names are as follows:\n"
+ " MT32_CONTROL.ROM or CM32L_CONTROL.ROM - control ROM file.\n"
+ " MT32_PCM.ROM or CM32L_PCM.ROM - PCM ROM file.\n");
+
+ const char *mt32ReverseStereo[] = {"off", "on",0};
+ Pstring = secprop->Add_string("mt32.reverse.stereo",Property::Changeable::WhenIdle,"off");
+ Pstring->Set_values(mt32ReverseStereo);
+ Pstring->Set_help("Reverse stereo channels for MT-32 output");
+
+ const char *mt32log[] = {"off", "on",0};
+ Pstring = secprop->Add_string("mt32.verbose",Property::Changeable::WhenIdle,"off");
+ Pstring->Set_values(mt32log);
+ Pstring->Set_help("MT-32 debug logging");
+
+ const char *mt32thread[] = {"off", "on",0};
+ Pstring = secprop->Add_string("mt32.thread",Property::Changeable::WhenIdle,"off");
+ Pstring->Set_values(mt32thread);
+ Pstring->Set_help("MT-32 rendering in separate thread");
+
+ const char *mt32DACModes[] = {"0", "1", "2", "3", "auto",0};
+ Pstring = secprop->Add_string("mt32.dac",Property::Changeable::WhenIdle,"auto");
+ Pstring->Set_values(mt32DACModes);
+ Pstring->Set_help("MT-32 DAC input emulation mode\n"
+ "Nice = 0 - default\n"
+ "Produces samples at double the volume, without tricks.\n"
+ "Higher quality than the real devices\n\n"
+
+ "Pure = 1\n"
+ "Produces samples that exactly match the bits output from the emulated LA32.\n"
+ "Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)\n"
+ "Much less likely to overdrive than any other mode.\n"
+ "Half the volume of any of the other modes.\n"
+ "Perfect for developers while debugging :)\n\n"
+
+ "GENERATION1 = 2\n"
+ "Re-orders the LA32 output bits as in early generation MT-32s (according to Wikipedia).\n"
+ "Bit order at DAC (where each number represents the original LA32 output bit number, and XX means the bit is always low):\n"
+ "15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 XX\n\n"
+
+ "GENERATION2 = 3\n"
+ "Re-orders the LA32 output bits as in later generations (personally confirmed on my CM-32L - KG).\n"
+ "Bit order at DAC (where each number represents the original LA32 output bit number):\n"
+ "15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 14\n");
+
+ const char *mt32analogModes[] = {"0", "1", "2", "3", "auto",0};
+ Pstring = secprop->Add_string("mt32.analog",Property::Changeable::WhenIdle,"auto");
+ Pstring->Set_values(mt32analogModes);
+ Pstring->Set_help("MT-32 analogue output emulation mode\n"
+ "Digital = 0\n"
+ "Only digital path is emulated. The output samples correspond to the digital output signal appeared at the DAC entrance.\n"
+ "Fastest mode.\n\n"
+
+ "Coarse = 1\n"
+ "Coarse emulation of LPF circuit. High frequencies are boosted, sample rate remains unchanged.\n"
+ "A bit better sounding but also a bit slower.\n\n"
+
+ "Accurate = 2 - default\n"
+ "Finer emulation of LPF circuit. Output signal is upsampled to 48 kHz to allow emulation of audible mirror spectra above 16 kHz,\n"
+ "which is passed through the LPF circuit without significant attenuation.\n"
+ "Sounding is closer to the analog output from real hardware but also slower than the modes 0 and 1.\n\n"
+
+ "Oversampled = 3\n"
+ "Same as the default mode 2 but the output signal is 2x oversampled, i.e. the output sample rate is 96 kHz.\n"
+ "Even slower than all the other modes but better retains highest frequencies while further resampled in DOSBox mixer.\n");
+
+ const char *mt32reverbModes[] = {"0", "1", "2", "3", "auto",0};
+ Pstring = secprop->Add_string("mt32.reverb.mode",Property::Changeable::WhenIdle,"auto");
+ Pstring->Set_values(mt32reverbModes);
+ Pstring->Set_help("MT-32 reverb mode");
+
+ const char *mt32reverbTimes[] = {"0", "1", "2", "3", "4", "5", "6", "7",0};
+ Pint = secprop->Add_int("mt32.reverb.time",Property::Changeable::WhenIdle,5);
+ Pint->Set_values(mt32reverbTimes);
+ Pint->Set_help("MT-32 reverb decaying time");
+
+ const char *mt32reverbLevels[] = {"0", "1", "2", "3", "4", "5", "6", "7",0};
+ Pint = secprop->Add_int("mt32.reverb.level",Property::Changeable::WhenIdle,3);
+ Pint->Set_values(mt32reverbLevels);
+ Pint->Set_help("MT-32 reverb level");
+#include "mt32options.h"
+
#if C_DEBUG
secprop=control->AddSection_prop("debug",&DEBUG_Init);
@@ -145,10 +65,10 @@ index 61d15e5..0ec8ebc 100644
Bitu cmd_len;
diff --git a/src/gui/midi_mt32.cpp b/src/gui/midi_mt32.cpp
new file mode 100644
index 0000000..902651e
index 0000000..82bcc96
--- /dev/null
+++ b/src/gui/midi_mt32.cpp
@@ -0,0 +1,247 @@
@@ -0,0 +1,248 @@
+#include <SDL_thread.h>
+#include <SDL_timer.h>
+#include <SDL_endian.h>
@@ -161,8 +81,6 @@ index 0000000..902651e
+#include "midi_mt32.h"
+
+static const Bitu MILLIS_PER_SECOND = 1000;
+static const Bitu MINIMUM_RENDER_MILLIS = 16;
+static const Bitu AUDIO_LATENCY_MILLIS = 2 * MINIMUM_RENDER_MILLIS;
+
+MidiHandler_mt32 &MidiHandler_mt32::GetInstance() {
+ static MidiHandler_mt32 midiHandler_mt32;
@@ -227,13 +145,12 @@ index 0000000..902651e
+ const MT32Emu::ROMImage *controlROMImage = MT32Emu::ROMImage::makeROMImage(&controlROMFile);
+ const MT32Emu::ROMImage *pcmROMImage = MT32Emu::ROMImage::makeROMImage(&pcmROMFile);
+
+ MT32Emu::AnalogOutputMode analogOutputMode = MT32Emu::AnalogOutputMode_ACCURATE;
+ if (strcmp(section->Get_string("mt32.analog"), "auto") != 0) {
+ analogOutputMode = (MT32Emu::AnalogOutputMode)atoi(section->Get_string("mt32.analog"));
+ }
+ MT32Emu::AnalogOutputMode analogOutputMode = (MT32Emu::AnalogOutputMode)section->Get_int("mt32.analog");
+
+ synth = new MT32Emu::Synth(&reportHandler);
+ if (!synth->open(*controlROMImage, *pcmROMImage, analogOutputMode)) {
+ delete synth;
+ synth = NULL;
+ LOG_MSG("MT32: Error initialising emulation");
+ return false;
+ }
@@ -249,13 +166,11 @@ index 0000000..902651e
+ synth->setReverbOverridden(true);
+ }
+
+ if (strcmp(section->Get_string("mt32.dac"), "auto") != 0) {
+ synth->setDACInputMode((MT32Emu::DACInputMode)atoi(section->Get_string("mt32.dac")));
+ }
+ synth->setDACInputMode((MT32Emu::DACInputMode)section->Get_int("mt32.dac"));
+
+ synth->setReversedStereoEnabled(strcmp(section->Get_string("mt32.reverse.stereo"), "on") == 0);
+ noise = strcmp(section->Get_string("mt32.verbose"), "on") == 0;
+ renderInThread = strcmp(section->Get_string("mt32.thread"), "on") == 0;
+ synth->setReversedStereoEnabled(section->Get_bool("mt32.reverse.stereo"));
+ noise = section->Get_bool("mt32.verbose");
+ renderInThread = section->Get_bool("mt32.thread");
+
+ if (noise) LOG_MSG("MT32: Adding mixer channel at sample rate %d", synth->getStereoOutputSampleRate());
+ chan = MIXER_AddChannel(mixerCallBack, synth->getStereoOutputSampleRate(), "MT32");
@@ -264,8 +179,14 @@ index 0000000..902651e
+ renderPos = 0;
+ playPos = 0;
+ sampleRateRatio = MT32Emu::SAMPLE_RATE / (double)synth->getStereoOutputSampleRate();
+ minimumRenderFrames = (MINIMUM_RENDER_MILLIS * synth->getStereoOutputSampleRate()) / MILLIS_PER_SECOND;
+ framesPerAudioBuffer = (AUDIO_LATENCY_MILLIS * synth->getStereoOutputSampleRate()) / MILLIS_PER_SECOND;
+ int chunkSize = section->Get_int("mt32.chunk");
+ minimumRenderFrames = (chunkSize * synth->getStereoOutputSampleRate()) / MILLIS_PER_SECOND;
+ int latency = section->Get_int("mt32.prebuffer");
+ if (latency <= chunkSize) {
+ latency = 2 * chunkSize;
+ LOG_MSG("MT32: chunk length must be less than prebuffer length, prebuffer length reset to %i ms.", latency);
+ }
+ framesPerAudioBuffer = (latency * synth->getStereoOutputSampleRate()) / MILLIS_PER_SECOND;
+ audioBufferSize = framesPerAudioBuffer << 1;
+ audioBuffer = new Bit16s[audioBufferSize];
+ render(framesPerAudioBuffer - 1, audioBuffer);
@@ -456,3 +377,101 @@ index 0000000..45b20cd
+};
+
+#endif /* DOSBOX_MIDI_MT32_H */
diff --git a/src/mt32options.h b/src/mt32options.h
new file mode 100644
index 0000000..4d67069
--- /dev/null
+++ b/src/mt32options.h
@@ -0,0 +1,92 @@
+Pstring = secprop->Add_string("mt32.romdir",Property::Changeable::WhenIdle,"");
+Pstring->Set_help("Name of the directory where MT-32 Control and PCM ROM files can be found. Emulation requires these files to work.\n"
+ " Accepted file names are as follows:\n"
+ " MT32_CONTROL.ROM or CM32L_CONTROL.ROM - control ROM file.\n"
+ " MT32_PCM.ROM or CM32L_PCM.ROM - PCM ROM file.\n");
+
+Pbool = secprop->Add_bool("mt32.reverse.stereo",Property::Changeable::WhenIdle,false);
+Pbool->Set_help("Reverse stereo channels for MT-32 output");
+
+Pbool = secprop->Add_bool("mt32.verbose",Property::Changeable::WhenIdle,false);
+Pbool->Set_help("MT-32 debug logging");
+
+Pbool = secprop->Add_bool("mt32.thread",Property::Changeable::WhenIdle,false);
+Pbool->Set_help("MT-32 rendering in separate thread");
+
+const char *mt32chunk[] = {"2", "3", "16", "99", "100",0};
+Pint = secprop->Add_int("mt32.chunk",Property::Changeable::WhenIdle,16);
+Pint->Set_values(mt32chunk);
+Pint->SetMinMax(2,100);
+Pint->Set_help("Minimum milliseconds of data to render at once.\n"
+ "Increasing this value reduces rendering overhead which may improve performance but also increases audio lag.\n"
+ "Valid for rendering in separate thread only.");
+
+const char *mt32prebuffer[] = {"3", "4", "32", "199", "200",0};
+Pint = secprop->Add_int("mt32.prebuffer",Property::Changeable::WhenIdle,32);
+Pint->Set_values(mt32prebuffer);
+Pint->SetMinMax(3,200);
+Pint->Set_help("How many milliseconds of data to render ahead.\n"
+ "Increasing this value may help to avoid underruns but also increases audio lag.\n"
+ "Cannot be set less than or equal to mt32.chunk value.\n"
+ "Valid for rendering in separate thread only.");
+
+const char *mt32DACModes[] = {"0", "1", "2", "3",0};
+Pint = secprop->Add_int("mt32.dac",Property::Changeable::WhenIdle,0);
+Pint->Set_values(mt32DACModes);
+Pint->Set_help("MT-32 DAC input emulation mode\n"
+ "Nice = 0 - default\n"
+ "Produces samples at double the volume, without tricks.\n"
+ "Higher quality than the real devices\n\n"
+
+ "Pure = 1\n"
+ "Produces samples that exactly match the bits output from the emulated LA32.\n"
+ "Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)\n"
+ "Much less likely to overdrive than any other mode.\n"
+ "Half the volume of any of the other modes.\n"
+ "Perfect for developers while debugging :)\n\n"
+
+ "GENERATION1 = 2\n"
+ "Re-orders the LA32 output bits as in early generation MT-32s (according to Wikipedia).\n"
+ "Bit order at DAC (where each number represents the original LA32 output bit number, and XX means the bit is always low):\n"
+ "15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 XX\n\n"
+
+ "GENERATION2 = 3\n"
+ "Re-orders the LA32 output bits as in later generations (personally confirmed on my CM-32L - KG).\n"
+ "Bit order at DAC (where each number represents the original LA32 output bit number):\n"
+ "15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 14");
+
+const char *mt32analogModes[] = {"0", "1", "2", "3",0};
+Pint = secprop->Add_int("mt32.analog",Property::Changeable::WhenIdle,2);
+Pint->Set_values(mt32analogModes);
+Pint->Set_help("MT-32 analogue output emulation mode\n"
+ "Digital = 0\n"
+ "Only digital path is emulated. The output samples correspond to the digital output signal appeared at the DAC entrance.\n"
+ "Fastest mode.\n\n"
+
+ "Coarse = 1\n"
+ "Coarse emulation of LPF circuit. High frequencies are boosted, sample rate remains unchanged.\n"
+ "A bit better sounding but also a bit slower.\n\n"
+
+ "Accurate = 2 - default\n"
+ "Finer emulation of LPF circuit. Output signal is upsampled to 48 kHz to allow emulation of audible mirror spectra above 16 kHz,\n"
+ "which is passed through the LPF circuit without significant attenuation.\n"
+ "Sounding is closer to the analog output from real hardware but also slower than the modes 0 and 1.\n\n"
+
+ "Oversampled = 3\n"
+ "Same as the default mode 2 but the output signal is 2x oversampled, i.e. the output sample rate is 96 kHz.\n"
+ "Even slower than all the other modes but better retains highest frequencies while further resampled in DOSBox mixer.");
+
+const char *mt32reverbModes[] = {"0", "1", "2", "3", "auto",0};
+Pstring = secprop->Add_string("mt32.reverb.mode",Property::Changeable::WhenIdle,"auto");
+Pstring->Set_values(mt32reverbModes);
+Pstring->Set_help("MT-32 reverb mode");
+
+const char *mt32reverbTimes[] = {"0", "1", "2", "3", "4", "5", "6", "7",0};
+Pint = secprop->Add_int("mt32.reverb.time",Property::Changeable::WhenIdle,5);
+Pint->Set_values(mt32reverbTimes);
+Pint->Set_help("MT-32 reverb decaying time");
+
+const char *mt32reverbLevels[] = {"0", "1", "2", "3", "4", "5", "6", "7",0};
+Pint = secprop->Add_int("mt32.reverb.level",Property::Changeable::WhenIdle,3);
+Pint->Set_values(mt32reverbLevels);
+Pint->Set_help("MT-32 reverb level");

View File

@@ -1,6 +1,6 @@
commit 3dbce2eee06d542ac32bb7e11529286dd8c62af6
commit 09287d2a7aece2abe44d5a35161d48b881aa5bc9
Author: sergm <sergm@bigmir.net>
Date: Tue Dec 2 09:19:32 2014 +0200
Date: Tue Dec 2 21:46:05 2014 +0200
mt32emu patch
@@ -15,7 +15,7 @@ index 184469e..286638d 100644
-
+LIBS += -lmt32emu
diff --git a/src/dosbox.cpp b/src/dosbox.cpp
index 6aad419..9a3f96c 100644
index 6aad419..9fec99a 100644
--- a/src/dosbox.cpp
+++ b/src/dosbox.cpp
@@ -483,7 +483,7 @@ void DOSBOX_Init(void) {
@@ -27,91 +27,11 @@ index 6aad419..9a3f96c 100644
Pstring = secprop->Add_string("mpu401",Property::Changeable::WhenIdle,"intelligent");
Pstring->Set_values(mputypes);
Pstring->Set_help("Type of MPU-401 to emulate.");
@@ -499,6 +499,88 @@ void DOSBOX_Init(void) {
@@ -499,6 +499,8 @@ void DOSBOX_Init(void) {
" In that case, add 'delaysysex', for example: midiconfig=2 delaysysex\n"
" See the README/Manual for more details.");
+ Pstring = secprop->Add_string("mt32.romdir",Property::Changeable::WhenIdle,"");
+ Pstring->Set_help("Name of the directory where MT-32 Control and PCM ROM files can be found. Emulation requires these files to work.\n"
+ " Accepted file names are as follows:\n"
+ " MT32_CONTROL.ROM or CM32L_CONTROL.ROM - control ROM file.\n"
+ " MT32_PCM.ROM or CM32L_PCM.ROM - PCM ROM file.\n");
+
+ const char *mt32ReverseStereo[] = {"off", "on",0};
+ Pstring = secprop->Add_string("mt32.reverse.stereo",Property::Changeable::WhenIdle,"off");
+ Pstring->Set_values(mt32ReverseStereo);
+ Pstring->Set_help("Reverse stereo channels for MT-32 output");
+
+ const char *mt32log[] = {"off", "on",0};
+ Pstring = secprop->Add_string("mt32.verbose",Property::Changeable::WhenIdle,"off");
+ Pstring->Set_values(mt32log);
+ Pstring->Set_help("MT-32 debug logging");
+
+ const char *mt32thread[] = {"off", "on",0};
+ Pstring = secprop->Add_string("mt32.thread",Property::Changeable::WhenIdle,"off");
+ Pstring->Set_values(mt32thread);
+ Pstring->Set_help("MT-32 rendering in separate thread");
+
+ const char *mt32DACModes[] = {"0", "1", "2", "3", "auto",0};
+ Pstring = secprop->Add_string("mt32.dac",Property::Changeable::WhenIdle,"auto");
+ Pstring->Set_values(mt32DACModes);
+ Pstring->Set_help("MT-32 DAC input emulation mode\n"
+ "Nice = 0 - default\n"
+ "Produces samples at double the volume, without tricks.\n"
+ "Higher quality than the real devices\n\n"
+
+ "Pure = 1\n"
+ "Produces samples that exactly match the bits output from the emulated LA32.\n"
+ "Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)\n"
+ "Much less likely to overdrive than any other mode.\n"
+ "Half the volume of any of the other modes.\n"
+ "Perfect for developers while debugging :)\n\n"
+
+ "GENERATION1 = 2\n"
+ "Re-orders the LA32 output bits as in early generation MT-32s (according to Wikipedia).\n"
+ "Bit order at DAC (where each number represents the original LA32 output bit number, and XX means the bit is always low):\n"
+ "15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 XX\n\n"
+
+ "GENERATION2 = 3\n"
+ "Re-orders the LA32 output bits as in later generations (personally confirmed on my CM-32L - KG).\n"
+ "Bit order at DAC (where each number represents the original LA32 output bit number):\n"
+ "15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 14\n");
+
+ const char *mt32analogModes[] = {"0", "1", "2", "3", "auto",0};
+ Pstring = secprop->Add_string("mt32.analog",Property::Changeable::WhenIdle,"auto");
+ Pstring->Set_values(mt32analogModes);
+ Pstring->Set_help("MT-32 analogue output emulation mode\n"
+ "Digital = 0\n"
+ "Only digital path is emulated. The output samples correspond to the digital output signal appeared at the DAC entrance.\n"
+ "Fastest mode.\n\n"
+
+ "Coarse = 1\n"
+ "Coarse emulation of LPF circuit. High frequencies are boosted, sample rate remains unchanged.\n"
+ "A bit better sounding but also a bit slower.\n\n"
+
+ "Accurate = 2 - default\n"
+ "Finer emulation of LPF circuit. Output signal is upsampled to 48 kHz to allow emulation of audible mirror spectra above 16 kHz,\n"
+ "which is passed through the LPF circuit without significant attenuation.\n"
+ "Sounding is closer to the analog output from real hardware but also slower than the modes 0 and 1.\n\n"
+
+ "Oversampled = 3\n"
+ "Same as the default mode 2 but the output signal is 2x oversampled, i.e. the output sample rate is 96 kHz.\n"
+ "Even slower than all the other modes but better retains highest frequencies while further resampled in DOSBox mixer.\n");
+
+ const char *mt32reverbModes[] = {"0", "1", "2", "3", "auto",0};
+ Pstring = secprop->Add_string("mt32.reverb.mode",Property::Changeable::WhenIdle,"auto");
+ Pstring->Set_values(mt32reverbModes);
+ Pstring->Set_help("MT-32 reverb mode");
+
+ const char *mt32reverbTimes[] = {"0", "1", "2", "3", "4", "5", "6", "7",0};
+ Pint = secprop->Add_int("mt32.reverb.time",Property::Changeable::WhenIdle,5);
+ Pint->Set_values(mt32reverbTimes);
+ Pint->Set_help("MT-32 reverb decaying time");
+
+ const char *mt32reverbLevels[] = {"0", "1", "2", "3", "4", "5", "6", "7",0};
+ Pint = secprop->Add_int("mt32.reverb.level",Property::Changeable::WhenIdle,3);
+ Pint->Set_values(mt32reverbLevels);
+ Pint->Set_help("MT-32 reverb level");
+#include "mt32options.h"
+
#if C_DEBUG
secprop=control->AddSection_prop("debug",&DEBUG_Init);
@@ -143,10 +63,10 @@ index ce4647f..3d289b8 100644
void MIDI_RawOutByte(Bit8u data) {
diff --git a/src/gui/midi_mt32.cpp b/src/gui/midi_mt32.cpp
new file mode 100644
index 0000000..902651e
index 0000000..82bcc96
--- /dev/null
+++ b/src/gui/midi_mt32.cpp
@@ -0,0 +1,247 @@
@@ -0,0 +1,248 @@
+#include <SDL_thread.h>
+#include <SDL_timer.h>
+#include <SDL_endian.h>
@@ -159,8 +79,6 @@ index 0000000..902651e
+#include "midi_mt32.h"
+
+static const Bitu MILLIS_PER_SECOND = 1000;
+static const Bitu MINIMUM_RENDER_MILLIS = 16;
+static const Bitu AUDIO_LATENCY_MILLIS = 2 * MINIMUM_RENDER_MILLIS;
+
+MidiHandler_mt32 &MidiHandler_mt32::GetInstance() {
+ static MidiHandler_mt32 midiHandler_mt32;
@@ -225,13 +143,12 @@ index 0000000..902651e
+ const MT32Emu::ROMImage *controlROMImage = MT32Emu::ROMImage::makeROMImage(&controlROMFile);
+ const MT32Emu::ROMImage *pcmROMImage = MT32Emu::ROMImage::makeROMImage(&pcmROMFile);
+
+ MT32Emu::AnalogOutputMode analogOutputMode = MT32Emu::AnalogOutputMode_ACCURATE;
+ if (strcmp(section->Get_string("mt32.analog"), "auto") != 0) {
+ analogOutputMode = (MT32Emu::AnalogOutputMode)atoi(section->Get_string("mt32.analog"));
+ }
+ MT32Emu::AnalogOutputMode analogOutputMode = (MT32Emu::AnalogOutputMode)section->Get_int("mt32.analog");
+
+ synth = new MT32Emu::Synth(&reportHandler);
+ if (!synth->open(*controlROMImage, *pcmROMImage, analogOutputMode)) {
+ delete synth;
+ synth = NULL;
+ LOG_MSG("MT32: Error initialising emulation");
+ return false;
+ }
@@ -247,13 +164,11 @@ index 0000000..902651e
+ synth->setReverbOverridden(true);
+ }
+
+ if (strcmp(section->Get_string("mt32.dac"), "auto") != 0) {
+ synth->setDACInputMode((MT32Emu::DACInputMode)atoi(section->Get_string("mt32.dac")));
+ }
+ synth->setDACInputMode((MT32Emu::DACInputMode)section->Get_int("mt32.dac"));
+
+ synth->setReversedStereoEnabled(strcmp(section->Get_string("mt32.reverse.stereo"), "on") == 0);
+ noise = strcmp(section->Get_string("mt32.verbose"), "on") == 0;
+ renderInThread = strcmp(section->Get_string("mt32.thread"), "on") == 0;
+ synth->setReversedStereoEnabled(section->Get_bool("mt32.reverse.stereo"));
+ noise = section->Get_bool("mt32.verbose");
+ renderInThread = section->Get_bool("mt32.thread");
+
+ if (noise) LOG_MSG("MT32: Adding mixer channel at sample rate %d", synth->getStereoOutputSampleRate());
+ chan = MIXER_AddChannel(mixerCallBack, synth->getStereoOutputSampleRate(), "MT32");
@@ -262,8 +177,14 @@ index 0000000..902651e
+ renderPos = 0;
+ playPos = 0;
+ sampleRateRatio = MT32Emu::SAMPLE_RATE / (double)synth->getStereoOutputSampleRate();
+ minimumRenderFrames = (MINIMUM_RENDER_MILLIS * synth->getStereoOutputSampleRate()) / MILLIS_PER_SECOND;
+ framesPerAudioBuffer = (AUDIO_LATENCY_MILLIS * synth->getStereoOutputSampleRate()) / MILLIS_PER_SECOND;
+ int chunkSize = section->Get_int("mt32.chunk");
+ minimumRenderFrames = (chunkSize * synth->getStereoOutputSampleRate()) / MILLIS_PER_SECOND;
+ int latency = section->Get_int("mt32.prebuffer");
+ if (latency <= chunkSize) {
+ latency = 2 * chunkSize;
+ LOG_MSG("MT32: chunk length must be less than prebuffer length, prebuffer length reset to %i ms.", latency);
+ }
+ framesPerAudioBuffer = (latency * synth->getStereoOutputSampleRate()) / MILLIS_PER_SECOND;
+ audioBufferSize = framesPerAudioBuffer << 1;
+ audioBuffer = new Bit16s[audioBufferSize];
+ render(framesPerAudioBuffer - 1, audioBuffer);
@@ -454,3 +375,101 @@ index 0000000..45b20cd
+};
+
+#endif /* DOSBOX_MIDI_MT32_H */
diff --git a/src/mt32options.h b/src/mt32options.h
new file mode 100644
index 0000000..aebf042
--- /dev/null
+++ b/src/mt32options.h
@@ -0,0 +1,92 @@
+Pstring = secprop->Add_string("mt32.romdir",Property::Changeable::WhenIdle,"");
+Pstring->Set_help("Name of the directory where MT-32 Control and PCM ROM files can be found. Emulation requires these files to work.\n"
+ " Accepted file names are as follows:\n"
+ " MT32_CONTROL.ROM or CM32L_CONTROL.ROM - control ROM file.\n"
+ " MT32_PCM.ROM or CM32L_PCM.ROM - PCM ROM file.");
+
+Pbool = secprop->Add_bool("mt32.reverse.stereo",Property::Changeable::WhenIdle,false);
+Pbool->Set_help("Reverse stereo channels for MT-32 output");
+
+Pbool = secprop->Add_bool("mt32.verbose",Property::Changeable::WhenIdle,false);
+Pbool->Set_help("MT-32 debug logging");
+
+Pbool = secprop->Add_bool("mt32.thread",Property::Changeable::WhenIdle,false);
+Pbool->Set_help("MT-32 rendering in separate thread");
+
+const char *mt32chunk[] = {"2", "3", "16", "99", "100",0};
+Pint = secprop->Add_int("mt32.chunk",Property::Changeable::WhenIdle,16);
+Pint->Set_values(mt32chunk);
+Pint->SetMinMax(2,100);
+Pint->Set_help("Minimum milliseconds of data to render at once.\n"
+ "Increasing this value reduces rendering overhead which may improve performance but also increases audio lag.\n"
+ "Valid for rendering in separate thread only.");
+
+const char *mt32prebuffer[] = {"3", "4", "32", "199", "200",0};
+Pint = secprop->Add_int("mt32.prebuffer",Property::Changeable::WhenIdle,32);
+Pint->Set_values(mt32prebuffer);
+Pint->SetMinMax(3,200);
+Pint->Set_help("How many milliseconds of data to render ahead.\n"
+ "Increasing this value may help to avoid underruns but also increases audio lag.\n"
+ "Cannot be set less than or equal to mt32.chunk value.\n"
+ "Valid for rendering in separate thread only.");
+
+const char *mt32DACModes[] = {"0", "1", "2", "3",0};
+Pint = secprop->Add_int("mt32.dac",Property::Changeable::WhenIdle,0);
+Pint->Set_values(mt32DACModes);
+Pint->Set_help("MT-32 DAC input emulation mode\n"
+ "Nice = 0 - default\n"
+ "Produces samples at double the volume, without tricks.\n"
+ "Higher quality than the real devices\n\n"
+
+ "Pure = 1\n"
+ "Produces samples that exactly match the bits output from the emulated LA32.\n"
+ "Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)\n"
+ "Much less likely to overdrive than any other mode.\n"
+ "Half the volume of any of the other modes.\n"
+ "Perfect for developers while debugging :)\n\n"
+
+ "GENERATION1 = 2\n"
+ "Re-orders the LA32 output bits as in early generation MT-32s (according to Wikipedia).\n"
+ "Bit order at DAC (where each number represents the original LA32 output bit number, and XX means the bit is always low):\n"
+ "15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 XX\n\n"
+
+ "GENERATION2 = 3\n"
+ "Re-orders the LA32 output bits as in later generations (personally confirmed on my CM-32L - KG).\n"
+ "Bit order at DAC (where each number represents the original LA32 output bit number):\n"
+ "15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 14");
+
+const char *mt32analogModes[] = {"0", "1", "2", "3",0};
+Pint = secprop->Add_int("mt32.analog",Property::Changeable::WhenIdle,2);
+Pint->Set_values(mt32analogModes);
+Pint->Set_help("MT-32 analogue output emulation mode\n"
+ "Digital = 0\n"
+ "Only digital path is emulated. The output samples correspond to the digital output signal appeared at the DAC entrance.\n"
+ "Fastest mode.\n\n"
+
+ "Coarse = 1\n"
+ "Coarse emulation of LPF circuit. High frequencies are boosted, sample rate remains unchanged.\n"
+ "A bit better sounding but also a bit slower.\n\n"
+
+ "Accurate = 2 - default\n"
+ "Finer emulation of LPF circuit. Output signal is upsampled to 48 kHz to allow emulation of audible mirror spectra above 16 kHz,\n"
+ "which is passed through the LPF circuit without significant attenuation.\n"
+ "Sounding is closer to the analog output from real hardware but also slower than the modes 0 and 1.\n\n"
+
+ "Oversampled = 3\n"
+ "Same as the default mode 2 but the output signal is 2x oversampled, i.e. the output sample rate is 96 kHz.\n"
+ "Even slower than all the other modes but better retains highest frequencies while further resampled in DOSBox mixer.");
+
+const char *mt32reverbModes[] = {"0", "1", "2", "3", "auto",0};
+Pstring = secprop->Add_string("mt32.reverb.mode",Property::Changeable::WhenIdle,"auto");
+Pstring->Set_values(mt32reverbModes);
+Pstring->Set_help("MT-32 reverb mode");
+
+const char *mt32reverbTimes[] = {"0", "1", "2", "3", "4", "5", "6", "7",0};
+Pint = secprop->Add_int("mt32.reverb.time",Property::Changeable::WhenIdle,5);
+Pint->Set_values(mt32reverbTimes);
+Pint->Set_help("MT-32 reverb decaying time");
+
+const char *mt32reverbLevels[] = {"0", "1", "2", "3", "4", "5", "6", "7",0};
+Pint = secprop->Add_int("mt32.reverb.level",Property::Changeable::WhenIdle,3);
+Pint->Set_values(mt32reverbLevels);
+Pint->Set_help("MT-32 reverb level");