[Scummvm-cvs-logs] scummvm master -> e34b5be8e3d4842e273f08821b6a7bd7ba65e843

bluegr bluegr at gmail.com
Sun Dec 21 21:20:43 CET 2014


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
e34b5be8e3 MT32: Update to munt 1.5.0


Commit: e34b5be8e3d4842e273f08821b6a7bd7ba65e843
    https://github.com/scummvm/scummvm/commit/e34b5be8e3d4842e273f08821b6a7bd7ba65e843
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2014-12-21T22:19:28+02:00

Commit Message:
MT32: Update to munt 1.5.0

This syncs with munt commit 4041a16a5d

Changed paths:
  A audio/softsynth/mt32/Analog.cpp
  A audio/softsynth/mt32/Analog.h
  A audio/softsynth/mt32/MemoryRegion.h
  A audio/softsynth/mt32/MidiEventQueue.h
  A audio/softsynth/mt32/Types.h
  A audio/softsynth/mt32/internals.h
    NEWS
    audio/softsynth/mt32/BReverbModel.cpp
    audio/softsynth/mt32/BReverbModel.h
    audio/softsynth/mt32/LA32FloatWaveGenerator.cpp
    audio/softsynth/mt32/LA32Ramp.cpp
    audio/softsynth/mt32/LA32WaveGenerator.cpp
    audio/softsynth/mt32/Part.cpp
    audio/softsynth/mt32/Partial.cpp
    audio/softsynth/mt32/PartialManager.cpp
    audio/softsynth/mt32/Poly.cpp
    audio/softsynth/mt32/Poly.h
    audio/softsynth/mt32/ROMInfo.cpp
    audio/softsynth/mt32/ROMInfo.h
    audio/softsynth/mt32/Structures.h
    audio/softsynth/mt32/Synth.cpp
    audio/softsynth/mt32/Synth.h
    audio/softsynth/mt32/TVA.cpp
    audio/softsynth/mt32/TVF.cpp
    audio/softsynth/mt32/TVP.cpp
    audio/softsynth/mt32/Tables.cpp
    audio/softsynth/mt32/Tables.h
    audio/softsynth/mt32/module.mk
    audio/softsynth/mt32/mt32emu.h



diff --git a/NEWS b/NEWS
index 762af70..470ddc0 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,9 @@ For a more comprehensive changelog of the latest experimental code, see:
  New Games:
    - Added support for Sfinx.
 
+ General:
+   - Updated Munt MT-32 emulation code to version 1.5.0.
+
  Broken Sword 1:
    - Fix speech endianness detection on big endian systems for the mac
      version (bug #6720).
diff --git a/audio/softsynth/mt32/Analog.cpp b/audio/softsynth/mt32/Analog.cpp
new file mode 100644
index 0000000..8ac28e4
--- /dev/null
+++ b/audio/softsynth/mt32/Analog.cpp
@@ -0,0 +1,348 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 2.1 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//#include <cstring>
+#include "Analog.h"
+
+namespace MT32Emu {
+
+#if MT32EMU_USE_FLOAT_SAMPLES
+
+/* FIR approximation of the overall impulse response of the cascade composed of the sample & hold circuit and the low pass filter
+ * of the MT-32 first generation.
+ * The coefficients below are found by windowing the inverse DFT of the 1024 pin frequency response converted to the minimum phase.
+ * The frequency response of the LPF is computed directly, the effect of the S&H is approximated by multiplying the LPF frequency
+ * response by the corresponding sinc. Although, the LPF has DC gain of 3.2, we ignore this in the emulation and use normalised model.
+ * The peak gain of the normalised cascade appears about 1.7 near 11.8 kHz. Relative error doesn't exceed 1% for the frequencies
+ * below 12.5 kHz. In the higher frequency range, the relative error is below 8%. Peak error value is at 16 kHz.
+ */
+static const float COARSE_LPF_TAPS_MT32[] = {
+	1.272473681f, -0.220267785f, -0.158039905f, 0.179603785f, -0.111484097f, 0.054137498f, -0.023518029f, 0.010997169f, -0.006935698f
+};
+
+// Similar approximation for new MT-32 and CM-32L/LAPC-I LPF. As the voltage controlled amplifier was introduced, LPF has unity DC gain.
+// The peak gain value shifted towards higher frequencies and a bit higher about 1.83 near 13 kHz.
+static const float COARSE_LPF_TAPS_CM32L[] = {
+	1.340615635f, -0.403331694f, 0.036005517f, 0.066156844f, -0.069672532f, 0.049563806f, -0.031113416f, 0.019169774f, -0.012421368f
+};
+
+#else
+
+static const unsigned int COARSE_LPF_FRACTION_BITS = 14;
+
+// Integer versions of the FIRs above multiplied by (1 << 14) and rounded.
+static const SampleEx COARSE_LPF_TAPS_MT32[] = {
+	20848, -3609, -2589, 2943, -1827, 887, -385, 180, -114
+};
+
+static const SampleEx COARSE_LPF_TAPS_CM32L[] = {
+	21965, -6608, 590, 1084, -1142, 812, -510, 314, -204
+};
+
+#endif
+
+/* Combined FIR that both approximates the impulse response of the analogue circuits of sample & hold and the low pass filter
+ * in the audible frequency range (below 20 kHz) and attenuates unwanted mirror spectra above 28 kHz as well. It is a polyphase
+ * filter intended for resampling the signal to 48 kHz yet for applying high frequency boost.
+ * As with the filter above, the analogue LPF frequency response is obtained for 1536 pin grid for range up to 96 kHz and multiplied
+ * by the corresponding sinc. The result is further squared, windowed and passed to generalised Parks-McClellan routine as a desired response.
+ * Finally, the minimum phase factor is found that's essentially the coefficients below.
+ * Relative error in the audible frequency range doesn't exceed 0.0006%, attenuation in the stopband is better than 100 dB.
+ * This level of performance makes it nearly bit-accurate for standard 16-bit sample resolution.
+ */
+
+// FIR version for MT-32 first generation.
+static const float ACCURATE_LPF_TAPS_MT32[] = {
+	0.003429281f, 0.025929869f, 0.096587777f, 0.228884848f, 0.372413431f, 0.412386503f, 0.263980018f,
+	-0.014504962f, -0.237394528f, -0.257043496f, -0.103436603f, 0.063996095f, 0.124562333f, 0.083703206f,
+	0.013921662f, -0.033475018f, -0.046239712f, -0.029310921f, 0.00126585f, 0.021060961f, 0.017925605f,
+	0.003559874f, -0.005105248f, -0.005647917f, -0.004157918f, -0.002065664f, 0.00158747f, 0.003762585f,
+	0.001867137f, -0.001090028f, -0.001433979f, -0.00022367f, 4.34308E-05f, -0.000247827f, 0.000157087f,
+	0.000605823f, 0.000197317f, -0.000370511f, -0.000261202f, 9.96069E-05f, 9.85073E-05f, -5.28754E-05f,
+	-1.00912E-05f, 7.69943E-05f, 2.03162E-05f, -5.67967E-05f, -3.30637E-05f, 1.61958E-05f, 1.73041E-05f
+};
+
+// FIR version for new MT-32 and CM-32L/LAPC-I.
+static const float ACCURATE_LPF_TAPS_CM32L[] = {
+	0.003917452f, 0.030693861f, 0.116424199f, 0.275101674f, 0.43217361f, 0.431247894f, 0.183255659f,
+	-0.174955671f, -0.354240244f, -0.212401714f, 0.072259178f, 0.204655344f, 0.108336211f, -0.039099027f,
+	-0.075138174f, -0.026261906f, 0.00582663f, 0.003052193f, 0.00613657f, 0.017017951f, 0.008732535f,
+	-0.011027427f, -0.012933664f, 0.001158097f, 0.006765958f, 0.00046778f, -0.002191106f, 0.001561017f,
+	0.001842871f, -0.001996876f, -0.002315836f, 0.000980965f, 0.001817454f, -0.000243272f, -0.000972848f,
+	0.000149941f, 0.000498886f, -0.000204436f, -0.000347415f, 0.000142386f, 0.000249137f, -4.32946E-05f,
+	-0.000131231f, 3.88575E-07f, 4.48813E-05f, -1.31906E-06f, -1.03499E-05f, 7.71971E-06f, 2.86721E-06f
+};
+
+// According to the CM-64 PCB schematic, there is a difference in the values of the LPF entrance resistors for the reverb and non-reverb channels.
+// This effectively results in non-unity LPF DC gain for the reverb channel of 0.68 while the LPF has unity DC gain for the LA32 output channels.
+// In emulation, the reverb output gain is multiplied by this factor to compensate for the LPF gain difference.
+static const float CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR = 0.68f;
+
+static const unsigned int OUTPUT_GAIN_FRACTION_BITS = 8;
+static const float OUTPUT_GAIN_MULTIPLIER = float(1 << OUTPUT_GAIN_FRACTION_BITS);
+
+static const unsigned int COARSE_LPF_DELAY_LINE_LENGTH = 8; // Must be a power of 2
+static const unsigned int ACCURATE_LPF_DELAY_LINE_LENGTH = 16; // Must be a power of 2
+static const unsigned int ACCURATE_LPF_NUMBER_OF_PHASES = 3; // Upsampling factor
+static const unsigned int ACCURATE_LPF_PHASE_INCREMENT_REGULAR = 2; // Downsampling factor
+static const unsigned int ACCURATE_LPF_PHASE_INCREMENT_OVERSAMPLED = 1; // No downsampling
+static const Bit32u ACCURATE_LPF_DELTAS_REGULAR[][ACCURATE_LPF_NUMBER_OF_PHASES] = { { 0, 0, 0 }, { 1, 1, 0 }, { 1, 2, 1 } };
+static const Bit32u ACCURATE_LPF_DELTAS_OVERSAMPLED[][ACCURATE_LPF_NUMBER_OF_PHASES] = { { 0, 0, 0 }, { 1, 0, 0 }, { 1, 0, 1 } };
+
+class AbstractLowPassFilter {
+public:
+	static AbstractLowPassFilter &createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF);
+	static void muteRingBuffer(SampleEx *ringBuffer, unsigned int length);
+
+	virtual ~AbstractLowPassFilter() {}
+	virtual SampleEx process(SampleEx sample) = 0;
+	virtual bool hasNextSample() const;
+	virtual unsigned int getOutputSampleRate() const;
+	virtual unsigned int estimateInSampleCount(unsigned int outSamples) const;
+	virtual void addPositionIncrement(unsigned int) {}
+};
+
+class NullLowPassFilter : public AbstractLowPassFilter {
+public:
+	SampleEx process(SampleEx sample);
+};
+
+class CoarseLowPassFilter : public AbstractLowPassFilter {
+private:
+	const SampleEx * const LPF_TAPS;
+	SampleEx ringBuffer[COARSE_LPF_DELAY_LINE_LENGTH];
+	unsigned int ringBufferPosition;
+
+public:
+	CoarseLowPassFilter(bool oldMT32AnalogLPF);
+	SampleEx process(SampleEx sample);
+};
+
+class AccurateLowPassFilter : public AbstractLowPassFilter {
+private:
+	const float * const LPF_TAPS;
+	const Bit32u (* const deltas)[ACCURATE_LPF_NUMBER_OF_PHASES];
+	const unsigned int phaseIncrement;
+	const unsigned int outputSampleRate;
+
+	SampleEx ringBuffer[ACCURATE_LPF_DELAY_LINE_LENGTH];
+	unsigned int ringBufferPosition;
+	unsigned int phase;
+
+public:
+	AccurateLowPassFilter(bool oldMT32AnalogLPF, bool oversample);
+	SampleEx process(SampleEx sample);
+	bool hasNextSample() const;
+	unsigned int getOutputSampleRate() const;
+	unsigned int estimateInSampleCount(unsigned int outSamples) const;
+	void addPositionIncrement(unsigned int positionIncrement);
+};
+
+Analog::Analog(const AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures) :
+	leftChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())),
+	rightChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())),
+	synthGain(0),
+	reverbGain(0)
+{}
+
+Analog::~Analog() {
+	delete &leftChannelLPF;
+	delete &rightChannelLPF;
+}
+
+void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, Bit32u outLength) {
+	if (outStream == NULL) {
+		leftChannelLPF.addPositionIncrement(outLength);
+		rightChannelLPF.addPositionIncrement(outLength);
+		return;
+	}
+
+	while (0 < (outLength--)) {
+		SampleEx outSampleL;
+		SampleEx outSampleR;
+
+		if (leftChannelLPF.hasNextSample()) {
+			outSampleL = leftChannelLPF.process(0);
+			outSampleR = rightChannelLPF.process(0);
+		} else {
+			SampleEx inSampleL = ((SampleEx)*(nonReverbLeft++) + (SampleEx)*(reverbDryLeft++)) * synthGain + (SampleEx)*(reverbWetLeft++) * reverbGain;
+			SampleEx inSampleR = ((SampleEx)*(nonReverbRight++) + (SampleEx)*(reverbDryRight++)) * synthGain + (SampleEx)*(reverbWetRight++) * reverbGain;
+
+#if !MT32EMU_USE_FLOAT_SAMPLES
+			inSampleL >>= OUTPUT_GAIN_FRACTION_BITS;
+			inSampleR >>= OUTPUT_GAIN_FRACTION_BITS;
+#endif
+
+			outSampleL = leftChannelLPF.process(inSampleL);
+			outSampleR = rightChannelLPF.process(inSampleR);
+		}
+
+		*((*outStream)++) = Synth::clipSampleEx(outSampleL);
+		*((*outStream)++) = Synth::clipSampleEx(outSampleR);
+	}
+}
+
+unsigned int Analog::getOutputSampleRate() const {
+	return leftChannelLPF.getOutputSampleRate();
+}
+
+Bit32u Analog::getDACStreamsLength(Bit32u outputLength) const {
+	return leftChannelLPF.estimateInSampleCount(outputLength);
+}
+
+void Analog::setSynthOutputGain(float useSynthGain) {
+#if MT32EMU_USE_FLOAT_SAMPLES
+	synthGain = useSynthGain;
+#else
+	if (OUTPUT_GAIN_MULTIPLIER < useSynthGain) useSynthGain = OUTPUT_GAIN_MULTIPLIER;
+	synthGain = SampleEx(useSynthGain * OUTPUT_GAIN_MULTIPLIER);
+#endif
+}
+
+void Analog::setReverbOutputGain(float useReverbGain, bool mt32ReverbCompatibilityMode) {
+	if (!mt32ReverbCompatibilityMode) useReverbGain *= CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR;
+#if MT32EMU_USE_FLOAT_SAMPLES
+	reverbGain = useReverbGain;
+#else
+	if (OUTPUT_GAIN_MULTIPLIER < useReverbGain) useReverbGain = OUTPUT_GAIN_MULTIPLIER;
+	reverbGain = SampleEx(useReverbGain * OUTPUT_GAIN_MULTIPLIER);
+#endif
+}
+
+AbstractLowPassFilter &AbstractLowPassFilter::createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF) {
+	switch (mode) {
+		case AnalogOutputMode_COARSE:
+			return *new CoarseLowPassFilter(oldMT32AnalogLPF);
+		case AnalogOutputMode_ACCURATE:
+			return *new AccurateLowPassFilter(oldMT32AnalogLPF, false);
+		case AnalogOutputMode_OVERSAMPLED:
+			return *new AccurateLowPassFilter(oldMT32AnalogLPF, true);
+		default:
+			return *new NullLowPassFilter;
+	}
+}
+
+void AbstractLowPassFilter::muteRingBuffer(SampleEx *ringBuffer, unsigned int length) {
+
+#if MT32EMU_USE_FLOAT_SAMPLES
+
+	SampleEx *p = ringBuffer;
+	while (length--) {
+		*(p++) = 0.0f;
+	}
+
+#else
+
+	memset(ringBuffer, 0, length * sizeof(SampleEx));
+
+#endif
+
+}
+
+bool AbstractLowPassFilter::hasNextSample() const {
+	return false;
+}
+
+unsigned int AbstractLowPassFilter::getOutputSampleRate() const {
+	return SAMPLE_RATE;
+}
+
+unsigned int AbstractLowPassFilter::estimateInSampleCount(unsigned int outSamples) const {
+	return outSamples;
+}
+
+SampleEx NullLowPassFilter::process(const SampleEx inSample) {
+	return inSample;
+}
+
+CoarseLowPassFilter::CoarseLowPassFilter(bool oldMT32AnalogLPF) :
+	LPF_TAPS(oldMT32AnalogLPF ? COARSE_LPF_TAPS_MT32 : COARSE_LPF_TAPS_CM32L),
+	ringBufferPosition(0)
+{
+	muteRingBuffer(ringBuffer, COARSE_LPF_DELAY_LINE_LENGTH);
+}
+
+SampleEx CoarseLowPassFilter::process(const SampleEx inSample) {
+	static const unsigned int DELAY_LINE_MASK = COARSE_LPF_DELAY_LINE_LENGTH - 1;
+
+	SampleEx sample = LPF_TAPS[COARSE_LPF_DELAY_LINE_LENGTH] * ringBuffer[ringBufferPosition];
+	ringBuffer[ringBufferPosition] = Synth::clipSampleEx(inSample);
+
+	for (unsigned int i = 0; i < COARSE_LPF_DELAY_LINE_LENGTH; i++) {
+		sample += LPF_TAPS[i] * ringBuffer[(i + ringBufferPosition) & DELAY_LINE_MASK];
+	}
+
+	ringBufferPosition = (ringBufferPosition - 1) & DELAY_LINE_MASK;
+
+#if !MT32EMU_USE_FLOAT_SAMPLES
+	sample >>= COARSE_LPF_FRACTION_BITS;
+#endif
+
+	return sample;
+}
+
+AccurateLowPassFilter::AccurateLowPassFilter(const bool oldMT32AnalogLPF, const bool oversample) :
+	LPF_TAPS(oldMT32AnalogLPF ? ACCURATE_LPF_TAPS_MT32 : ACCURATE_LPF_TAPS_CM32L),
+	deltas(oversample ? ACCURATE_LPF_DELTAS_OVERSAMPLED : ACCURATE_LPF_DELTAS_REGULAR),
+	phaseIncrement(oversample ? ACCURATE_LPF_PHASE_INCREMENT_OVERSAMPLED : ACCURATE_LPF_PHASE_INCREMENT_REGULAR),
+	outputSampleRate(SAMPLE_RATE * ACCURATE_LPF_NUMBER_OF_PHASES / phaseIncrement),
+	ringBufferPosition(0),
+	phase(0)
+{
+	muteRingBuffer(ringBuffer, ACCURATE_LPF_DELAY_LINE_LENGTH);
+}
+
+SampleEx AccurateLowPassFilter::process(const SampleEx inSample) {
+	static const unsigned int DELAY_LINE_MASK = ACCURATE_LPF_DELAY_LINE_LENGTH - 1;
+
+	float sample = (phase == 0) ? LPF_TAPS[ACCURATE_LPF_DELAY_LINE_LENGTH * ACCURATE_LPF_NUMBER_OF_PHASES] * ringBuffer[ringBufferPosition] : 0.0f;
+	if (!hasNextSample()) {
+		ringBuffer[ringBufferPosition] = inSample;
+	}
+
+	for (unsigned int tapIx = phase, delaySampleIx = 0; delaySampleIx < ACCURATE_LPF_DELAY_LINE_LENGTH; delaySampleIx++, tapIx += ACCURATE_LPF_NUMBER_OF_PHASES) {
+		sample += LPF_TAPS[tapIx] * ringBuffer[(delaySampleIx + ringBufferPosition) & DELAY_LINE_MASK];
+	}
+
+	phase += phaseIncrement;
+	if (ACCURATE_LPF_NUMBER_OF_PHASES <= phase) {
+		phase -= ACCURATE_LPF_NUMBER_OF_PHASES;
+		ringBufferPosition = (ringBufferPosition - 1) & DELAY_LINE_MASK;
+	}
+
+	return SampleEx(ACCURATE_LPF_NUMBER_OF_PHASES * sample);
+}
+
+bool AccurateLowPassFilter::hasNextSample() const {
+	return phaseIncrement <= phase;
+}
+
+unsigned int AccurateLowPassFilter::getOutputSampleRate() const {
+	return outputSampleRate;
+}
+
+unsigned int AccurateLowPassFilter::estimateInSampleCount(unsigned int outSamples) const {
+	Bit32u cycleCount = outSamples / ACCURATE_LPF_NUMBER_OF_PHASES;
+	Bit32u remainder = outSamples - cycleCount * ACCURATE_LPF_NUMBER_OF_PHASES;
+	return cycleCount * phaseIncrement + deltas[remainder][phase];
+}
+
+void AccurateLowPassFilter::addPositionIncrement(const unsigned int positionIncrement) {
+	phase = (phase + positionIncrement * phaseIncrement) % ACCURATE_LPF_NUMBER_OF_PHASES;
+}
+
+}
diff --git a/audio/softsynth/mt32/Analog.h b/audio/softsynth/mt32/Analog.h
new file mode 100644
index 0000000..a48db72
--- /dev/null
+++ b/audio/softsynth/mt32/Analog.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 2.1 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_ANALOG_H
+#define MT32EMU_ANALOG_H
+
+#include "mt32emu.h"
+
+namespace MT32Emu {
+
+class AbstractLowPassFilter;
+
+/* Analog class is dedicated to perform fair emulation of analogue circuitry of hardware units that is responsible
+ * for processing output signal after the DAC. It appears that the analogue circuit labeled "LPF" on the schematic
+ * also applies audible changes to the signal spectra. There is a significant boost of higher frequencies observed
+ * aside from quite poor attenuation of the mirror spectra above 16 kHz which is due to a relatively low filter order.
+ *
+ * As the final mixing of multiplexed output signal is performed after the DAC, this function is migrated here from Synth.
+ * Saying precisely, mixing is performed within the LPF as the entrance resistors are actually components of a LPF
+ * designed using the multiple feedback topology. Nevertheless, the schematic separates them.
+ */
+class Analog {
+public:
+	Analog(AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures);
+	~Analog();
+	void process(Sample **outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, const Bit32u outLength);
+	unsigned int getOutputSampleRate() const;
+	Bit32u getDACStreamsLength(Bit32u outputLength) const;
+	void setSynthOutputGain(float synthGain);
+	void setReverbOutputGain(float reverbGain, bool mt32ReverbCompatibilityMode);
+
+private:
+	AbstractLowPassFilter &leftChannelLPF;
+	AbstractLowPassFilter &rightChannelLPF;
+	SampleEx synthGain;
+	SampleEx reverbGain;
+
+	Analog(Analog &);
+};
+
+}
+
+#endif
diff --git a/audio/softsynth/mt32/BReverbModel.cpp b/audio/softsynth/mt32/BReverbModel.cpp
index 37b3e96..5e02db8 100644
--- a/audio/softsynth/mt32/BReverbModel.cpp
+++ b/audio/softsynth/mt32/BReverbModel.cpp
@@ -15,7 +15,7 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-//#include <memory.h>
+//#include <cstring>
 #include "mt32emu.h"
 #include "BReverbModel.h"
 
@@ -501,9 +501,9 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *
 				 *   Analysing of the algorithm suggests that the overflow is most probable when the combs output is added below.
 				 *   So, despite this isn't actually accurate, we only add the check here for performance reasons.
 				 */
-				Sample outSample = Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s((Bit32s)outL1 + Bit32s(outL1 >> 1)) + (Bit32s)outL2) + Bit32s(outL2 >> 1)) + (Bit32s)outL3);
+				Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx((SampleEx)outL1 + SampleEx(outL1 >> 1)) + (SampleEx)outL2) + SampleEx(outL2 >> 1)) + (SampleEx)outL3);
 #else
-				Sample outSample = Synth::clipBit16s((Bit32s)outL1 + Bit32s(outL1 >> 1) + (Bit32s)outL2 + Bit32s(outL2 >> 1) + (Bit32s)outL3);
+				Sample outSample = Synth::clipSampleEx((SampleEx)outL1 + SampleEx(outL1 >> 1) + (SampleEx)outL2 + SampleEx(outL2 >> 1) + (SampleEx)outL3);
 #endif
 				*(outLeft++) = weirdMul(outSample, wetLevel, 0xFF);
 			}
@@ -515,9 +515,9 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *
 				Sample outSample = 1.5f * (outR1 + outR2) + outR3;
 #elif MT32EMU_BOSS_REVERB_PRECISE_MODE
 				// See the note above for the left channel output.
-				Sample outSample = Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s((Bit32s)outR1 + Bit32s(outR1 >> 1)) + (Bit32s)outR2) + Bit32s(outR2 >> 1)) + (Bit32s)outR3);
+				Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx((SampleEx)outR1 + SampleEx(outR1 >> 1)) + (SampleEx)outR2) + SampleEx(outR2 >> 1)) + (SampleEx)outR3);
 #else
-				Sample outSample = Synth::clipBit16s((Bit32s)outR1 + Bit32s(outR1 >> 1) + (Bit32s)outR2 + Bit32s(outR2 >> 1) + (Bit32s)outR3);
+				Sample outSample = Synth::clipSampleEx((SampleEx)outR1 + SampleEx(outR1 >> 1) + (SampleEx)outR2 + SampleEx(outR2 >> 1) + (SampleEx)outR3);
 #endif
 				*(outRight++) = weirdMul(outSample, wetLevel, 0xFF);
 			}
diff --git a/audio/softsynth/mt32/BReverbModel.h b/audio/softsynth/mt32/BReverbModel.h
index 9b84090..764daf1 100644
--- a/audio/softsynth/mt32/BReverbModel.h
+++ b/audio/softsynth/mt32/BReverbModel.h
@@ -95,7 +95,6 @@ class BReverbModel {
 	const bool tapDelayMode;
 	Bit32u dryAmp;
 	Bit32u wetLevel;
-	void mute();
 
 	static const BReverbSettings &getCM32L_LAPCSettings(const ReverbMode mode);
 	static const BReverbSettings &getMT32Settings(const ReverbMode mode);
@@ -107,6 +106,7 @@ public:
 	void open();
 	// May be called multiple times without an open() in between.
 	void close();
+	void mute();
 	void setParameters(Bit8u time, Bit8u level);
 	void process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples);
 	bool isActive() const;
diff --git a/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp b/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp
index 9265d80..42d820e 100644
--- a/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp
+++ b/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp
@@ -18,7 +18,7 @@
 //#include <cmath>
 #include "mt32emu.h"
 #include "mmath.h"
-#include "LA32FloatWaveGenerator.h"
+#include "internals.h"
 
 namespace MT32Emu {
 
diff --git a/audio/softsynth/mt32/LA32Ramp.cpp b/audio/softsynth/mt32/LA32Ramp.cpp
index 454612c..2b31a33 100644
--- a/audio/softsynth/mt32/LA32Ramp.cpp
+++ b/audio/softsynth/mt32/LA32Ramp.cpp
@@ -50,8 +50,8 @@ We haven't fully explored:
 //#include <cmath>
 
 #include "mt32emu.h"
-#include "LA32Ramp.h"
 #include "mmath.h"
+#include "internals.h"
 
 namespace MT32Emu {
 
diff --git a/audio/softsynth/mt32/LA32WaveGenerator.cpp b/audio/softsynth/mt32/LA32WaveGenerator.cpp
index 7ac7cc6..765f75f 100644
--- a/audio/softsynth/mt32/LA32WaveGenerator.cpp
+++ b/audio/softsynth/mt32/LA32WaveGenerator.cpp
@@ -15,15 +15,15 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-//#include <cmath>
-#include "mt32emu.h"
-#include "mmath.h"
-#include "LA32WaveGenerator.h"
-
 #if MT32EMU_USE_FLOAT_SAMPLES
 #include "LA32FloatWaveGenerator.cpp"
 #else
 
+//#include <cmath>
+#include "mt32emu.h"
+#include "mmath.h"
+#include "internals.h"
+
 namespace MT32Emu {
 
 static const Bit32u SINE_SEGMENT_RELATIVE_LENGTH = 1 << 18;
diff --git a/audio/softsynth/mt32/MemoryRegion.h b/audio/softsynth/mt32/MemoryRegion.h
new file mode 100644
index 0000000..c0cb041
--- /dev/null
+++ b/audio/softsynth/mt32/MemoryRegion.h
@@ -0,0 +1,124 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 2.1 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_MEMORY_REGION_H
+#define MT32EMU_MEMORY_REGION_H
+
+namespace MT32Emu {
+
+enum MemoryRegionType {
+	MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset
+};
+
+class MemoryRegion {
+private:
+	Synth *synth;
+	Bit8u *realMemory;
+	Bit8u *maxTable;
+public:
+	MemoryRegionType type;
+	Bit32u startAddr, entrySize, entries;
+
+	MemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable, MemoryRegionType useType, Bit32u useStartAddr, Bit32u useEntrySize, Bit32u useEntries) {
+		synth = useSynth;
+		realMemory = useRealMemory;
+		maxTable = useMaxTable;
+		type = useType;
+		startAddr = useStartAddr;
+		entrySize = useEntrySize;
+		entries = useEntries;
+	}
+	int lastTouched(Bit32u addr, Bit32u len) const {
+		return (offset(addr) + len - 1) / entrySize;
+	}
+	int firstTouchedOffset(Bit32u addr) const {
+		return offset(addr) % entrySize;
+	}
+	int firstTouched(Bit32u addr) const {
+		return offset(addr) / entrySize;
+	}
+	Bit32u regionEnd() const {
+		return startAddr + entrySize * entries;
+	}
+	bool contains(Bit32u addr) const {
+		return addr >= startAddr && addr < regionEnd();
+	}
+	int offset(Bit32u addr) const {
+		return addr - startAddr;
+	}
+	Bit32u getClampedLen(Bit32u addr, Bit32u len) const {
+		if (addr + len > regionEnd())
+			return regionEnd() - addr;
+		return len;
+	}
+	Bit32u next(Bit32u addr, Bit32u len) const {
+		if (addr + len > regionEnd()) {
+			return regionEnd() - addr;
+		}
+		return 0;
+	}
+	Bit8u getMaxValue(int off) const {
+		if (maxTable == NULL)
+			return 0xFF;
+		return maxTable[off % entrySize];
+	}
+	Bit8u *getRealMemory() const {
+		return realMemory;
+	}
+	bool isReadable() const {
+		return getRealMemory() != NULL;
+	}
+	void read(unsigned int entry, unsigned int off, Bit8u *dst, unsigned int len) const;
+	void write(unsigned int entry, unsigned int off, const Bit8u *src, unsigned int len, bool init = false) const;
+};
+
+class PatchTempMemoryRegion : public MemoryRegion {
+public:
+	PatchTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_PatchTemp, MT32EMU_MEMADDR(0x030000), sizeof(MemParams::PatchTemp), 9) {}
+};
+class RhythmTempMemoryRegion : public MemoryRegion {
+public:
+	RhythmTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_RhythmTemp, MT32EMU_MEMADDR(0x030110), sizeof(MemParams::RhythmTemp), 85) {}
+};
+class TimbreTempMemoryRegion : public MemoryRegion {
+public:
+	TimbreTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_TimbreTemp, MT32EMU_MEMADDR(0x040000), sizeof(TimbreParam), 8) {}
+};
+class PatchesMemoryRegion : public MemoryRegion {
+public:
+	PatchesMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Patches, MT32EMU_MEMADDR(0x050000), sizeof(PatchParam), 128) {}
+};
+class TimbresMemoryRegion : public MemoryRegion {
+public:
+	TimbresMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Timbres, MT32EMU_MEMADDR(0x080000), sizeof(MemParams::PaddedTimbre), 64 + 64 + 64 + 64) {}
+};
+class SystemMemoryRegion : public MemoryRegion {
+public:
+	SystemMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_System, MT32EMU_MEMADDR(0x100000), sizeof(MemParams::System), 1) {}
+};
+class DisplayMemoryRegion : public MemoryRegion {
+public:
+	DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1) {}
+};
+class ResetMemoryRegion : public MemoryRegion {
+public:
+	ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {}
+};
+
+}
+
+#endif
diff --git a/audio/softsynth/mt32/MidiEventQueue.h b/audio/softsynth/mt32/MidiEventQueue.h
new file mode 100644
index 0000000..b1948c5
--- /dev/null
+++ b/audio/softsynth/mt32/MidiEventQueue.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 2.1 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_MIDI_EVENT_QUEUE_H
+#define MT32EMU_MIDI_EVENT_QUEUE_H
+
+namespace MT32Emu {
+
+/**
+ * Used to safely store timestamped MIDI events in a local queue.
+ */
+struct MidiEvent {
+	Bit32u shortMessageData;
+	const Bit8u *sysexData;
+	Bit32u sysexLength;
+	Bit32u timestamp;
+
+	~MidiEvent();
+	void setShortMessage(Bit32u shortMessageData, Bit32u timestamp);
+	void setSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp);
+};
+
+/**
+ * Simple queue implementation using a ring buffer to store incoming MIDI event before the synth actually processes it.
+ * It is intended to:
+ * - get rid of prerenderer while retaining graceful partial abortion
+ * - add fair emulation of the MIDI interface delays
+ * - extend the synth interface with the default implementation of a typical rendering loop.
+ * THREAD SAFETY:
+ * It is safe to use either in a single thread environment or when there are only two threads - one performs only reading
+ * and one performs only writing. More complicated usage requires external synchronisation.
+ */
+class MidiEventQueue {
+private:
+	MidiEvent * const ringBuffer;
+	const Bit32u ringBufferMask;
+	volatile Bit32u startPosition;
+	volatile Bit32u endPosition;
+
+public:
+	MidiEventQueue(Bit32u ringBufferSize = DEFAULT_MIDI_EVENT_QUEUE_SIZE); // Must be a power of 2
+	~MidiEventQueue();
+	void reset();
+	bool pushShortMessage(Bit32u shortMessageData, Bit32u timestamp);
+	bool pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp);
+	const MidiEvent *peekMidiEvent();
+	void dropMidiEvent();
+	bool isFull() const;
+};
+
+}
+
+#endif
diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp
index d92473b..cffc3ed 100644
--- a/audio/softsynth/mt32/Part.cpp
+++ b/audio/softsynth/mt32/Part.cpp
@@ -19,6 +19,7 @@
 //#include <cstring>
 
 #include "mt32emu.h"
+#include "internals.h"
 #include "PartialManager.h"
 
 namespace MT32Emu {
diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp
index 7dcc6e9..7348087 100644
--- a/audio/softsynth/mt32/Partial.cpp
+++ b/audio/softsynth/mt32/Partial.cpp
@@ -21,6 +21,7 @@
 
 #include "mt32emu.h"
 #include "mmath.h"
+#include "internals.h"
 
 namespace MT32Emu {
 
@@ -312,8 +313,8 @@ bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long len
 		// Though, it is unknown whether this overflow is exploited somewhere.
 		Sample leftOut = Sample((sample * leftPanValue) >> 8);
 		Sample rightOut = Sample((sample * rightPanValue) >> 8);
-		*leftBuf = Synth::clipBit16s((Bit32s)*leftBuf + (Bit32s)leftOut);
-		*rightBuf = Synth::clipBit16s((Bit32s)*rightBuf + (Bit32s)rightOut);
+		*leftBuf = Synth::clipSampleEx((SampleEx)*leftBuf + (SampleEx)leftOut);
+		*rightBuf = Synth::clipSampleEx((SampleEx)*rightBuf + (SampleEx)rightOut);
 		leftBuf++;
 		rightBuf++;
 #endif
diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp
index fe73087..8ca6e4e 100644
--- a/audio/softsynth/mt32/PartialManager.cpp
+++ b/audio/softsynth/mt32/PartialManager.cpp
@@ -18,6 +18,7 @@
 //#include <cstring>
 
 #include "mt32emu.h"
+#include "internals.h"
 #include "PartialManager.h"
 
 namespace MT32Emu {
diff --git a/audio/softsynth/mt32/Poly.cpp b/audio/softsynth/mt32/Poly.cpp
index e07ceb4..badcd8f 100644
--- a/audio/softsynth/mt32/Poly.cpp
+++ b/audio/softsynth/mt32/Poly.cpp
@@ -16,6 +16,7 @@
  */
 
 #include "mt32emu.h"
+#include "internals.h"
 
 namespace MT32Emu {
 
diff --git a/audio/softsynth/mt32/Poly.h b/audio/softsynth/mt32/Poly.h
index 9c6431c..e261436 100644
--- a/audio/softsynth/mt32/Poly.h
+++ b/audio/softsynth/mt32/Poly.h
@@ -21,6 +21,7 @@
 namespace MT32Emu {
 
 class Part;
+class Partial;
 
 enum PolyState {
 	POLY_Playing,
diff --git a/audio/softsynth/mt32/ROMInfo.cpp b/audio/softsynth/mt32/ROMInfo.cpp
index eb96226..7c01270 100644
--- a/audio/softsynth/mt32/ROMInfo.cpp
+++ b/audio/softsynth/mt32/ROMInfo.cpp
@@ -21,8 +21,8 @@
 namespace MT32Emu {
 
 static const ROMInfo *getKnownROMInfoFromList(unsigned int index) {
-	static const ControlROMFeatureSet MT32_COMPATIBLE(true);
-	static const ControlROMFeatureSet CM32L_COMPATIBLE(false);
+	static const ControlROMFeatureSet MT32_COMPATIBLE(true, true);
+	static const ControlROMFeatureSet CM32L_COMPATIBLE(false, false);
 
 	// Known ROMs
 	static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL, &MT32_COMPATIBLE};
@@ -106,7 +106,6 @@ void ROMImage::freeROMImage(const ROMImage *romImage) {
 	delete romImage;
 }
 
-
 Common::File* ROMImage::getFile() const {
 	return file;
 }
@@ -115,11 +114,17 @@ const ROMInfo* ROMImage::getROMInfo() const {
 	return romInfo;
 }
 
-ControlROMFeatureSet::ControlROMFeatureSet(bool useDefaultReverbMT32Compatible) : defaultReverbMT32Compatible(useDefaultReverbMT32Compatible) {
-}
+ControlROMFeatureSet::ControlROMFeatureSet(bool useDefaultReverbMT32Compatible, bool useOldMT32AnalogLPF) :
+	defaultReverbMT32Compatible(useDefaultReverbMT32Compatible),
+	oldMT32AnalogLPF(useOldMT32AnalogLPF)
+{}
 
 bool ControlROMFeatureSet::isDefaultReverbMT32Compatible() const {
 	return defaultReverbMT32Compatible;
 }
 
+bool ControlROMFeatureSet::isOldMT32AnalogLPF() const {
+	return oldMT32AnalogLPF;
+}
+
 }
diff --git a/audio/softsynth/mt32/ROMInfo.h b/audio/softsynth/mt32/ROMInfo.h
index cecbb60..4682620 100644
--- a/audio/softsynth/mt32/ROMInfo.h
+++ b/audio/softsynth/mt32/ROMInfo.h
@@ -77,10 +77,12 @@ public:
 struct ControlROMFeatureSet {
 private:
 	unsigned int defaultReverbMT32Compatible : 1;
+	unsigned int oldMT32AnalogLPF : 1;
 
 public:
-	ControlROMFeatureSet(bool defaultReverbMT32Compatible);
+	ControlROMFeatureSet(bool defaultReverbMT32Compatible, bool oldMT32AnalogLPF);
 	bool isDefaultReverbMT32Compatible() const;
+	bool isOldMT32AnalogLPF() const;
 };
 
 }
diff --git a/audio/softsynth/mt32/Structures.h b/audio/softsynth/mt32/Structures.h
index 35dcee9..4dada3a 100644
--- a/audio/softsynth/mt32/Structures.h
+++ b/audio/softsynth/mt32/Structures.h
@@ -31,19 +31,6 @@ namespace MT32Emu {
 #define MT32EMU_ALIGN_PACKED __attribute__((packed))
 #endif
 
-typedef unsigned int       Bit32u;
-typedef   signed int       Bit32s;
-typedef unsigned short int Bit16u;
-typedef   signed short int Bit16s;
-typedef unsigned char      Bit8u;
-typedef   signed char      Bit8s;
-
-#if MT32EMU_USE_FLOAT_SAMPLES
-typedef float Sample;
-#else
-typedef Bit16s Sample;
-#endif
-
 // The following structures represent the MT-32's memory
 // Since sysex allows this memory to be written to in blocks of bytes,
 // we keep this packed so that we can copy data into the various
@@ -184,7 +171,37 @@ struct MemParams {
 #pragma pack()
 #endif
 
-struct ControlROMPCMStruct;
+struct ControlROMMap {
+	Bit16u idPos;
+	Bit16u idLen;
+	const char *idBytes;
+	Bit16u pcmTable; // 4 * pcmCount bytes
+	Bit16u pcmCount;
+	Bit16u timbreAMap; // 128 bytes
+	Bit16u timbreAOffset;
+	bool timbreACompressed;
+	Bit16u timbreBMap; // 128 bytes
+	Bit16u timbreBOffset;
+	bool timbreBCompressed;
+	Bit16u timbreRMap; // 2 * timbreRCount bytes
+	Bit16u timbreRCount;
+	Bit16u rhythmSettings; // 4 * rhythmSettingsCount bytes
+	Bit16u rhythmSettingsCount;
+	Bit16u reserveSettings; // 9 bytes
+	Bit16u panSettings; // 8 bytes
+	Bit16u programSettings; // 8 bytes
+	Bit16u rhythmMaxTable; // 4 bytes
+	Bit16u patchMaxTable; // 16 bytes
+	Bit16u systemMaxTable; // 23 bytes
+	Bit16u timbreMaxTable; // 72 bytes
+};
+
+struct ControlROMPCMStruct {
+	Bit8u pos;
+	Bit8u len;
+	Bit8u pitchLSB;
+	Bit8u pitchMSB;
+};
 
 struct PCMWaveEntry {
 	Bit32u addr;
@@ -216,8 +233,6 @@ struct PatchCache {
 	const TimbreParam::PartialParam *partialParam;
 };
 
-class Partial; // Forward reference for class defined in partial.h
-
 }
 
 #endif
diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp
index 3bff429..6df7eb9 100644
--- a/audio/softsynth/mt32/Synth.cpp
+++ b/audio/softsynth/mt32/Synth.cpp
@@ -22,12 +22,19 @@
 
 #include "mt32emu.h"
 #include "mmath.h"
-#include "PartialManager.h"
+#include "internals.h"
+
+#include "Analog.h"
 #include "BReverbModel.h"
-#include "common/debug.h"
+#include "MemoryRegion.h"
+#include "MidiEventQueue.h"
+#include "PartialManager.h"
 
 namespace MT32Emu {
 
+// MIDI interface data transfer rate in samples. Used to simulate the transfer delay.
+static const double MIDI_DATA_TRANSFER_RATE = (double)SAMPLE_RATE / 31250.0 * 8.0;
+
 static const ControlROMMap ControlROMMaps[7] = {
 	// ID    IDc IDbytes                     PCMmap  PCMc  tmbrA   tmbrAO, tmbrAC tmbrB   tmbrBO, tmbrBC tmbrR   trC  rhythm  rhyC  rsrv    panpot  prog    rhyMax  patMax  sysMax  timMax
 	{0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000,  128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200,  30, 0x73A6,  85,  0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A},
@@ -46,18 +53,15 @@ static inline void advanceStreamPosition(Sample *&stream, Bit32u posDelta) {
 	}
 }
 
-Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) {
+Bit8u Synth::calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum) {
+	unsigned int checksum = -initChecksum;
 	for (unsigned int i = 0; i < len; i++) {
-		checksum = checksum + data[i];
+		checksum -= data[i];
 	}
-	checksum = checksum & 0x7f;
-	if (checksum) {
-		checksum = 0x80 - checksum;
-	}
-	return checksum;
+	return Bit8u(checksum & 0x7f);
 }
 
-Synth::Synth(ReportHandler *useReportHandler) {
+Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams()), mt32default(*new MemParams()) {
 	isOpen = false;
 	reverbOverridden = false;
 	partialCount = DEFAULT_MAX_PARTIALS;
@@ -75,6 +79,7 @@ Synth::Synth(ReportHandler *useReportHandler) {
 		reverbModels[i] = NULL;
 	}
 	reverbModel = NULL;
+	analog = NULL;
 	setDACInputMode(DACInputMode_NICE);
 	setMIDIDelayMode(MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY);
 	setOutputGain(1.0f);
@@ -92,6 +97,8 @@ Synth::~Synth() {
 	if (isDefaultReportHandler) {
 		delete reportHandler;
 	}
+	delete &mt32ram;
+	delete &mt32default;
 }
 
 void ReportHandler::showLCDMessage(const char *data) {
@@ -126,7 +133,7 @@ void Synth::printDebug(const char *fmt, ...) {
 	va_list ap;
 	va_start(ap, fmt);
 #if MT32EMU_DEBUG_SAMPLESTAMPS > 0
-	reportHandler->printDebug("[%u] ", renderedSampleCount);
+	reportHandler->printDebug("[%u] ", (char *)&renderedSampleCount);
 #endif
 	reportHandler->printDebug(fmt, ap);
 	va_end(ap);
@@ -211,10 +218,7 @@ MIDIDelayMode Synth::getMIDIDelayMode() const {
 void Synth::setOutputGain(float newOutputGain) {
 	if (newOutputGain < 0.0f) newOutputGain = -newOutputGain;
 	outputGain = newOutputGain;
-#if !MT32EMU_USE_FLOAT_SAMPLES
-	if (256.0f < newOutputGain) newOutputGain = 256.0f;
-	effectiveOutputGain = int(newOutputGain * 256.0f);
-#endif
+	if (analog != NULL) analog->setSynthOutputGain(newOutputGain);
 }
 
 float Synth::getOutputGain() const {
@@ -224,13 +228,7 @@ float Synth::getOutputGain() const {
 void Synth::setReverbOutputGain(float newReverbOutputGain) {
 	if (newReverbOutputGain < 0.0f) newReverbOutputGain = -newReverbOutputGain;
 	reverbOutputGain = newReverbOutputGain;
-	if (!isMT32ReverbCompatibilityMode()) newReverbOutputGain *= CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR;
-#if MT32EMU_USE_FLOAT_SAMPLES
-	effectiveReverbOutputGain = newReverbOutputGain;
-#else
-	if (256.0f < newReverbOutputGain) newReverbOutputGain = 256.0f;
-	effectiveReverbOutputGain = int(newReverbOutputGain * 256.0f);
-#endif
+	if (analog != NULL) analog->setReverbOutputGain(newReverbOutputGain, isMT32ReverbCompatibilityMode());
 }
 
 float Synth::getReverbOutputGain() const {
@@ -393,7 +391,11 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTi
 	return true;
 }
 
-bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount) {
+bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode) {
+	return open(controlROMImage, pcmROMImage, DEFAULT_MAX_PARTIALS, analogOutputMode);
+}
+
+bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount, AnalogOutputMode analogOutputMode) {
 	if (isOpen) {
 		return false;
 	}
@@ -548,6 +550,10 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u
 
 	midiQueue = new MidiEventQueue();
 
+	analog = new Analog(analogOutputMode, controlROMFeatures);
+	setOutputGain(outputGain);
+	setReverbOutputGain(reverbOutputGain);
+
 	isOpen = true;
 	isEnabled = false;
 
@@ -565,6 +571,9 @@ void Synth::close(bool forced) {
 	delete midiQueue;
 	midiQueue = NULL;
 
+	delete analog;
+	analog = NULL;
+
 	delete partialManager;
 	partialManager = NULL;
 
@@ -603,16 +612,37 @@ void Synth::flushMIDIQueue() {
 	}
 }
 
-void Synth::setMIDIEventQueueSize(Bit32u useSize) {
-	if (midiQueue != NULL) {
-		flushMIDIQueue();
-		delete midiQueue;
-		midiQueue = new MidiEventQueue(useSize);
+Bit32u Synth::setMIDIEventQueueSize(Bit32u useSize) {
+	static const Bit32u MAX_QUEUE_SIZE = (1 << 24); // This results in about 256 Mb - much greater than any reasonable value
+
+	if (midiQueue == NULL) return 0;
+	flushMIDIQueue();
+
+	// Find a power of 2 that is >= useSize
+	Bit32u binarySize = 1;
+	if (useSize < MAX_QUEUE_SIZE) {
+		// Using simple linear search as this isn't time critical
+		while (binarySize < useSize) binarySize <<= 1;
+	} else {
+		binarySize = MAX_QUEUE_SIZE;
 	}
+	delete midiQueue;
+	midiQueue = new MidiEventQueue(binarySize);
+	return binarySize;
 }
 
 Bit32u Synth::getShortMessageLength(Bit32u msg) {
-	if ((msg & 0xF0) == 0xF0) return 1;
+	if ((msg & 0xF0) == 0xF0) {
+		switch (msg & 0xFF) {
+			case 0xF1:
+			case 0xF3:
+				return 2;
+			case 0xF2:
+				return 3;
+			default:
+				return 1;
+		}
+	}
 	// NOTE: This calculation isn't quite correct
 	// as it doesn't consider the running status byte
 	return ((msg & 0xE0) == 0xC0) ? 2 : 3;
@@ -638,6 +668,7 @@ bool Synth::playMsg(Bit32u msg, Bit32u timestamp) {
 	if (midiDelayMode != MIDIDelayMode_IMMEDIATE) {
 		timestamp = addMIDIInterfaceDelay(getShortMessageLength(msg), timestamp);
 	}
+	if (!isEnabled) isEnabled = true;
 	return midiQueue->pushShortMessage(msg, timestamp);
 }
 
@@ -650,16 +681,19 @@ bool Synth::playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp) {
 	if (midiDelayMode == MIDIDelayMode_DELAY_ALL) {
 		timestamp = addMIDIInterfaceDelay(len, timestamp);
 	}
+	if (!isEnabled) isEnabled = true;
 	return midiQueue->pushSysex(sysex, len, timestamp);
 }
 
 void Synth::playMsgNow(Bit32u msg) {
-	// FIXME: Implement active sensing
+	// NOTE: Active sense IS implemented in real hardware. However, realtime processing is clearly out of the library scope.
+	//       It is assumed that realtime consumers of the library respond to these MIDI events as appropriate.
+
 	unsigned char code     = (unsigned char)((msg & 0x0000F0) >> 4);
 	unsigned char chan     = (unsigned char)(msg & 0x00000F);
 	unsigned char note     = (unsigned char)((msg & 0x007F00) >> 8);
 	unsigned char velocity = (unsigned char)((msg & 0x7F0000) >> 16);
-	isEnabled = true;
+	if (!isEnabled) isEnabled = true;
 
 	//printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
 
@@ -831,7 +865,7 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command,
 		printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len);
 		return;
 	}
-	unsigned char checksum = calcSysexChecksum(sysex, len - 1, 0);
+	Bit8u checksum = calcSysexChecksum(sysex, len - 1);
 	if (checksum != sysex[len - 1]) {
 		printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum);
 		return;
@@ -1410,9 +1444,8 @@ void MidiEvent::setSysex(const Bit8u *useSysexData, Bit32u useSysexLength, Bit32
 	memcpy(dstSysexData, useSysexData, sysexLength);
 }
 
-MidiEventQueue::MidiEventQueue(Bit32u useRingBufferSize) : ringBufferSize(useRingBufferSize) {
-	ringBuffer = new MidiEvent[ringBufferSize];
-	memset(ringBuffer, 0, ringBufferSize * sizeof(MidiEvent));
+MidiEventQueue::MidiEventQueue(Bit32u useRingBufferSize) : ringBuffer(new MidiEvent[useRingBufferSize]), ringBufferMask(useRingBufferSize - 1) {
+	memset(ringBuffer, 0, useRingBufferSize * sizeof(MidiEvent));
 	reset();
 }
 
@@ -1426,7 +1459,7 @@ void MidiEventQueue::reset() {
 }
 
 bool MidiEventQueue::pushShortMessage(Bit32u shortMessageData, Bit32u timestamp) {
-	unsigned int newEndPosition = (endPosition + 1) % ringBufferSize;
+	Bit32u newEndPosition = (endPosition + 1) & ringBufferMask;
 	// Is ring buffer full?
 	if (startPosition == newEndPosition) return false;
 	ringBuffer[endPosition].setShortMessage(shortMessageData, timestamp);
@@ -1435,7 +1468,7 @@ bool MidiEventQueue::pushShortMessage(Bit32u shortMessageData, Bit32u timestamp)
 }
 
 bool MidiEventQueue::pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp) {
-	unsigned int newEndPosition = (endPosition + 1) % ringBufferSize;
+	Bit32u newEndPosition = (endPosition + 1) & ringBufferMask;
 	// Is ring buffer full?
 	if (startPosition == newEndPosition) return false;
 	ringBuffer[endPosition].setSysex(sysexData, sysexLength, timestamp);
@@ -1450,31 +1483,36 @@ const MidiEvent *MidiEventQueue::peekMidiEvent() {
 void MidiEventQueue::dropMidiEvent() {
 	// Is ring buffer empty?
 	if (startPosition != endPosition) {
-		startPosition = (startPosition + 1) % ringBufferSize;
+		startPosition = (startPosition + 1) & ringBufferMask;
 	}
 }
 
+bool MidiEventQueue::isFull() const {
+	return startPosition == ((endPosition + 1) & ringBufferMask);
+}
+
+unsigned int Synth::getStereoOutputSampleRate() const {
+	return (analog == NULL) ? SAMPLE_RATE : analog->getOutputSampleRate();
+}
+
 void Synth::render(Sample *stream, Bit32u len) {
-	Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN];
-	Sample tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
-	Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN];
-	Sample tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
-	Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN];
-	Sample tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
+	if (!isEnabled) {
+		renderedSampleCount += analog->getDACStreamsLength(len);
+		analog->process(NULL, NULL, NULL, NULL, NULL, NULL, NULL, len);
+		muteSampleBuffer(stream, len << 1);
+		return;
+	}
+
+	// As in AnalogOutputMode_ACCURATE mode output is upsampled, buffer size MAX_SAMPLES_PER_RUN is more than enough.
+	Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
+	Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
+	Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN], tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
 
 	while (len > 0) {
-		Bit32u thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
-		renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisLen);
-		for (Bit32u i = 0; i < thisLen; i++) {
-#if MT32EMU_USE_FLOAT_SAMPLES
-			*(stream++) = tmpNonReverbLeft[i] + tmpReverbDryLeft[i] + tmpReverbWetLeft[i];
-			*(stream++) = tmpNonReverbRight[i] + tmpReverbDryRight[i] + tmpReverbWetRight[i];
-#else
-			*(stream++) = clipBit16s((Bit32s)tmpNonReverbLeft[i] + (Bit32s)tmpReverbDryLeft[i] + (Bit32s)tmpReverbWetLeft[i]);
-			*(stream++) = clipBit16s((Bit32s)tmpNonReverbRight[i] + (Bit32s)tmpReverbDryRight[i] + (Bit32s)tmpReverbWetRight[i]);
-#endif
-		}
-		len -= thisLen;
+		Bit32u thisPassLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
+		renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, analog->getDACStreamsLength(thisPassLen));
+		analog->process(&stream, tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisPassLen);
+		len -= thisPassLen;
 	}
 }
 
@@ -1518,7 +1556,10 @@ void Synth::renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample
 // In GENERATION2 units, the output from LA32 goes to the Boss chip already bit-shifted.
 // In NICE mode, it's also better to increase volume before the reverb processing to preserve accuracy.
 void Synth::produceLA32Output(Sample *buffer, Bit32u len) {
-#if !MT32EMU_USE_FLOAT_SAMPLES
+#if MT32EMU_USE_FLOAT_SAMPLES
+	(void)buffer;
+	(void)len;
+#else
 	switch (dacInputMode) {
 		case DACInputMode_GENERATION2:
 			while (len--) {
@@ -1528,7 +1569,7 @@ void Synth::produceLA32Output(Sample *buffer, Bit32u len) {
 			break;
 		case DACInputMode_NICE:
 			while (len--) {
-				*buffer = clipBit16s(Bit32s(*buffer) << 1);
+				*buffer = clipSampleEx(SampleEx(*buffer) << 1);
 				++buffer;
 			}
 			break;
@@ -1538,26 +1579,16 @@ void Synth::produceLA32Output(Sample *buffer, Bit32u len) {
 #endif
 }
 
-void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len, bool reverb) {
-	if (dacInputMode == DACInputMode_PURE) return;
-
+void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len) {
 #if MT32EMU_USE_FLOAT_SAMPLES
-	float gain = reverb ? effectiveReverbOutputGain : outputGain;
-	while (len--) {
-		*(buffer++) *= gain;
-	}
+	(void)buffer;
+	(void)len;
 #else
-	int gain = reverb ? effectiveReverbOutputGain : effectiveOutputGain;
 	if (dacInputMode == DACInputMode_GENERATION1) {
 		while (len--) {
-			Bit32s target = Bit16s((*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE));
-			*(buffer++) = clipBit16s((target * gain) >> 8);
+			*buffer = Sample((*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE));
+			++buffer;
 		}
-		return;
-	}
-	while (len--) {
-		*buffer = clipBit16s((Bit32s(*buffer) * gain) >> 8);
-		++buffer;
 	}
 #endif
 }
@@ -1566,18 +1597,18 @@ void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sampl
 	// Even if LA32 output isn't desired, we proceed anyway with temp buffers
 	Sample tmpBufNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpBufNonReverbRight[MAX_SAMPLES_PER_RUN];
 	if (nonReverbLeft == NULL) nonReverbLeft = tmpBufNonReverbLeft;
-	if (nonReverbLeft == NULL) nonReverbRight = tmpBufNonReverbRight;
+	if (nonReverbRight == NULL) nonReverbRight = tmpBufNonReverbRight;
 
 	Sample tmpBufReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpBufReverbDryRight[MAX_SAMPLES_PER_RUN];
 	if (reverbDryLeft == NULL) reverbDryLeft = tmpBufReverbDryLeft;
 	if (reverbDryRight == NULL) reverbDryRight = tmpBufReverbDryRight;
 
-	muteSampleBuffer(nonReverbLeft, len);
-	muteSampleBuffer(nonReverbRight, len);
-	muteSampleBuffer(reverbDryLeft, len);
-	muteSampleBuffer(reverbDryRight, len);
-
 	if (isEnabled) {
+		muteSampleBuffer(nonReverbLeft, len);
+		muteSampleBuffer(nonReverbRight, len);
+		muteSampleBuffer(reverbDryLeft, len);
+		muteSampleBuffer(reverbDryRight, len);
+
 		for (unsigned int i = 0; i < getPartialCount(); i++) {
 			if (partialManager->shouldReverb(i)) {
 				partialManager->produceOutput(i, reverbDryLeft, reverbDryRight, len);
@@ -1591,8 +1622,8 @@ void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sampl
 
 		if (isReverbEnabled()) {
 			reverbModel->process(reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, len);
-			if (reverbWetLeft != NULL) convertSamplesToOutput(reverbWetLeft, len, true);
-			if (reverbWetRight != NULL) convertSamplesToOutput(reverbWetRight, len, true);
+			if (reverbWetLeft != NULL) convertSamplesToOutput(reverbWetLeft, len);
+			if (reverbWetRight != NULL) convertSamplesToOutput(reverbWetRight, len);
 		} else {
 			muteSampleBuffer(reverbWetLeft, len);
 			muteSampleBuffer(reverbWetRight, len);
@@ -1601,15 +1632,20 @@ void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sampl
 		// Don't bother with conversion if the output is going to be unused
 		if (nonReverbLeft != tmpBufNonReverbLeft) {
 			produceLA32Output(nonReverbLeft, len);
-			convertSamplesToOutput(nonReverbLeft, len, false);
+			convertSamplesToOutput(nonReverbLeft, len);
 		}
 		if (nonReverbRight != tmpBufNonReverbRight) {
 			produceLA32Output(nonReverbRight, len);
-			convertSamplesToOutput(nonReverbRight, len, false);
+			convertSamplesToOutput(nonReverbRight, len);
 		}
-		if (reverbDryLeft != tmpBufReverbDryLeft) convertSamplesToOutput(reverbDryLeft, len, false);
-		if (reverbDryRight != tmpBufReverbDryRight) convertSamplesToOutput(reverbDryRight, len, false);
+		if (reverbDryLeft != tmpBufReverbDryLeft) convertSamplesToOutput(reverbDryLeft, len);
+		if (reverbDryRight != tmpBufReverbDryRight) convertSamplesToOutput(reverbDryRight, len);
 	} else {
+		// Avoid muting buffers that wasn't requested
+		if (nonReverbLeft != tmpBufNonReverbLeft) muteSampleBuffer(nonReverbLeft, len);
+		if (nonReverbRight != tmpBufNonReverbRight) muteSampleBuffer(nonReverbRight, len);
+		if (reverbDryLeft != tmpBufReverbDryLeft) muteSampleBuffer(reverbDryLeft, len);
+		if (reverbDryRight != tmpBufReverbDryRight) muteSampleBuffer(reverbDryRight, len);
 		muteSampleBuffer(reverbWetLeft, len);
 		muteSampleBuffer(reverbWetRight, len);
 	}
@@ -1651,14 +1687,48 @@ bool Synth::isActive() const {
 	return false;
 }
 
-const Partial *Synth::getPartial(unsigned int partialNum) const {
-	return partialManager->getPartial(partialNum);
-}
-
 unsigned int Synth::getPartialCount() const {
 	return partialCount;
 }
 
+void Synth::getPartStates(bool *partStates) const {
+	for (int partNumber = 0; partNumber < 9; partNumber++) {
+		const Part *part = parts[partNumber];
+		partStates[partNumber] = part->getActiveNonReleasingPartialCount() > 0;
+	}
+}
+
+void Synth::getPartialStates(PartialState *partialStates) const {
+	static const PartialState partialPhaseToState[8] = {
+		PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK,
+		PartialState_SUSTAIN, PartialState_SUSTAIN, PartialState_RELEASE, PartialState_INACTIVE
+	};
+
+	for (unsigned int partialNum = 0; partialNum < getPartialCount(); partialNum++) {
+		const Partial *partial = partialManager->getPartial(partialNum);
+		partialStates[partialNum] = partial->isActive() ? partialPhaseToState[partial->getTVA()->getPhase()] : PartialState_INACTIVE;
+	}
+}
+
+unsigned int Synth::getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u *velocities) const {
+	unsigned int playingNotes = 0;
+	if (isOpen && (partNumber < 9)) {
+		const Part *part = parts[partNumber];
+		const Poly *poly = part->getFirstActivePoly();
+		while (poly != NULL) {
+			keys[playingNotes] = (Bit8u)poly->getKey();
+			velocities[playingNotes] = (Bit8u)poly->getVelocity();
+			playingNotes++;
+			poly = poly->getNext();
+		}
+	}
+	return playingNotes;
+}
+
+const char *Synth::getPatchName(unsigned int partNumber) const {
+	return (!isOpen || partNumber > 8) ? NULL : parts[partNumber]->getCurrentInstr();
+}
+
 const Part *Synth::getPart(unsigned int partNum) const {
 	if (partNum > 8) {
 		return NULL;
diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h
index 37fb7b2..97d4644 100644
--- a/audio/softsynth/mt32/Synth.h
+++ b/audio/softsynth/mt32/Synth.h
@@ -19,15 +19,31 @@
 #define MT32EMU_SYNTH_H
 
 //#include <cstdarg>
+//#include <cstring>
 
 namespace MT32Emu {
 
-class TableInitialiser;
+class Analog;
+class BReverbModel;
+class MemoryRegion;
+class MidiEventQueue;
+class Part;
+class Poly;
 class Partial;
 class PartialManager;
-class Part;
-class ROMImage;
-class BReverbModel;
+
+class PatchTempMemoryRegion;
+class RhythmTempMemoryRegion;
+class TimbreTempMemoryRegion;
+class PatchesMemoryRegion;
+class TimbresMemoryRegion;
+class SystemMemoryRegion;
+class DisplayMemoryRegion;
+class ResetMemoryRegion;
+
+struct ControlROMMap;
+struct PCMWaveEntry;
+struct MemParams;
 
 /**
  * Methods for emulating the connection between the LA32 and the DAC, which involves
@@ -43,8 +59,7 @@ enum DACInputMode {
 	// Produces samples that exactly match the bits output from the emulated LA32.
 	// * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)
 	// * Much less likely to overdrive than any other mode.
-	// * Half the volume of any of the other modes, meaning its volume relative to the reverb
-	//   output when mixed together directly will sound wrong.
+	// * Half the volume of any of the other modes.
 	// * Output gain is ignored for both LA32 and reverb output.
 	// * Perfect for developers while debugging :)
 	DACInputMode_PURE,
@@ -60,6 +75,7 @@ enum DACInputMode {
 	DACInputMode_GENERATION2
 };
 
+// Methods for emulating the effective delay of incoming MIDI messages introduced by a MIDI interface.
 enum MIDIDelayMode {
 	// Process incoming MIDI events immediately.
 	MIDIDelayMode_IMMEDIATE,
@@ -72,6 +88,35 @@ enum MIDIDelayMode {
 	MIDIDelayMode_DELAY_ALL
 };
 
+// Methods for emulating the effects of analogue circuits of real hardware units on the output signal.
+enum AnalogOutputMode {
+	// Only digital path is emulated. The output samples correspond to the digital signal at the DAC entrance.
+	AnalogOutputMode_DIGITAL_ONLY,
+	// Coarse emulation of LPF circuit. High frequencies are boosted, sample rate remains unchanged.
+	AnalogOutputMode_COARSE,
+	// Finer emulation of LPF circuit. Output signal is upsampled to 48 kHz to allow emulation of audible mirror spectra above 16 kHz,
+	// which is passed through the LPF circuit without significant attenuation.
+	AnalogOutputMode_ACCURATE,
+	// Same as AnalogOutputMode_ACCURATE mode but the output signal is 2x oversampled, i.e. the output sample rate is 96 kHz.
+	// This makes subsequent resampling easier. Besides, due to nonlinear passband of the LPF emulated, it takes fewer number of MACs
+	// compared to a regular LPF FIR implementations.
+	AnalogOutputMode_OVERSAMPLED
+};
+
+enum ReverbMode {
+	REVERB_MODE_ROOM,
+	REVERB_MODE_HALL,
+	REVERB_MODE_PLATE,
+	REVERB_MODE_TAP_DELAY
+};
+
+enum PartialState {
+	PartialState_INACTIVE,
+	PartialState_ATTACK,
+	PartialState_SUSTAIN,
+	PartialState_RELEASE
+};
+
 const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41;
 
 const Bit8u SYSEX_MDL_MT32 = 0x16;
@@ -87,148 +132,10 @@ const Bit8u SYSEX_CMD_EOD = 0x45; // End of data
 const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error
 const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection
 
-const int MAX_SYSEX_SIZE = 512;
+const int MAX_SYSEX_SIZE = 512; // FIXME: Does this correspond to a real MIDI buffer used in h/w devices?
 
 const unsigned int CONTROL_ROM_SIZE = 64 * 1024;
 
-struct ControlROMPCMStruct {
-	Bit8u pos;
-	Bit8u len;
-	Bit8u pitchLSB;
-	Bit8u pitchMSB;
-};
-
-struct ControlROMMap {
-	Bit16u idPos;
-	Bit16u idLen;
-	const char *idBytes;
-	Bit16u pcmTable; // 4 * pcmCount bytes
-	Bit16u pcmCount;
-	Bit16u timbreAMap; // 128 bytes
-	Bit16u timbreAOffset;
-	bool timbreACompressed;
-	Bit16u timbreBMap; // 128 bytes
-	Bit16u timbreBOffset;
-	bool timbreBCompressed;
-	Bit16u timbreRMap; // 2 * timbreRCount bytes
-	Bit16u timbreRCount;
-	Bit16u rhythmSettings; // 4 * rhythmSettingsCount bytes
-	Bit16u rhythmSettingsCount;
-	Bit16u reserveSettings; // 9 bytes
-	Bit16u panSettings; // 8 bytes
-	Bit16u programSettings; // 8 bytes
-	Bit16u rhythmMaxTable; // 4 bytes
-	Bit16u patchMaxTable; // 16 bytes
-	Bit16u systemMaxTable; // 23 bytes
-	Bit16u timbreMaxTable; // 72 bytes
-};
-
-enum MemoryRegionType {
-	MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset
-};
-
-enum ReverbMode {
-	REVERB_MODE_ROOM,
-	REVERB_MODE_HALL,
-	REVERB_MODE_PLATE,
-	REVERB_MODE_TAP_DELAY
-};
-
-class MemoryRegion {
-private:
-	Synth *synth;
-	Bit8u *realMemory;
-	Bit8u *maxTable;
-public:
-	MemoryRegionType type;
-	Bit32u startAddr, entrySize, entries;
-
-	MemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable, MemoryRegionType useType, Bit32u useStartAddr, Bit32u useEntrySize, Bit32u useEntries) {
-		synth = useSynth;
-		realMemory = useRealMemory;
-		maxTable = useMaxTable;
-		type = useType;
-		startAddr = useStartAddr;
-		entrySize = useEntrySize;
-		entries = useEntries;
-	}
-	int lastTouched(Bit32u addr, Bit32u len) const {
-		return (offset(addr) + len - 1) / entrySize;
-	}
-	int firstTouchedOffset(Bit32u addr) const {
-		return offset(addr) % entrySize;
-	}
-	int firstTouched(Bit32u addr) const {
-		return offset(addr) / entrySize;
-	}
-	Bit32u regionEnd() const {
-		return startAddr + entrySize * entries;
-	}
-	bool contains(Bit32u addr) const {
-		return addr >= startAddr && addr < regionEnd();
-	}
-	int offset(Bit32u addr) const {
-		return addr - startAddr;
-	}
-	Bit32u getClampedLen(Bit32u addr, Bit32u len) const {
-		if (addr + len > regionEnd())
-			return regionEnd() - addr;
-		return len;
-	}
-	Bit32u next(Bit32u addr, Bit32u len) const {
-		if (addr + len > regionEnd()) {
-			return regionEnd() - addr;
-		}
-		return 0;
-	}
-	Bit8u getMaxValue(int off) const {
-		if (maxTable == NULL)
-			return 0xFF;
-		return maxTable[off % entrySize];
-	}
-	Bit8u *getRealMemory() const {
-		return realMemory;
-	}
-	bool isReadable() const {
-		return getRealMemory() != NULL;
-	}
-	void read(unsigned int entry, unsigned int off, Bit8u *dst, unsigned int len) const;
-	void write(unsigned int entry, unsigned int off, const Bit8u *src, unsigned int len, bool init = false) const;
-};
-
-class PatchTempMemoryRegion : public MemoryRegion {
-public:
-	PatchTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_PatchTemp, MT32EMU_MEMADDR(0x030000), sizeof(MemParams::PatchTemp), 9) {}
-};
-class RhythmTempMemoryRegion : public MemoryRegion {
-public:
-	RhythmTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_RhythmTemp, MT32EMU_MEMADDR(0x030110), sizeof(MemParams::RhythmTemp), 85) {}
-};
-class TimbreTempMemoryRegion : public MemoryRegion {
-public:
-	TimbreTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_TimbreTemp, MT32EMU_MEMADDR(0x040000), sizeof(TimbreParam), 8) {}
-};
-class PatchesMemoryRegion : public MemoryRegion {
-public:
-	PatchesMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Patches, MT32EMU_MEMADDR(0x050000), sizeof(PatchParam), 128) {}
-};
-class TimbresMemoryRegion : public MemoryRegion {
-public:
-	TimbresMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Timbres, MT32EMU_MEMADDR(0x080000), sizeof(MemParams::PaddedTimbre), 64 + 64 + 64 + 64) {}
-};
-class SystemMemoryRegion : public MemoryRegion {
-public:
-	SystemMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_System, MT32EMU_MEMADDR(0x100000), sizeof(MemParams::System), 1) {}
-};
-class DisplayMemoryRegion : public MemoryRegion {
-public:
-	DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1) {}
-};
-class ResetMemoryRegion : public MemoryRegion {
-public:
-	ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {}
-};
-
 class ReportHandler {
 friend class Synth;
 
@@ -254,47 +161,6 @@ protected:
 	virtual void onProgramChanged(int /* partNum */, int /* bankNum */, const char * /* patchName */) {}
 };
 
-/**
- * Used to safely store timestamped MIDI events in a local queue.
- */
-struct MidiEvent {
-	Bit32u shortMessageData;
-	const Bit8u *sysexData;
-	Bit32u sysexLength;
-	Bit32u timestamp;
-
-	~MidiEvent();
-	void setShortMessage(Bit32u shortMessageData, Bit32u timestamp);
-	void setSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp);
-};
-
-/**
- * Simple queue implementation using a ring buffer to store incoming MIDI event before the synth actually processes it.
- * It is intended to:
- * - get rid of prerenderer while retaining graceful partial abortion
- * - add fair emulation of the MIDI interface delays
- * - extend the synth interface with the default implementation of a typical rendering loop.
- * THREAD SAFETY:
- * It is safe to use either in a single thread environment or when there are only two threads - one performs only reading
- * and one performs only writing. More complicated usage requires external synchronisation.
- */
-class MidiEventQueue {
-private:
-	MidiEvent *ringBuffer;
-	Bit32u ringBufferSize;
-	volatile Bit32u startPosition;
-	volatile Bit32u endPosition;
-
-public:
-	MidiEventQueue(Bit32u ringBufferSize = DEFAULT_MIDI_EVENT_QUEUE_SIZE);
-	~MidiEventQueue();
-	void reset();
-	bool pushShortMessage(Bit32u shortMessageData, Bit32u timestamp);
-	bool pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp);
-	const MidiEvent *peekMidiEvent();
-	void dropMidiEvent();
-};
-
 class Synth {
 friend class Part;
 friend class RhythmPart;
@@ -335,7 +201,7 @@ private:
 	volatile Bit32u lastReceivedMIDIEventTimestamp;
 	volatile Bit32u renderedSampleCount;
 
-	MemParams mt32ram, mt32default;
+	MemParams &mt32ram, &mt32default;
 
 	BReverbModel *reverbModels[4];
 	BReverbModel *reverbModel;
@@ -346,12 +212,6 @@ private:
 
 	float outputGain;
 	float reverbOutputGain;
-#if MT32EMU_USE_FLOAT_SAMPLES
-	float effectiveReverbOutputGain;
-#else
-	int effectiveOutputGain;
-	int effectiveReverbOutputGain;
-#endif
 
 	bool reversedStereoEnabled;
 
@@ -368,11 +228,12 @@ private:
 	// We emulate this by delaying new MIDI events processing until abortion finishes.
 	Poly *abortingPoly;
 
-	Bit32u getShortMessageLength(Bit32u msg);
+	Analog *analog;
+
 	Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp);
 
 	void produceLA32Output(Sample *buffer, Bit32u len);
-	void convertSamplesToOutput(Sample *buffer, Bit32u len, bool reverb);
+	void convertSamplesToOutput(Sample *buffer, Bit32u len);
 	bool isAbortingPoly() const;
 	void doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len);
 
@@ -404,13 +265,20 @@ private:
 	void newTimbreSet(int partNum, Bit8u timbreGroup, const char patchName[]);
 	void printDebug(const char *fmt, ...);
 
+	// partNum should be 0..7 for Part 1..8, or 8 for Rhythm
+	const Part *getPart(unsigned int partNum) const;
+
 public:
-	static inline Bit16s clipBit16s(Bit32s sample) {
+	static inline Sample clipSampleEx(SampleEx sampleEx) {
+#if MT32EMU_USE_FLOAT_SAMPLES
+		return sampleEx;
+#else
 		// Clamp values above 32767 to 32767, and values below -32768 to -32768
 		// FIXME: Do we really need this stuff? I think these branches are very well predicted. Instead, this introduces a chain.
 		// The version below is actually a bit faster on my system...
-		//return ((sample + 0x8000) & ~0xFFFF) ? (sample >> 31) ^ 0x7FFF : (Bit16s)sample;
-		return ((-0x8000 <= sample) && (sample <= 0x7FFF)) ? (Bit16s)sample : (sample >> 31) ^ 0x7FFF;
+		//return ((sampleEx + 0x8000) & ~0xFFFF) ? (sampleEx >> 31) ^ 0x7FFF : (Sample)sampleEx;
+		return ((-0x8000 <= sampleEx) && (sampleEx <= 0x7FFF)) ? (Sample)sampleEx : (sampleEx >> 31) ^ 0x7FFF;
+#endif
 	}
 
 	static inline void muteSampleBuffer(Sample *buffer, Bit32u len) {
@@ -426,7 +294,8 @@ public:
 #endif
 	}
 
-	static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum);
+	static Bit32u getShortMessageLength(Bit32u msg);
+	static Bit8u calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum = 0);
 
 	// Optionally sets callbacks for reporting various errors, information and debug messages
 	Synth(ReportHandler *useReportHandler = NULL);
@@ -435,8 +304,12 @@ public:
 	// Used to initialise the MT-32. Must be called before any other function.
 	// Returns true if initialization was sucessful, otherwise returns false.
 	// controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth.
-	// usePartialCount sets the maximum number of partials playing simultaneously for this session.
-	bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount = DEFAULT_MAX_PARTIALS);
+	// usePartialCount sets the maximum number of partials playing simultaneously for this session (optional).
+	// analogOutputMode sets the mode for emulation of analogue circuitry of the hardware units (optional).
+	bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount = DEFAULT_MAX_PARTIALS, AnalogOutputMode analogOutputMode = AnalogOutputMode_COARSE);
+
+	// Overloaded method which opens the synth with default partial count.
+	bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode);
 
 	// Closes the MT-32 and deallocates any memory used by the synthesizer
 	void close(bool forced = false);
@@ -444,29 +317,34 @@ public:
 	// All the enqueued events are processed by the synth immediately.
 	void flushMIDIQueue();
 
-	// Sets size of the internal MIDI event queue.
+	// Sets size of the internal MIDI event queue. The queue size is set to the minimum power of 2 that is greater or equal to the size specified.
 	// The queue is flushed before reallocation.
-	void setMIDIEventQueueSize(Bit32u);
+	// Returns the actual queue size being used.
+	Bit32u setMIDIEventQueueSize(Bit32u);
 
 	// Enqueues a MIDI event for subsequent playback.
-	// The minimum delay involves the delay introduced while the event is transferred via MIDI interface
+	// The MIDI event will be processed not before the specified timestamp.
+	// The timestamp is measured as the global rendered sample count since the synth was created (at the native sample rate 32000 Hz).
+	// The minimum delay involves emulation of the delay introduced while the event is transferred via MIDI interface
 	// and emulation of the MCU busy-loop while it frees partials for use by a new Poly.
-	// Calls from multiple threads must be synchronised, although,
-	// no synchronisation is required with the rendering thread.
+	// Calls from multiple threads must be synchronised, although, no synchronisation is required with the rendering thread.
+	// The methods return false if the MIDI event queue is full and the message cannot be enqueued.
 
-	// The MIDI event will be processed not before the specified timestamp.
-	// The timestamp is measured as the global rendered sample count since the synth was created.
+	// Enqueues a single short MIDI message. The message must contain a status byte.
 	bool playMsg(Bit32u msg, Bit32u timestamp);
+	// Enqueues a single well formed System Exclusive MIDI message.
 	bool playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp);
-	// The MIDI event will be processed ASAP.
+
+	// Overloaded methods for the MIDI events to be processed ASAP.
 	bool playMsg(Bit32u msg);
 	bool playSysex(const Bit8u *sysex, Bit32u len);
 
 	// WARNING:
 	// The methods below don't ensure minimum 1-sample delay between sequential MIDI events,
 	// and a sequence of NoteOn and immediately succeeding NoteOff messages is always silent.
+	// A thread that invokes these methods must be explicitly synchronised with the thread performing sample rendering.
 
-	// Sends a 4-byte MIDI message to the MT-32 for immediate playback.
+	// Sends a short MIDI message to the synth for immediate playback. The message must contain a status byte.
 	void playMsgNow(Bit32u msg);
 	void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
 
@@ -495,12 +373,17 @@ public:
 	void setMIDIDelayMode(MIDIDelayMode mode);
 	MIDIDelayMode getMIDIDelayMode() const;
 
-	// Sets output gain factor. Applied to all output samples and unrelated with the synth's Master volume.
+	// Sets output gain factor for synth output channels. Applied to all output samples and unrelated with the synth's Master volume,
+	// it rather corresponds to the gain of the output analog circuitry of the hardware units. However, together with setReverbOutputGain()
+	// it offers to the user a capability to control the gain of reverb and non-reverb output channels independently.
 	// Ignored in DACInputMode_PURE
 	void setOutputGain(float);
 	float getOutputGain() const;
 
-	// Sets output gain factor for the reverb wet output. setOutputGain() doesn't change reverb output gain.
+	// Sets output gain factor for the reverb wet output channels. It rather corresponds to the gain of the output
+	// analog circuitry of the hardware units. However, together with setOutputGain() it offers to the user a capability
+	// to control the gain of reverb and non-reverb output channels independently.
+	//
 	// Note: We're currently emulate CM-32L/CM-64 reverb quite accurately and the reverb output level closely
 	// corresponds to the level of digital capture. Although, according to the CM-64 PCB schematic,
 	// there is a difference in the reverb analogue circuit, and the resulting output gain is 0.68
@@ -512,12 +395,21 @@ public:
 	void setReversedStereoEnabled(bool enabled);
 	bool isReversedStereoEnabled();
 
-	// Renders samples to the specified output stream.
-	// The length is in frames, not bytes (in 16-bit stereo,
-	// one frame is 4 bytes).
+	// Returns actual sample rate used in emulation of stereo analog circuitry of hardware units.
+	// See comment for render() below.
+	unsigned int getStereoOutputSampleRate() const;
+
+	// Renders samples to the specified output stream as if they were sampled at the analog stereo output.
+	// When AnalogOutputMode is set to ACCURATE, the output signal is upsampled to 48 kHz in order
+	// to retain emulation accuracy in whole audible frequency spectra. Otherwise, native digital signal sample rate is retained.
+	// getStereoOutputSampleRate() can be used to query actual sample rate of the output signal.
+	// The length is in frames, not bytes (in 16-bit stereo, one frame is 4 bytes).
 	void render(Sample *stream, Bit32u len);
 
-	// Renders samples to the specified output streams (any or all of which may be NULL).
+	// Renders samples to the specified output streams as if they appeared at the DAC entrance.
+	// No further processing performed in analog circuitry emulation is applied to the signal.
+	// NULL may be specified in place of any or all of the stream buffers.
+	// The length is in samples, not bytes.
 	void renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len);
 
 	// Returns true when there is at least one active partial, otherwise false.
@@ -526,15 +418,28 @@ public:
 	// Returns true if hasActivePartials() returns true, or reverb is (somewhat unreliably) detected as being active.
 	bool isActive() const;
 
-	const Partial *getPartial(unsigned int partialNum) const;
-
 	// Returns the maximum number of partials playing simultaneously.
 	unsigned int getPartialCount() const;
 
-	void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
+	// Fills in current states of all the parts into the array provided. The array must have at least 9 entries to fit values for all the parts.
+	// If the value returned for a part is true, there is at least one active non-releasing partial playing on this part.
+	// This info is useful in emulating behaviour of LCD display of the hardware units.
+	void getPartStates(bool *partStates) const;
 
-	// partNum should be 0..7 for Part 1..8, or 8 for Rhythm
-	const Part *getPart(unsigned int partNum) const;
+	// Fills in current states of all the partials into the array provided. The array must be large enough to accommodate states of all the partials.
+	void getPartialStates(PartialState *partialStates) const;
+
+	// Fills in information about currently playing notes on the specified part into the arrays provided. The arrays must be large enough
+	// to accommodate data for all the playing notes. The maximum number of simultaneously playing notes cannot exceed the number of partials.
+	// Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm.
+	// Returns the number of currently playing notes on the specified part.
+	unsigned int getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u *velocities) const;
+
+	// Returns name of the patch set on the specified part.
+	// Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm.
+	const char *getPatchName(unsigned int partNumber) const;
+
+	void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
 };
 
 }
diff --git a/audio/softsynth/mt32/TVA.cpp b/audio/softsynth/mt32/TVA.cpp
index 3fefb79..894e53f 100644
--- a/audio/softsynth/mt32/TVA.cpp
+++ b/audio/softsynth/mt32/TVA.cpp
@@ -23,6 +23,7 @@
 
 #include "mt32emu.h"
 #include "mmath.h"
+#include "internals.h"
 
 namespace MT32Emu {
 
diff --git a/audio/softsynth/mt32/TVF.cpp b/audio/softsynth/mt32/TVF.cpp
index bf8d50a..164cf2b 100644
--- a/audio/softsynth/mt32/TVF.cpp
+++ b/audio/softsynth/mt32/TVF.cpp
@@ -19,6 +19,7 @@
 
 #include "mt32emu.h"
 #include "mmath.h"
+#include "internals.h"
 
 namespace MT32Emu {
 
diff --git a/audio/softsynth/mt32/TVP.cpp b/audio/softsynth/mt32/TVP.cpp
index 374646e..a8003d9 100644
--- a/audio/softsynth/mt32/TVP.cpp
+++ b/audio/softsynth/mt32/TVP.cpp
@@ -19,6 +19,7 @@
 //#include <cstdlib>
 
 #include "mt32emu.h"
+#include "internals.h"
 
 namespace MT32Emu {
 
diff --git a/audio/softsynth/mt32/Tables.cpp b/audio/softsynth/mt32/Tables.cpp
index ae9f11f..7e165b5 100644
--- a/audio/softsynth/mt32/Tables.cpp
+++ b/audio/softsynth/mt32/Tables.cpp
@@ -16,14 +16,15 @@
  */
 
 //#include <cmath>
-//#include <cstdlib>
-//#include <cstring>
 
 #include "mt32emu.h"
 #include "mmath.h"
+#include "Tables.h"
 
 namespace MT32Emu {
 
+// UNUSED: const int MIDDLEC = 60;
+
 const Tables &Tables::getInstance() {
 	static const Tables instance;
 	return instance;
diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h
index e7b97af..8865c7f 100644
--- a/audio/softsynth/mt32/Tables.h
+++ b/audio/softsynth/mt32/Tables.h
@@ -20,24 +20,11 @@
 
 namespace MT32Emu {
 
-// Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent.
-// In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator.
-// The output from the synth is supposed to be resampled to convert the sample rate.
-const unsigned int SAMPLE_RATE = 32000;
-
-// MIDI interface data transfer rate in samples. Used to simulate the transfer delay.
-const double MIDI_DATA_TRANSFER_RATE = (double)SAMPLE_RATE / 31250.0 * 8.0;
-
-const float CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR = 0.68f;
-
-const int MIDDLEC = 60;
-
-class Synth;
-
 class Tables {
 private:
 	Tables();
 	Tables(Tables &);
+	~Tables() {}
 
 public:
 	static const Tables &getInstance();
diff --git a/audio/softsynth/mt32/Types.h b/audio/softsynth/mt32/Types.h
new file mode 100644
index 0000000..934b1a1
--- /dev/null
+++ b/audio/softsynth/mt32/Types.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 2.1 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_TYPES_H
+#define MT32EMU_TYPES_H
+
+namespace MT32Emu {
+
+typedef unsigned int       Bit32u;
+typedef   signed int       Bit32s;
+typedef unsigned short int Bit16u;
+typedef   signed short int Bit16s;
+typedef unsigned char      Bit8u;
+typedef   signed char      Bit8s;
+
+#if MT32EMU_USE_FLOAT_SAMPLES
+typedef float Sample;
+typedef float SampleEx;
+#else
+typedef Bit16s Sample;
+typedef Bit32s SampleEx;
+#endif
+
+}
+
+#endif
diff --git a/audio/softsynth/mt32/internals.h b/audio/softsynth/mt32/internals.h
new file mode 100644
index 0000000..ef56819
--- /dev/null
+++ b/audio/softsynth/mt32/internals.h
@@ -0,0 +1,83 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 2.1 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_INTERNALS_H
+#define MT32EMU_INTERNALS_H
+
+// Debugging
+
+// 0: Standard debug output is not stamped with the rendered sample count
+// 1: Standard debug output is stamped with the rendered sample count
+// NOTE: The "samplestamp" corresponds to the end of the last completed rendering run.
+//       This is important to bear in mind for debug output that occurs during a run.
+#define MT32EMU_DEBUG_SAMPLESTAMPS 0
+
+// 0: No debug output for initialisation progress
+// 1: Debug output for initialisation progress
+#define MT32EMU_MONITOR_INIT 0
+
+// 0: No debug output for MIDI events
+// 1: Debug output for weird MIDI events
+#define MT32EMU_MONITOR_MIDI 0
+
+// 0: No debug output for note on/off
+// 1: Basic debug output for note on/off
+// 2: Comprehensive debug output for note on/off
+#define MT32EMU_MONITOR_INSTRUMENTS 0
+
+// 0: No debug output for partial allocations
+// 1: Show partial stats when an allocation fails
+// 2: Show partial stats with every new poly
+// 3: Show individual partial allocations/deactivations
+#define MT32EMU_MONITOR_PARTIALS 0
+
+// 0: No debug output for sysex
+// 1: Basic debug output for sysex
+#define MT32EMU_MONITOR_SYSEX 0
+
+// 0: No debug output for sysex writes to the timbre areas
+// 1: Debug output with the name and location of newly-written timbres
+// 2: Complete dump of timbre parameters for newly-written timbres
+#define MT32EMU_MONITOR_TIMBRES 0
+
+// 0: No TVA/TVF-related debug output.
+// 1: Shows changes to TVA/TVF target, increment and phase.
+#define MT32EMU_MONITOR_TVA 0
+#define MT32EMU_MONITOR_TVF 0
+
+// Configuration
+
+// If non-zero, deletes reverb buffers that are not in use to save memory.
+// If zero, keeps reverb buffers for all modes around all the time to avoid allocating/freeing in the critical path.
+#define MT32EMU_REDUCE_REVERB_MEMORY 1
+
+// 0: Maximum speed at the cost of a bit lower emulation accuracy.
+// 1: Maximum achievable emulation accuracy.
+#define MT32EMU_BOSS_REVERB_PRECISE_MODE 0
+
+#include "Structures.h"
+#include "Tables.h"
+#include "Poly.h"
+#include "LA32Ramp.h"
+#include "LA32WaveGenerator.h"
+#include "TVA.h"
+#include "TVP.h"
+#include "TVF.h"
+#include "Partial.h"
+#include "Part.h"
+
+#endif
diff --git a/audio/softsynth/mt32/module.mk b/audio/softsynth/mt32/module.mk
index 1c8aa12..f966da8 100644
--- a/audio/softsynth/mt32/module.mk
+++ b/audio/softsynth/mt32/module.mk
@@ -1,6 +1,7 @@
 MODULE := audio/softsynth/mt32
 
 MODULE_OBJS := \
+	Analog.o \
 	BReverbModel.o \
 	LA32Ramp.o \
 	LA32WaveGenerator.o \
diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h
index d738a5d..1574c08 100644
--- a/audio/softsynth/mt32/mt32emu.h
+++ b/audio/softsynth/mt32/mt32emu.h
@@ -18,63 +18,20 @@
 #ifndef MT32EMU_MT32EMU_H
 #define MT32EMU_MT32EMU_H
 
-// Debugging
-
-// 0: Standard debug output is not stamped with the rendered sample count
-// 1: Standard debug output is stamped with the rendered sample count
-// NOTE: The "samplestamp" corresponds to the end of the last completed rendering run.
-//       This is important to bear in mind for debug output that occurs during a run.
-#define MT32EMU_DEBUG_SAMPLESTAMPS 0
-
-// 0: No debug output for initialisation progress
-// 1: Debug output for initialisation progress
-#define MT32EMU_MONITOR_INIT 0
-
-// 0: No debug output for MIDI events
-// 1: Debug output for weird MIDI events
-#define MT32EMU_MONITOR_MIDI 0
-
-// 0: No debug output for note on/off
-// 1: Basic debug output for note on/off
-// 2: Comprehensive debug output for note on/off
-#define MT32EMU_MONITOR_INSTRUMENTS 0
-
-// 0: No debug output for partial allocations
-// 1: Show partial stats when an allocation fails
-// 2: Show partial stats with every new poly
-// 3: Show individual partial allocations/deactivations
-#define MT32EMU_MONITOR_PARTIALS 0
-
-// 0: No debug output for sysex
-// 1: Basic debug output for sysex
-#define MT32EMU_MONITOR_SYSEX 0
-
-// 0: No debug output for sysex writes to the timbre areas
-// 1: Debug output with the name and location of newly-written timbres
-// 2: Complete dump of timbre parameters for newly-written timbres
-#define MT32EMU_MONITOR_TIMBRES 0
-
-// 0: No TVA/TVF-related debug output.
-// 1: Shows changes to TVA/TVF target, increment and phase.
-#define MT32EMU_MONITOR_TVA 0
-#define MT32EMU_MONITOR_TVF 0
-
 // Configuration
 
-// If non-zero, deletes reverb buffers that are not in use to save memory.
-// If zero, keeps reverb buffers for all modes around all the time to avoid allocating/freeing in the critical path.
-#define MT32EMU_REDUCE_REVERB_MEMORY 1
-
-// 0: Maximum speed at the cost of a bit lower emulation accuracy.
-// 1: Maximum achievable emulation accuracy.
-#define MT32EMU_BOSS_REVERB_PRECISE_MODE 0
-
 // 0: Use 16-bit signed samples and refined wave generator based on logarithmic fixed-point computations and LUTs. Maximum emulation accuracy and speed.
 // 1: Use float samples in the wave generator and renderer. Maximum output quality and minimum noise.
 #define MT32EMU_USE_FLOAT_SAMPLES 0
 
 namespace MT32Emu
 {
+// Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent.
+// In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator,
+// except the emulation of analogue path.
+// The output from the synth is supposed to be resampled externally in order to convert to the desired sample rate.
+const unsigned int SAMPLE_RATE = 32000;
+
 // The default value for the maximum number of partials playing simultaneously.
 const unsigned int DEFAULT_MAX_PARTIALS = 32;
 
@@ -97,17 +54,7 @@ const unsigned int MAX_SAMPLES_PER_RUN = 4096;
 const unsigned int DEFAULT_MIDI_EVENT_QUEUE_SIZE = 1024;
 }
 
-#include "Structures.h"
-#include "common/file.h"
-#include "Tables.h"
-#include "Poly.h"
-#include "LA32Ramp.h"
-#include "LA32WaveGenerator.h"
-#include "TVA.h"
-#include "TVP.h"
-#include "TVF.h"
-#include "Partial.h"
-#include "Part.h"
+#include "Types.h"
 #include "ROMInfo.h"
 #include "Synth.h"
 






More information about the Scummvm-git-logs mailing list