[Scummvm-git-logs] scummvm master -> 50d79c5f265aad592ae7f17209653ccbb1fde488

tsoliman tarek at bashasoliman.com
Wed Jan 3 17:51:05 CET 2018


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:
50d79c5f26 MT32: Update to munt 2.3.0


Commit: 50d79c5f265aad592ae7f17209653ccbb1fde488
    https://github.com/scummvm/scummvm/commit/50d79c5f265aad592ae7f17209653ccbb1fde488
Author: Tarek Soliman (tsoliman at scummvm.org)
Date: 2018-01-03T10:40:23-06:00

Commit Message:
MT32: Update to munt 2.3.0

This uses upstream commit 939cc986d9ffd044f8c6149361127ad5d94e430f

Closes gh-1091

Changed paths:
    audio/softsynth/mt32/Analog.cpp
    audio/softsynth/mt32/Analog.h
    audio/softsynth/mt32/BReverbModel.cpp
    audio/softsynth/mt32/BReverbModel.h
    audio/softsynth/mt32/Enumerations.h
    audio/softsynth/mt32/File.cpp
    audio/softsynth/mt32/File.h
    audio/softsynth/mt32/FileStream.cpp
    audio/softsynth/mt32/FileStream.h
    audio/softsynth/mt32/LA32FloatWaveGenerator.cpp
    audio/softsynth/mt32/LA32FloatWaveGenerator.h
    audio/softsynth/mt32/LA32Ramp.cpp
    audio/softsynth/mt32/LA32Ramp.h
    audio/softsynth/mt32/LA32WaveGenerator.cpp
    audio/softsynth/mt32/LA32WaveGenerator.h
    audio/softsynth/mt32/MemoryRegion.h
    audio/softsynth/mt32/MidiEventQueue.h
    audio/softsynth/mt32/MidiStreamParser.cpp
    audio/softsynth/mt32/MidiStreamParser.h
    audio/softsynth/mt32/Part.cpp
    audio/softsynth/mt32/Part.h
    audio/softsynth/mt32/Partial.cpp
    audio/softsynth/mt32/Partial.h
    audio/softsynth/mt32/PartialManager.cpp
    audio/softsynth/mt32/PartialManager.h
    audio/softsynth/mt32/Poly.cpp
    audio/softsynth/mt32/Poly.h
    audio/softsynth/mt32/ROMInfo.cpp
    audio/softsynth/mt32/ROMInfo.h
    audio/softsynth/mt32/SampleRateConverter.cpp
    audio/softsynth/mt32/SampleRateConverter.h
    audio/softsynth/mt32/Structures.h
    audio/softsynth/mt32/Synth.cpp
    audio/softsynth/mt32/Synth.h
    audio/softsynth/mt32/TVA.cpp
    audio/softsynth/mt32/TVA.h
    audio/softsynth/mt32/TVF.cpp
    audio/softsynth/mt32/TVF.h
    audio/softsynth/mt32/TVP.cpp
    audio/softsynth/mt32/TVP.h
    audio/softsynth/mt32/Tables.cpp
    audio/softsynth/mt32/Tables.h
    audio/softsynth/mt32/Types.h
    audio/softsynth/mt32/c_interface/c_interface.cpp
    audio/softsynth/mt32/c_interface/c_interface.h
    audio/softsynth/mt32/c_interface/c_types.h
    audio/softsynth/mt32/c_interface/cpp_interface.h
    audio/softsynth/mt32/config.h
    audio/softsynth/mt32/globals.h
    audio/softsynth/mt32/internals.h
    audio/softsynth/mt32/mmath.h
    audio/softsynth/mt32/module.mk
    audio/softsynth/mt32/mt32emu.h
    audio/softsynth/mt32/srchelper/InternalResampler.cpp
    audio/softsynth/mt32/srchelper/InternalResampler.h
    audio/softsynth/mt32/srchelper/SamplerateAdapter.cpp
    audio/softsynth/mt32/srchelper/SamplerateAdapter.h
    audio/softsynth/mt32/srchelper/SoxrAdapter.cpp
    audio/softsynth/mt32/srchelper/SoxrAdapter.h
    audio/softsynth/mt32/srchelper/srctools/include/FIRResampler.h
    audio/softsynth/mt32/srchelper/srctools/include/FloatSampleProvider.h
    audio/softsynth/mt32/srchelper/srctools/include/IIR2xResampler.h
    audio/softsynth/mt32/srchelper/srctools/include/LinearResampler.h
    audio/softsynth/mt32/srchelper/srctools/include/ResamplerModel.h
    audio/softsynth/mt32/srchelper/srctools/include/ResamplerStage.h
    audio/softsynth/mt32/srchelper/srctools/include/SincResampler.h
    audio/softsynth/mt32/srchelper/srctools/src/FIRResampler.cpp
    audio/softsynth/mt32/srchelper/srctools/src/IIR2xResampler.cpp
    audio/softsynth/mt32/srchelper/srctools/src/LinearResampler.cpp
    audio/softsynth/mt32/srchelper/srctools/src/ResamplerModel.cpp
    audio/softsynth/mt32/srchelper/srctools/src/SincResampler.cpp


diff --git a/audio/softsynth/mt32/Analog.cpp b/audio/softsynth/mt32/Analog.cpp
index 31e8856..2901198 100644
--- a/audio/softsynth/mt32/Analog.cpp
+++ b/audio/softsynth/mt32/Analog.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -24,8 +24,6 @@
 
 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.
@@ -34,31 +32,27 @@ namespace MT32Emu {
  * 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[] = {
+static const FloatSample COARSE_LPF_FLOAT_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[] = {
+static const FloatSample COARSE_LPF_FLOAT_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;
+static const unsigned int COARSE_LPF_INT_FRACTION_BITS = 14;
 
 // Integer versions of the FIRs above multiplied by (1 << 14) and rounded.
-static const SampleEx COARSE_LPF_TAPS_MT32[] = {
+static const IntSampleEx COARSE_LPF_INT_TAPS_MT32[] = {
 	20848, -3609, -2589, 2943, -1827, 887, -385, 180, -114
 };
 
-static const SampleEx COARSE_LPF_TAPS_CM32L[] = {
+static const IntSampleEx COARSE_LPF_INT_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.
@@ -70,7 +64,7 @@ static const SampleEx COARSE_LPF_TAPS_CM32L[] = {
  */
 
 // FIR version for MT-32 first generation.
-static const float ACCURATE_LPF_TAPS_MT32[] = {
+static const FloatSample 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,
@@ -81,7 +75,7 @@ static const float ACCURATE_LPF_TAPS_MT32[] = {
 };
 
 // FIR version for new MT-32 and CM-32L/LAPC-I.
-static const float ACCURATE_LPF_TAPS_CM32L[] = {
+static const FloatSample 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,
@@ -107,177 +101,269 @@ static const unsigned int ACCURATE_LPF_PHASE_INCREMENT_OVERSAMPLED = 1; // No do
 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 } };
 
+template <class SampleEx>
 class AbstractLowPassFilter {
 public:
-	static AbstractLowPassFilter &createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF);
+	static AbstractLowPassFilter<SampleEx> &createLowPassFilter(const AnalogOutputMode mode, const bool oldMT32AnalogLPF);
 
 	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) {}
+	virtual SampleEx process(const SampleEx sample) = 0;
+
+	virtual bool hasNextSample() const {
+		return false;
+	}
+
+	virtual unsigned int getOutputSampleRate() const {
+		return SAMPLE_RATE;
+	}
+
+	virtual unsigned int estimateInSampleCount(const unsigned int outSamples) const {
+		return outSamples;
+	}
+
+	virtual void addPositionIncrement(const unsigned int) {}
 };
 
-class NullLowPassFilter : public AbstractLowPassFilter {
+template <class SampleEx>
+class NullLowPassFilter : public AbstractLowPassFilter<SampleEx> {
 public:
-	SampleEx process(SampleEx sample);
+	SampleEx process(const SampleEx sample) {
+		return sample;
+	}
 };
 
-class CoarseLowPassFilter : public AbstractLowPassFilter {
+template <class SampleEx>
+class CoarseLowPassFilter : public AbstractLowPassFilter<SampleEx> {
 private:
-	const SampleEx * const LPF_TAPS;
+	const SampleEx * const lpfTaps;
 	SampleEx ringBuffer[COARSE_LPF_DELAY_LINE_LENGTH];
 	unsigned int ringBufferPosition;
 
 public:
-	CoarseLowPassFilter(bool oldMT32AnalogLPF);
-	SampleEx process(SampleEx sample);
+	static inline const SampleEx *getLPFTaps(const bool oldMT32AnalogLPF);
+	static inline SampleEx normaliseSample(const SampleEx sample);
+
+	explicit CoarseLowPassFilter(const bool oldMT32AnalogLPF) :
+		lpfTaps(getLPFTaps(oldMT32AnalogLPF)),
+		ringBufferPosition(0)
+	{
+		Synth::muteSampleBuffer(ringBuffer, COARSE_LPF_DELAY_LINE_LENGTH);
+	}
+
+	SampleEx process(const SampleEx inSample) {
+		static const unsigned int DELAY_LINE_MASK = COARSE_LPF_DELAY_LINE_LENGTH - 1;
+
+		SampleEx sample = lpfTaps[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 += lpfTaps[i] * ringBuffer[(i + ringBufferPosition) & DELAY_LINE_MASK];
+		}
+
+		ringBufferPosition = (ringBufferPosition - 1) & DELAY_LINE_MASK;
+
+		return normaliseSample(sample);
+	}
 };
 
-class AccurateLowPassFilter : public AbstractLowPassFilter {
+class AccurateLowPassFilter : public AbstractLowPassFilter<IntSampleEx>, public AbstractLowPassFilter<FloatSample> {
 private:
-	const float * const LPF_TAPS;
+	const FloatSample * 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];
+	FloatSample ringBuffer[ACCURATE_LPF_DELAY_LINE_LENGTH];
 	unsigned int ringBufferPosition;
 	unsigned int phase;
 
 public:
-	AccurateLowPassFilter(bool oldMT32AnalogLPF, bool oversample);
-	SampleEx process(SampleEx sample);
+	AccurateLowPassFilter(const bool oldMT32AnalogLPF, const bool oversample);
+	FloatSample process(const FloatSample sample);
+	IntSampleEx process(const IntSampleEx sample);
 	bool hasNextSample() const;
 	unsigned int getOutputSampleRate() const;
-	unsigned int estimateInSampleCount(unsigned int outSamples) const;
-	void addPositionIncrement(unsigned int positionIncrement);
+	unsigned int estimateInSampleCount(const unsigned int outSamples) const;
+	void addPositionIncrement(const unsigned int positionIncrement);
 };
 
-Analog::Analog(const AnalogOutputMode mode, const bool oldMT32AnalogLPF) :
-	leftChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, oldMT32AnalogLPF)),
-	rightChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, oldMT32AnalogLPF)),
-	synthGain(0),
-	reverbGain(0)
-{}
+static inline IntSampleEx normaliseSample(const IntSampleEx sample) {
+	return sample >> OUTPUT_GAIN_FRACTION_BITS;
+}
+
+static inline FloatSample normaliseSample(const FloatSample sample) {
+	return sample;
+}
+
+static inline float getActualReverbOutputGain(const float reverbGain, const bool mt32ReverbCompatibilityMode) {
+	return mt32ReverbCompatibilityMode ? reverbGain : reverbGain * CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR;
+}
 
-Analog::~Analog() {
-	delete &leftChannelLPF;
-	delete &rightChannelLPF;
+static inline IntSampleEx getIntOutputGain(const float outputGain) {
+	return IntSampleEx(((OUTPUT_GAIN_MULTIPLIER < outputGain) ? OUTPUT_GAIN_MULTIPLIER : outputGain) * OUTPUT_GAIN_MULTIPLIER);
 }
 
-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;
+template <class SampleEx>
+class AnalogImpl : public Analog {
+public:
+	AbstractLowPassFilter<SampleEx> &leftChannelLPF;
+	AbstractLowPassFilter<SampleEx> &rightChannelLPF;
+	SampleEx synthGain;
+	SampleEx reverbGain;
+
+	AnalogImpl(const AnalogOutputMode mode, const bool oldMT32AnalogLPF) :
+		leftChannelLPF(AbstractLowPassFilter<SampleEx>::createLowPassFilter(mode, oldMT32AnalogLPF)),
+		rightChannelLPF(AbstractLowPassFilter<SampleEx>::createLowPassFilter(mode, oldMT32AnalogLPF)),
+		synthGain(0),
+		reverbGain(0)
+	{}
+
+	~AnalogImpl() {
+		delete &leftChannelLPF;
+		delete &rightChannelLPF;
+	}
+
+	unsigned int getOutputSampleRate() const {
+		return leftChannelLPF.getOutputSampleRate();
 	}
 
-	while (0 < (outLength--)) {
-		SampleEx outSampleL;
-		SampleEx outSampleR;
+	Bit32u getDACStreamsLength(const Bit32u outputLength) const {
+		return leftChannelLPF.estimateInSampleCount(outputLength);
+	}
 
-		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;
+	void setSynthOutputGain(const float synthGain);
+	void setReverbOutputGain(const float reverbGain, const bool mt32ReverbCompatibilityMode);
 
-#if !MT32EMU_USE_FLOAT_SAMPLES
-			inSampleL >>= OUTPUT_GAIN_FRACTION_BITS;
-			inSampleR >>= OUTPUT_GAIN_FRACTION_BITS;
-#endif
+	bool process(IntSample *outStream, const IntSample *nonReverbLeft, const IntSample *nonReverbRight, const IntSample *reverbDryLeft, const IntSample *reverbDryRight, const IntSample *reverbWetLeft, const IntSample *reverbWetRight, Bit32u outLength);
+	bool process(FloatSample *outStream, const FloatSample *nonReverbLeft, const FloatSample *nonReverbRight, const FloatSample *reverbDryLeft, const FloatSample *reverbDryRight, const FloatSample *reverbWetLeft, const FloatSample *reverbWetRight, Bit32u outLength);
 
-			outSampleL = leftChannelLPF.process(inSampleL);
-			outSampleR = rightChannelLPF.process(inSampleR);
+	template <class Sample>
+	void produceOutput(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;
 		}
 
-		*(outStream++) = Synth::clipSampleEx(outSampleL);
-		*(outStream++) = Synth::clipSampleEx(outSampleR);
+		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;
+
+				outSampleL = leftChannelLPF.process(normaliseSample(inSampleL));
+				outSampleR = rightChannelLPF.process(normaliseSample(inSampleR));
+			}
+
+			*(outStream++) = Synth::clipSampleEx(outSampleL);
+			*(outStream++) = Synth::clipSampleEx(outSampleR);
+		}
+	}
+};
+
+Analog *Analog::createAnalog(const AnalogOutputMode mode, const bool oldMT32AnalogLPF, const RendererType rendererType) {
+	switch (rendererType)
+	{
+	case RendererType_BIT16S:
+		return new AnalogImpl<IntSampleEx>(mode, oldMT32AnalogLPF);
+	case RendererType_FLOAT:
+		return new AnalogImpl<FloatSample>(mode, oldMT32AnalogLPF);
 	}
+	return NULL;
+}
+
+template<>
+bool AnalogImpl<IntSampleEx>::process(IntSample *outStream, const IntSample *nonReverbLeft, const IntSample *nonReverbRight, const IntSample *reverbDryLeft, const IntSample *reverbDryRight, const IntSample *reverbWetLeft, const IntSample *reverbWetRight, Bit32u outLength) {
+	produceOutput(outStream, nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, outLength);
+	return true;
+}
+
+template<>
+bool AnalogImpl<FloatSample>::process(IntSample *, const IntSample *, const IntSample *, const IntSample *, const IntSample *, const IntSample *, const IntSample *, Bit32u) {
+	return false;
+}
+
+template<>
+bool AnalogImpl<IntSampleEx>::process(FloatSample *, const FloatSample *, const FloatSample *, const FloatSample *, const FloatSample *, const FloatSample *, const FloatSample *, Bit32u) {
+	return false;
 }
 
-unsigned int Analog::getOutputSampleRate() const {
-	return leftChannelLPF.getOutputSampleRate();
+template<>
+bool AnalogImpl<FloatSample>::process(FloatSample *outStream, const FloatSample *nonReverbLeft, const FloatSample *nonReverbRight, const FloatSample *reverbDryLeft, const FloatSample *reverbDryRight, const FloatSample *reverbWetLeft, const FloatSample *reverbWetRight, Bit32u outLength) {
+	produceOutput(outStream, nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, outLength);
+	return true;
 }
 
-Bit32u Analog::getDACStreamsLength(Bit32u outputLength) const {
-	return leftChannelLPF.estimateInSampleCount(outputLength);
+template<>
+void AnalogImpl<IntSampleEx>::setSynthOutputGain(const float useSynthGain) {
+	synthGain = getIntOutputGain(useSynthGain);
 }
 
-void Analog::setSynthOutputGain(float useSynthGain) {
-#if MT32EMU_USE_FLOAT_SAMPLES
+template<>
+void AnalogImpl<IntSampleEx>::setReverbOutputGain(const float useReverbGain, const bool mt32ReverbCompatibilityMode) {
+	reverbGain = getIntOutputGain(getActualReverbOutputGain(useReverbGain, mt32ReverbCompatibilityMode));
+}
+
+template<>
+void AnalogImpl<FloatSample>::setSynthOutputGain(const float useSynthGain) {
 	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
+template<>
+void AnalogImpl<FloatSample>::setReverbOutputGain(const float useReverbGain, const bool mt32ReverbCompatibilityMode) {
+	reverbGain = getActualReverbOutputGain(useReverbGain, mt32ReverbCompatibilityMode);
+}
+
+template<>
+AbstractLowPassFilter<IntSampleEx> &AbstractLowPassFilter<IntSampleEx>::createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF) {
+	switch (mode) {
+	case AnalogOutputMode_COARSE:
+		return *new CoarseLowPassFilter<IntSampleEx>(oldMT32AnalogLPF);
+	case AnalogOutputMode_ACCURATE:
+		return *new AccurateLowPassFilter(oldMT32AnalogLPF, false);
+	case AnalogOutputMode_OVERSAMPLED:
+		return *new AccurateLowPassFilter(oldMT32AnalogLPF, true);
+	default:
+		return *new NullLowPassFilter<IntSampleEx>;
+	}
 }
 
-AbstractLowPassFilter &AbstractLowPassFilter::createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF) {
+template<>
+AbstractLowPassFilter<FloatSample> &AbstractLowPassFilter<FloatSample>::createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF) {
 	switch (mode) {
 		case AnalogOutputMode_COARSE:
-			return *new CoarseLowPassFilter(oldMT32AnalogLPF);
+			return *new CoarseLowPassFilter<FloatSample>(oldMT32AnalogLPF);
 		case AnalogOutputMode_ACCURATE:
 			return *new AccurateLowPassFilter(oldMT32AnalogLPF, false);
 		case AnalogOutputMode_OVERSAMPLED:
 			return *new AccurateLowPassFilter(oldMT32AnalogLPF, true);
 		default:
-			return *new NullLowPassFilter;
+			return *new NullLowPassFilter<FloatSample>;
 	}
 }
 
-bool AbstractLowPassFilter::hasNextSample() const {
-	return false;
-}
-
-unsigned int AbstractLowPassFilter::getOutputSampleRate() const {
-	return SAMPLE_RATE;
-}
-
-unsigned int AbstractLowPassFilter::estimateInSampleCount(unsigned int outSamples) const {
-	return outSamples;
+template<>
+const IntSampleEx *CoarseLowPassFilter<IntSampleEx>::getLPFTaps(const bool oldMT32AnalogLPF) {
+	return oldMT32AnalogLPF ? COARSE_LPF_INT_TAPS_MT32 : COARSE_LPF_INT_TAPS_CM32L;
 }
 
-SampleEx NullLowPassFilter::process(const SampleEx inSample) {
-	return inSample;
+template<>
+const FloatSample *CoarseLowPassFilter<FloatSample>::getLPFTaps(const bool oldMT32AnalogLPF) {
+	return oldMT32AnalogLPF ? COARSE_LPF_FLOAT_TAPS_MT32 : COARSE_LPF_FLOAT_TAPS_CM32L;
 }
 
-CoarseLowPassFilter::CoarseLowPassFilter(bool oldMT32AnalogLPF) :
-	LPF_TAPS(oldMT32AnalogLPF ? COARSE_LPF_TAPS_MT32 : COARSE_LPF_TAPS_CM32L),
-	ringBufferPosition(0)
-{
-	Synth::muteSampleBuffer(ringBuffer, COARSE_LPF_DELAY_LINE_LENGTH);
+template<>
+IntSampleEx CoarseLowPassFilter<IntSampleEx>::normaliseSample(const IntSampleEx sample) {
+	return sample >> COARSE_LPF_INT_FRACTION_BITS;
 }
 
-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
-
+template<>
+FloatSample CoarseLowPassFilter<FloatSample>::normaliseSample(const FloatSample sample) {
 	return sample;
 }
 
@@ -292,10 +378,10 @@ AccurateLowPassFilter::AccurateLowPassFilter(const bool oldMT32AnalogLPF, const
 	Synth::muteSampleBuffer(ringBuffer, ACCURATE_LPF_DELAY_LINE_LENGTH);
 }
 
-SampleEx AccurateLowPassFilter::process(const SampleEx inSample) {
+FloatSample AccurateLowPassFilter::process(const FloatSample 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;
+	FloatSample sample = (phase == 0) ? LPF_TAPS[ACCURATE_LPF_DELAY_LINE_LENGTH * ACCURATE_LPF_NUMBER_OF_PHASES] * ringBuffer[ringBufferPosition] : 0.0f;
 	if (!hasNextSample()) {
 		ringBuffer[ringBufferPosition] = inSample;
 	}
@@ -310,7 +396,11 @@ SampleEx AccurateLowPassFilter::process(const SampleEx inSample) {
 		ringBufferPosition = (ringBufferPosition - 1) & DELAY_LINE_MASK;
 	}
 
-	return SampleEx(ACCURATE_LPF_NUMBER_OF_PHASES * sample);
+	return ACCURATE_LPF_NUMBER_OF_PHASES * sample;
+}
+
+IntSampleEx AccurateLowPassFilter::process(const IntSampleEx sample) {
+	return IntSampleEx(process(FloatSample(sample)));
 }
 
 bool AccurateLowPassFilter::hasNextSample() const {
@@ -321,7 +411,7 @@ unsigned int AccurateLowPassFilter::getOutputSampleRate() const {
 	return outputSampleRate;
 }
 
-unsigned int AccurateLowPassFilter::estimateInSampleCount(unsigned int outSamples) const {
+unsigned int AccurateLowPassFilter::estimateInSampleCount(const 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];
diff --git a/audio/softsynth/mt32/Analog.h b/audio/softsynth/mt32/Analog.h
index ee642f2..3b6dcab 100644
--- a/audio/softsynth/mt32/Analog.h
+++ b/audio/softsynth/mt32/Analog.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -20,13 +20,11 @@
 
 #include "globals.h"
 #include "internals.h"
-#include "Types.h"
 #include "Enumerations.h"
+#include "Types.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
@@ -38,21 +36,16 @@ class AbstractLowPassFilter;
  */
 class Analog {
 public:
-	Analog(const AnalogOutputMode mode, const bool oldMT32AnalogLPF);
-	~Analog();
-	void process(Sample *outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, 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 &);
+	static Analog *createAnalog(const AnalogOutputMode mode, const bool oldMT32AnalogLPF, const RendererType rendererType);
+
+	virtual ~Analog() {}
+	virtual unsigned int getOutputSampleRate() const = 0;
+	virtual Bit32u getDACStreamsLength(const Bit32u outputLength) const = 0;
+	virtual void setSynthOutputGain(const float synthGain) = 0;
+	virtual void setReverbOutputGain(const float reverbGain, const bool mt32ReverbCompatibilityMode) = 0;
+
+	virtual bool process(IntSample *outStream, const IntSample *nonReverbLeft, const IntSample *nonReverbRight, const IntSample *reverbDryLeft, const IntSample *reverbDryRight, const IntSample *reverbWetLeft, const IntSample *reverbWetRight, Bit32u outLength) = 0;
+	virtual bool process(FloatSample *outStream, const FloatSample *nonReverbLeft, const FloatSample *nonReverbRight, const FloatSample *reverbDryLeft, const FloatSample *reverbDryRight, const FloatSample *reverbWetLeft, const FloatSample *reverbWetRight, Bit32u outLength) = 0;
 };
 
 } // namespace MT32Emu
diff --git a/audio/softsynth/mt32/BReverbModel.cpp b/audio/softsynth/mt32/BReverbModel.cpp
index 2be1b41..af559a9 100644
--- a/audio/softsynth/mt32/BReverbModel.cpp
+++ b/audio/softsynth/mt32/BReverbModel.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -37,9 +37,26 @@ static const Bit32u PROCESS_DELAY = 1;
 static const Bit32u MODE_3_ADDITIONAL_DELAY = 1;
 static const Bit32u MODE_3_FEEDBACK_DELAY = 1;
 
+// Avoid denormals degrading performance, using biased input
+static const FloatSample BIAS = 1e-20f;
+
+struct BReverbSettings {
+	const Bit32u numberOfAllpasses;
+	const Bit32u * const allpassSizes;
+	const Bit32u numberOfCombs;
+	const Bit32u * const combSizes;
+	const Bit32u * const outLPositions;
+	const Bit32u * const outRPositions;
+	const Bit8u * const filterFactors;
+	const Bit8u * const feedbackFactors;
+	const Bit8u * const dryAmps;
+	const Bit8u * const wetLevels;
+	const Bit8u lpfAmp;
+};
+
 // Default reverb settings for "new" reverb model implemented in CM-32L / LAPC-I.
 // Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog).
-const BReverbSettings &BReverbModel::getCM32L_LAPCSettings(const ReverbMode mode) {
+static const BReverbSettings &getCM32L_LAPCSettings(const ReverbMode mode) {
 	static const Bit32u MODE_0_NUMBER_OF_ALLPASSES = 3;
 	static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78};
 	static const Bit32u MODE_0_NUMBER_OF_COMBS = 4; // Well, actually there are 3 comb filters, but the entrance LPF + delay can be processed via a hacked comb.
@@ -108,7 +125,7 @@ const BReverbSettings &BReverbModel::getCM32L_LAPCSettings(const ReverbMode mode
 
 // Default reverb settings for "old" reverb model implemented in MT-32.
 // Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog).
-const BReverbSettings &BReverbModel::getMT32Settings(const ReverbMode mode) {
+static const BReverbSettings &getMT32Settings(const ReverbMode mode) {
 	static const Bit32u MODE_0_NUMBER_OF_ALLPASSES = 3;
 	static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78};
 	static const Bit32u MODE_0_NUMBER_OF_COMBS = 4; // Same as above in the new model implementation
@@ -175,357 +192,471 @@ const BReverbSettings &BReverbModel::getMT32Settings(const ReverbMode mode) {
 	return *REVERB_SETTINGS[mode];
 }
 
-// This algorithm tries to emulate exactly Boss multiplication operation (at least this is what we see on reverb RAM data lines).
-// Also LA32 is suspected to use the similar one to perform PCM interpolation and ring modulation.
-static Sample weirdMul(Sample a, Bit8u addMask, Bit8u carryMask) {
-	(void)carryMask;
-#if MT32EMU_USE_FLOAT_SAMPLES
-	return a * addMask / 256.0f;
-#elif MT32EMU_BOSS_REVERB_PRECISE_MODE
+static inline IntSample weirdMul(IntSample sample, Bit8u addMask, Bit8u carryMask) {
+#if MT32EMU_BOSS_REVERB_PRECISE_MODE
+	// This algorithm tries to emulate exactly Boss multiplication operation (at least this is what we see on reverb RAM data lines).
 	Bit8u mask = 0x80;
-	Bit32s res = 0;
+	IntSampleEx res = 0;
 	for (int i = 0; i < 8; i++) {
-		Bit32s carry = (a < 0) && (mask & carryMask) > 0 ? a & 1 : 0;
-		a >>= 1;
-		res += (mask & addMask) > 0 ? a + carry : 0;
+		IntSampleEx carry = (sample < 0) && (mask & carryMask) > 0 ? sample & 1 : 0;
+		sample >>= 1;
+		res += (mask & addMask) > 0 ? sample + carry : 0;
 		mask >>= 1;
 	}
-	return res;
+	return IntSample(res);
 #else
-	return Sample((Bit32s(a) * addMask) >> 8);
+	(void)carryMask;
+	return IntSample((IntSampleEx(sample) * addMask) >> 8);
 #endif
 }
 
-RingBuffer::RingBuffer(Bit32u newsize) : size(newsize), index(0) {
-	buffer = new Sample[size];
+static inline FloatSample weirdMul(FloatSample sample, Bit8u addMask, Bit8u carryMask) {
+	(void)carryMask;
+	return sample * addMask / 256.0f;
 }
 
-RingBuffer::~RingBuffer() {
-	delete[] buffer;
-	buffer = NULL;
+static inline IntSample halveSample(IntSample sample) {
+	return sample >> 1;
 }
 
-Sample RingBuffer::next() {
-	if (++index >= size) {
-		index = 0;
-	}
-	return buffer[index];
+static inline FloatSample halveSample(FloatSample sample) {
+	return 0.5f * sample;
 }
 
-bool RingBuffer::isEmpty() const {
-	if (buffer == NULL) return true;
-
-#if MT32EMU_USE_FLOAT_SAMPLES
-	Sample max = 0.001f;
+static inline IntSample quarterSample(IntSample sample) {
+#if MT32EMU_BOSS_REVERB_PRECISE_MODE
+	return (sample >> 1) / 2;
 #else
-	Sample max = 8;
+	return sample >> 2;
 #endif
-	Sample *buf = buffer;
-	for (Bit32u i = 0; i < size; i++) {
-		if (*buf < -max || *buf > max) return false;
-		buf++;
-	}
-	return true;
 }
 
-void RingBuffer::mute() {
-	Synth::muteSampleBuffer(buffer, size);
+static inline FloatSample quarterSample(FloatSample sample) {
+	return 0.25f * sample;
 }
 
-AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {}
+static inline IntSample addDCBias(IntSample sample) {
+	return sample;
+}
 
-Sample AllpassFilter::process(const Sample in) {
-	// This model corresponds to the allpass filter implementation of the real CM-32L device
-	// found from sample analysis
+static inline FloatSample addDCBias(FloatSample sample) {
+	return sample + BIAS;
+}
 
-	const Sample bufferOut = next();
+static inline IntSample addAllpassNoise(IntSample sample) {
+#if MT32EMU_BOSS_REVERB_PRECISE_MODE
+	// This introduces reverb noise which actually makes output from the real Boss chip nondeterministic
+	return sample - 1;
+#else
+	return sample;
+#endif
+}
 
-#if MT32EMU_USE_FLOAT_SAMPLES
-	// store input - feedback / 2
-	buffer[index] = in - 0.5f * bufferOut;
+static inline FloatSample addAllpassNoise(FloatSample sample) {
+	return sample;
+}
 
-	// return buffer output + feedforward / 2
-	return bufferOut + 0.5f * buffer[index];
+/* NOTE:
+ *   Thanks to Mok for discovering, the adder in BOSS reverb chip is found to perform addition with saturation to avoid integer overflow.
+ *   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.
+ */
+static inline IntSample mixCombs(IntSample out1, IntSample out2, IntSample out3) {
+#if MT32EMU_BOSS_REVERB_PRECISE_MODE
+	return Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(IntSampleEx(out1) + (IntSampleEx(out1) >> 1)) + IntSampleEx(out2)) + (IntSampleEx(out2) >> 1)) + IntSampleEx(out3));
 #else
-	// store input - feedback / 2
-	buffer[index] = in - (bufferOut >> 1);
-
-	// return buffer output + feedforward / 2
-	return bufferOut + (buffer[index] >> 1);
+	return Synth::clipSampleEx(IntSampleEx(out1) + (IntSampleEx(out1) >> 1) + IntSampleEx(out2) + (IntSampleEx(out2) >> 1) + IntSampleEx(out3));
 #endif
 }
 
-CombFilter::CombFilter(const Bit32u useSize, const Bit8u useFilterFactor) : RingBuffer(useSize), filterFactor(useFilterFactor) {}
+static inline FloatSample mixCombs(FloatSample out1, FloatSample out2, FloatSample out3) {
+	return 1.5f * (out1 + out2) + out3;
+}
+
+template <class Sample>
+class RingBuffer {
+	static inline Sample sampleValueThreshold();
 
-void CombFilter::process(const Sample in) {
-	// This model corresponds to the comb filter implementation of the real CM-32L device
+protected:
+	Sample *buffer;
+	const Bit32u size;
+	Bit32u index;
 
-	// the previously stored value
-	const Sample last = buffer[index];
+public:
+	RingBuffer(const Bit32u newsize) : size(newsize), index(0) {
+		buffer = new Sample[size];
+	}
 
-	// prepare input + feedback
-	const Sample filterIn = in + weirdMul(next(), feedbackFactor, 0xF0);
+	virtual ~RingBuffer() {
+		delete[] buffer;
+		buffer = NULL;
+	}
 
-	// store input + feedback processed by a low-pass filter
-	buffer[index] = weirdMul(last, filterFactor, 0xC0) - filterIn;
-}
+	Sample next() {
+		if (++index >= size) {
+			index = 0;
+		}
+		return buffer[index];
+	}
+
+	bool isEmpty() const {
+		if (buffer == NULL) return true;
+
+		Sample *buf = buffer;
+		for (Bit32u i = 0; i < size; i++) {
+			if (*buf < -sampleValueThreshold() || *buf > sampleValueThreshold()) return false;
+			buf++;
+		}
+		return true;
+	}
+
+	void mute() {
+		Synth::muteSampleBuffer(buffer, size);
+	}
+};
 
-Sample CombFilter::getOutputAt(const Bit32u outIndex) const {
-	return buffer[(size + index - outIndex) % size];
+template<>
+IntSample RingBuffer<IntSample>::sampleValueThreshold() {
+	return 8;
 }
 
-void CombFilter::setFeedbackFactor(const Bit8u useFeedbackFactor) {
-	feedbackFactor = useFeedbackFactor;
+template<>
+FloatSample RingBuffer<FloatSample>::sampleValueThreshold() {
+	return 0.001f;
 }
 
-DelayWithLowPassFilter::DelayWithLowPassFilter(const Bit32u useSize, const Bit8u useFilterFactor, const Bit8u useAmp)
-	: CombFilter(useSize, useFilterFactor), amp(useAmp) {}
+template <class Sample>
+class AllpassFilter : public RingBuffer<Sample> {
+public:
+	AllpassFilter(const Bit32u useSize) : RingBuffer<Sample>(useSize) {}
 
-void DelayWithLowPassFilter::process(const Sample in) {
-	// the previously stored value
-	const Sample last = buffer[index];
+	// This model corresponds to the allpass filter implementation of the real CM-32L device
+	// found from sample analysis
+	Sample process(const Sample in) {
+		const Sample bufferOut = this->next();
 
-	// move to the next index
-	next();
+		// store input - feedback / 2
+		this->buffer[this->index] = in - halveSample(bufferOut);
 
-	// low-pass filter process
-	Sample lpfOut = weirdMul(last, filterFactor, 0xFF) + in;
+		// return buffer output + feedforward / 2
+		return bufferOut + halveSample(this->buffer[this->index]);
+	}
+};
 
-	// store lpfOut multiplied by LPF amp factor
-	buffer[index] = weirdMul(lpfOut, amp, 0xFF);
-}
+template <class Sample>
+class CombFilter : public RingBuffer<Sample> {
+protected:
+	const Bit8u filterFactor;
+	Bit8u feedbackFactor;
 
-TapDelayCombFilter::TapDelayCombFilter(const Bit32u useSize, const Bit8u useFilterFactor) : CombFilter(useSize, useFilterFactor) {}
+public:
+	CombFilter(const Bit32u useSize, const Bit8u useFilterFactor) : RingBuffer<Sample>(useSize), filterFactor(useFilterFactor) {}
 
-void TapDelayCombFilter::process(const Sample in) {
-	// the previously stored value
-	const Sample last = buffer[index];
+	// This model corresponds to the comb filter implementation of the real CM-32L device
+	void process(const Sample in) {
 
-	// move to the next index
-	next();
+		// the previously stored value
+		const Sample last = this->buffer[this->index];
 
-	// prepare input + feedback
-	// Actually, the size of the filter varies with the TIME parameter, the feedback sample is taken from the position just below the right output
-	const Sample filterIn = in + weirdMul(getOutputAt(outR + MODE_3_FEEDBACK_DELAY), feedbackFactor, 0xF0);
+		// prepare input + feedback
+		const Sample filterIn = in + weirdMul(this->next(), feedbackFactor, 0xF0);
 
-	// store input + feedback processed by a low-pass filter
-	buffer[index] = weirdMul(last, filterFactor, 0xF0) - filterIn;
-}
+		// store input + feedback processed by a low-pass filter
+		this->buffer[this->index] = weirdMul(last, filterFactor, 0xC0) - filterIn;
+	}
 
-Sample TapDelayCombFilter::getLeftOutput() const {
-	return getOutputAt(outL + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY);
-}
+	Sample getOutputAt(const Bit32u outIndex) const {
+		return this->buffer[(this->size + this->index - outIndex) % this->size];
+	}
 
-Sample TapDelayCombFilter::getRightOutput() const {
-	return getOutputAt(outR + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY);
-}
+	void setFeedbackFactor(const Bit8u useFeedbackFactor) {
+		feedbackFactor = useFeedbackFactor;
+	}
+};
 
-void TapDelayCombFilter::setOutputPositions(const Bit32u useOutL, const Bit32u useOutR) {
-	outL = useOutL;
-	outR = useOutR;
-}
+template <class Sample>
+class DelayWithLowPassFilter : public CombFilter<Sample> {
+	Bit8u amp;
 
-BReverbModel::BReverbModel(const ReverbMode mode, const bool mt32CompatibleModel) :
-	allpasses(NULL), combs(NULL),
-	currentSettings(mt32CompatibleModel ? getMT32Settings(mode) : getCM32L_LAPCSettings(mode)),
-	tapDelayMode(mode == REVERB_MODE_TAP_DELAY) {}
+public:
+	DelayWithLowPassFilter(const Bit32u useSize, const Bit8u useFilterFactor, const Bit8u useAmp)
+		: CombFilter<Sample>(useSize, useFilterFactor), amp(useAmp) {}
 
-BReverbModel::~BReverbModel() {
-	close();
-}
+	void process(const Sample in) {
+		// the previously stored value
+		const Sample last = this->buffer[this->index];
 
-void BReverbModel::open() {
-	if (currentSettings.numberOfAllpasses > 0) {
-		allpasses = new AllpassFilter*[currentSettings.numberOfAllpasses];
-		for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) {
-			allpasses[i] = new AllpassFilter(currentSettings.allpassSizes[i]);
-		}
+		// move to the next index
+		this->next();
+
+		// low-pass filter process
+		Sample lpfOut = weirdMul(last, this->filterFactor, 0xFF) + in;
+
+		// store lpfOut multiplied by LPF amp factor
+		this->buffer[this->index] = weirdMul(lpfOut, amp, 0xFF);
 	}
-	combs = new CombFilter*[currentSettings.numberOfCombs];
-	if (tapDelayMode) {
-		*combs = new TapDelayCombFilter(*currentSettings.combSizes, *currentSettings.filterFactors);
-	} else {
-		combs[0] = new DelayWithLowPassFilter(currentSettings.combSizes[0], currentSettings.filterFactors[0], currentSettings.lpfAmp);
-		for (Bit32u i = 1; i < currentSettings.numberOfCombs; i++) {
-			combs[i] = new CombFilter(currentSettings.combSizes[i], currentSettings.filterFactors[i]);
-		}
+};
+
+template <class Sample>
+class TapDelayCombFilter : public CombFilter<Sample> {
+	Bit32u outL;
+	Bit32u outR;
+
+public:
+	TapDelayCombFilter(const Bit32u useSize, const Bit8u useFilterFactor) : CombFilter<Sample>(useSize, useFilterFactor) {}
+
+	void process(const Sample in) {
+		// the previously stored value
+		const Sample last = this->buffer[this->index];
+
+		// move to the next index
+		this->next();
+
+		// prepare input + feedback
+		// Actually, the size of the filter varies with the TIME parameter, the feedback sample is taken from the position just below the right output
+		const Sample filterIn = in + weirdMul(this->getOutputAt(outR + MODE_3_FEEDBACK_DELAY), this->feedbackFactor, 0xF0);
+
+		// store input + feedback processed by a low-pass filter
+		this->buffer[this->index] = weirdMul(last, this->filterFactor, 0xF0) - filterIn;
 	}
-	mute();
-}
 
-void BReverbModel::close() {
-	if (allpasses != NULL) {
-		for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) {
-			if (allpasses[i] != NULL) {
-				delete allpasses[i];
-				allpasses[i] = NULL;
+	Sample getLeftOutput() const {
+		return this->getOutputAt(outL + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY);
+	}
+
+	Sample getRightOutput() const {
+		return this->getOutputAt(outR + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY);
+	}
+
+	void setOutputPositions(const Bit32u useOutL, const Bit32u useOutR) {
+		outL = useOutL;
+		outR = useOutR;
+	}
+};
+
+template <class Sample>
+class BReverbModelImpl : public BReverbModel {
+public:
+	AllpassFilter<Sample> **allpasses;
+	CombFilter<Sample> **combs;
+
+	const BReverbSettings ¤tSettings;
+	const bool tapDelayMode;
+	Bit8u dryAmp;
+	Bit8u wetLevel;
+
+	BReverbModelImpl(const ReverbMode mode, const bool mt32CompatibleModel) :
+		allpasses(NULL), combs(NULL),
+		currentSettings(mt32CompatibleModel ? getMT32Settings(mode) : getCM32L_LAPCSettings(mode)),
+		tapDelayMode(mode == REVERB_MODE_TAP_DELAY)
+	{}
+
+	~BReverbModelImpl() {
+		close();
+	}
+
+	bool isOpen() const {
+		return combs != NULL;
+	}
+
+	void open() {
+		if (isOpen()) return;
+		if (currentSettings.numberOfAllpasses > 0) {
+			allpasses = new AllpassFilter<Sample>*[currentSettings.numberOfAllpasses];
+			for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) {
+				allpasses[i] = new AllpassFilter<Sample>(currentSettings.allpassSizes[i]);
 			}
 		}
-		delete[] allpasses;
-		allpasses = NULL;
-	}
-	if (combs != NULL) {
-		for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) {
-			if (combs[i] != NULL) {
-				delete combs[i];
-				combs[i] = NULL;
+		combs = new CombFilter<Sample>*[currentSettings.numberOfCombs];
+		if (tapDelayMode) {
+			*combs = new TapDelayCombFilter<Sample>(*currentSettings.combSizes, *currentSettings.filterFactors);
+		} else {
+			combs[0] = new DelayWithLowPassFilter<Sample>(currentSettings.combSizes[0], currentSettings.filterFactors[0], currentSettings.lpfAmp);
+			for (Bit32u i = 1; i < currentSettings.numberOfCombs; i++) {
+				combs[i] = new CombFilter<Sample>(currentSettings.combSizes[i], currentSettings.filterFactors[i]);
 			}
 		}
-		delete[] combs;
-		combs = NULL;
+		mute();
 	}
-}
 
-void BReverbModel::mute() {
-	if (allpasses != NULL) {
-		for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) {
-			allpasses[i]->mute();
+	void close() {
+		if (allpasses != NULL) {
+			for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) {
+				if (allpasses[i] != NULL) {
+					delete allpasses[i];
+					allpasses[i] = NULL;
+				}
+			}
+			delete[] allpasses;
+			allpasses = NULL;
 		}
-	}
-	if (combs != NULL) {
-		for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) {
-			combs[i]->mute();
+		if (combs != NULL) {
+			for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) {
+				if (combs[i] != NULL) {
+					delete combs[i];
+					combs[i] = NULL;
+				}
+			}
+			delete[] combs;
+			combs = NULL;
 		}
 	}
-}
 
-void BReverbModel::setParameters(Bit8u time, Bit8u level) {
-	if (combs == NULL) return;
-	level &= 7;
-	time &= 7;
-	if (tapDelayMode) {
-		TapDelayCombFilter *comb = static_cast<TapDelayCombFilter *> (*combs);
-		comb->setOutputPositions(currentSettings.outLPositions[time], currentSettings.outRPositions[time & 7]);
-		comb->setFeedbackFactor(currentSettings.feedbackFactors[((level < 3) || (time < 6)) ? 0 : 1]);
-	} else {
-		for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) {
-			combs[i]->setFeedbackFactor(currentSettings.feedbackFactors[(i << 3) + time]);
+	void mute() {
+		if (allpasses != NULL) {
+			for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) {
+				allpasses[i]->mute();
+			}
+		}
+		if (combs != NULL) {
+			for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) {
+				combs[i]->mute();
+			}
 		}
 	}
-	if (time == 0 && level == 0) {
-		dryAmp = wetLevel = 0;
-	} else {
-		if (tapDelayMode && ((time == 0) || (time == 1 && level == 1))) {
-			// Looks like MT-32 implementation has some minor quirks in this mode:
-			// for odd level values, the output level changes sometimes depending on the time value which doesn't seem right.
-			dryAmp = currentSettings.dryAmps[level + 8];
+
+	void setParameters(Bit8u time, Bit8u level) {
+		if (!isOpen()) return;
+		level &= 7;
+		time &= 7;
+		if (tapDelayMode) {
+			TapDelayCombFilter<Sample> *comb = static_cast<TapDelayCombFilter<Sample> *> (*combs);
+			comb->setOutputPositions(currentSettings.outLPositions[time], currentSettings.outRPositions[time & 7]);
+			comb->setFeedbackFactor(currentSettings.feedbackFactors[((level < 3) || (time < 6)) ? 0 : 1]);
+		} else {
+			for (Bit32u i = 1; i < currentSettings.numberOfCombs; i++) {
+				combs[i]->setFeedbackFactor(currentSettings.feedbackFactors[(i << 3) + time]);
+			}
+		}
+		if (time == 0 && level == 0) {
+			dryAmp = wetLevel = 0;
 		} else {
-			dryAmp = currentSettings.dryAmps[level];
+			if (tapDelayMode && ((time == 0) || (time == 1 && level == 1))) {
+				// Looks like MT-32 implementation has some minor quirks in this mode:
+				// for odd level values, the output level changes sometimes depending on the time value which doesn't seem right.
+				dryAmp = currentSettings.dryAmps[level + 8];
+			} else {
+				dryAmp = currentSettings.dryAmps[level];
+			}
+			wetLevel = currentSettings.wetLevels[level];
 		}
-		wetLevel = currentSettings.wetLevels[level];
 	}
-}
 
-bool BReverbModel::isActive() const {
-	if (combs == NULL) {
+	bool isActive() const {
+		if (!isOpen()) return false;
+		for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) {
+			if (!allpasses[i]->isEmpty()) return true;
+		}
+		for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) {
+			if (!combs[i]->isEmpty()) return true;
+		}
 		return false;
 	}
-	for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) {
-		if (!allpasses[i]->isEmpty()) return true;
-	}
-	for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) {
-		if (!combs[i]->isEmpty()) return true;
-	}
-	return false;
-}
 
-bool BReverbModel::isMT32Compatible(const ReverbMode mode) const {
-	return &currentSettings == &getMT32Settings(mode);
-}
-
-void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, Bit32u numSamples) {
-	if (combs == NULL) {
-		Synth::muteSampleBuffer(outLeft, numSamples);
-		Synth::muteSampleBuffer(outRight, numSamples);
-		return;
+	bool isMT32Compatible(const ReverbMode mode) const {
+		return &currentSettings == &getMT32Settings(mode);
 	}
 
-	Sample dry;
-
-	while ((numSamples--) > 0) {
-		if (tapDelayMode) {
-#if MT32EMU_USE_FLOAT_SAMPLES
-			dry = (*(inLeft++) * 0.5f) + (*(inRight++) * 0.5f);
-#else
-			dry = (*(inLeft++) >> 1) + (*(inRight++) >> 1);
-#endif
-		} else {
-#if MT32EMU_USE_FLOAT_SAMPLES
-			dry = (*(inLeft++) * 0.25f) + (*(inRight++) * 0.25f);
-#elif MT32EMU_BOSS_REVERB_PRECISE_MODE
-			dry = (*(inLeft++) >> 1) / 2 + (*(inRight++) >> 1) / 2;
-#else
-			dry = (*(inLeft++) >> 2) + (*(inRight++) >> 2);
-#endif
+	template <class SampleEx>
+	void produceOutput(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, Bit32u numSamples) {
+		if (!isOpen()) {
+			Synth::muteSampleBuffer(outLeft, numSamples);
+			Synth::muteSampleBuffer(outRight, numSamples);
+			return;
 		}
 
-		// Looks like dryAmp doesn't change in MT-32 but it does in CM-32L / LAPC-I
-		dry = weirdMul(dry, dryAmp, 0xFF);
+		while ((numSamples--) > 0) {
+			Sample dry;
 
-		if (tapDelayMode) {
-			TapDelayCombFilter *comb = static_cast<TapDelayCombFilter *> (*combs);
-			comb->process(dry);
-			if (outLeft != NULL) {
-				*(outLeft++) = weirdMul(comb->getLeftOutput(), wetLevel, 0xFF);
-			}
-			if (outRight != NULL) {
-				*(outRight++) = weirdMul(comb->getRightOutput(), wetLevel, 0xFF);
+			if (tapDelayMode) {
+				dry = halveSample(*(inLeft++)) + halveSample(*(inRight++));
+			} else {
+				dry = quarterSample(*(inLeft++)) + quarterSample(*(inRight++));
 			}
-		} else {
-			// If the output position is equal to the comb size, get it now in order not to loose it
-			Sample link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1);
-
-			// Entrance LPF. Note, comb.process() differs a bit here.
-			combs[0]->process(dry);
 
-#if !MT32EMU_USE_FLOAT_SAMPLES
-			// This introduces reverb noise which actually makes output from the real Boss chip nondeterministic
-			link = link - 1;
-#endif
-			link = allpasses[0]->process(link);
-			link = allpasses[1]->process(link);
-			link = allpasses[2]->process(link);
-
-			// If the output position is equal to the comb size, get it now in order not to loose it
-			Sample outL1 = combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1);
-
-			combs[1]->process(link);
-			combs[2]->process(link);
-			combs[3]->process(link);
-
-			if (outLeft != NULL) {
-				Sample outL2 = combs[2]->getOutputAt(currentSettings.outLPositions[1]);
-				Sample outL3 = combs[3]->getOutputAt(currentSettings.outLPositions[2]);
-#if MT32EMU_USE_FLOAT_SAMPLES
-				Sample outSample = 1.5f * (outL1 + outL2) + outL3;
-#elif MT32EMU_BOSS_REVERB_PRECISE_MODE
-				/* NOTE:
-				 *   Thanks to Mok for discovering, the adder in BOSS reverb chip is found to perform addition with saturation to avoid integer overflow.
-				 *   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::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(SampleEx(outL1) + (SampleEx(outL1) >> 1)) + SampleEx(outL2)) + (SampleEx(outL2) >> 1)) + SampleEx(outL3));
-#else
-				Sample outSample = Synth::clipSampleEx(SampleEx(outL1) + (SampleEx(outL1) >> 1) + SampleEx(outL2) + (SampleEx(outL2) >> 1) + SampleEx(outL3));
-#endif
-				*(outLeft++) = weirdMul(outSample, wetLevel, 0xFF);
-			}
-			if (outRight != NULL) {
-				Sample outR1 = combs[1]->getOutputAt(currentSettings.outRPositions[0]);
-				Sample outR2 = combs[2]->getOutputAt(currentSettings.outRPositions[1]);
-				Sample outR3 = combs[3]->getOutputAt(currentSettings.outRPositions[2]);
-#if MT32EMU_USE_FLOAT_SAMPLES
-				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::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(SampleEx(outR1) + (SampleEx(outR1) >> 1)) + SampleEx(outR2)) + (SampleEx(outR2) >> 1)) + SampleEx(outR3));
-#else
-				Sample outSample = Synth::clipSampleEx(SampleEx(outR1) + (SampleEx(outR1) >> 1) + SampleEx(outR2) + (SampleEx(outR2) >> 1) + SampleEx(outR3));
-#endif
-				*(outRight++) = weirdMul(outSample, wetLevel, 0xFF);
-			}
-		}
+			// Looks like dryAmp doesn't change in MT-32 but it does in CM-32L / LAPC-I
+			dry = weirdMul(addDCBias(dry), dryAmp, 0xFF);
+
+			if (tapDelayMode) {
+				TapDelayCombFilter<Sample> *comb = static_cast<TapDelayCombFilter<Sample> *>(*combs);
+				comb->process(dry);
+				if (outLeft != NULL) {
+					*(outLeft++) = weirdMul(comb->getLeftOutput(), wetLevel, 0xFF);
+				}
+				if (outRight != NULL) {
+					*(outRight++) = weirdMul(comb->getRightOutput(), wetLevel, 0xFF);
+				}
+			} else {
+				DelayWithLowPassFilter<Sample> * const entranceDelay = static_cast<DelayWithLowPassFilter<Sample> *>(combs[0]);
+				// If the output position is equal to the comb size, get it now in order not to loose it
+				Sample link = entranceDelay->getOutputAt(currentSettings.combSizes[0] - 1);
+
+				// Entrance LPF. Note, comb.process() differs a bit here.
+				entranceDelay->process(dry);
+
+				link = allpasses[0]->process(addAllpassNoise(link));
+				link = allpasses[1]->process(link);
+				link = allpasses[2]->process(link);
+
+				// If the output position is equal to the comb size, get it now in order not to loose it
+				Sample outL1 = combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1);
+
+				combs[1]->process(link);
+				combs[2]->process(link);
+				combs[3]->process(link);
+
+				if (outLeft != NULL) {
+					Sample outL2 = combs[2]->getOutputAt(currentSettings.outLPositions[1]);
+					Sample outL3 = combs[3]->getOutputAt(currentSettings.outLPositions[2]);
+					Sample outSample = mixCombs(outL1, outL2, outL3);
+					*(outLeft++) = weirdMul(outSample, wetLevel, 0xFF);
+				}
+				if (outRight != NULL) {
+					Sample outR1 = combs[1]->getOutputAt(currentSettings.outRPositions[0]);
+					Sample outR2 = combs[2]->getOutputAt(currentSettings.outRPositions[1]);
+					Sample outR3 = combs[3]->getOutputAt(currentSettings.outRPositions[2]);
+					Sample outSample = mixCombs(outR1, outR2, outR3);
+					*(outRight++) = weirdMul(outSample, wetLevel, 0xFF);
+				}
+			} // if (tapDelayMode)
+		} // while ((numSamples--) > 0)
+	} // produceOutput
+
+	bool process(const IntSample *inLeft, const IntSample *inRight, IntSample *outLeft, IntSample *outRight, Bit32u numSamples);
+	bool process(const FloatSample *inLeft, const FloatSample *inRight, FloatSample *outLeft, FloatSample *outRight, Bit32u numSamples);
+};
+
+BReverbModel *BReverbModel::createBReverbModel(const ReverbMode mode, const bool mt32CompatibleModel, const RendererType rendererType) {
+	switch (rendererType)
+	{
+	case RendererType_BIT16S:
+		return new BReverbModelImpl<IntSample>(mode, mt32CompatibleModel);
+	case RendererType_FLOAT:
+		return new BReverbModelImpl<FloatSample>(mode, mt32CompatibleModel);
 	}
+	return NULL;
+}
+
+template <>
+bool BReverbModelImpl<IntSample>::process(const IntSample *inLeft, const IntSample *inRight, IntSample *outLeft, IntSample *outRight, Bit32u numSamples) {
+	produceOutput<IntSampleEx>(inLeft, inRight, outLeft, outRight, numSamples);
+	return true;
+}
+
+template <>
+bool BReverbModelImpl<IntSample>::process(const FloatSample *, const FloatSample *, FloatSample *, FloatSample *, Bit32u) {
+	return false;
+}
+
+template <>
+bool BReverbModelImpl<FloatSample>::process(const IntSample *, const IntSample *, IntSample *, IntSample *, Bit32u) {
+	return false;
+}
+
+template <>
+bool BReverbModelImpl<FloatSample>::process(const FloatSample *inLeft, const FloatSample *inRight, FloatSample *outLeft, FloatSample *outRight, Bit32u numSamples) {
+	produceOutput<FloatSample>(inLeft, inRight, outLeft, outRight, numSamples);
+	return true;
 }
 
 } // namespace MT32Emu
diff --git a/audio/softsynth/mt32/BReverbModel.h b/audio/softsynth/mt32/BReverbModel.h
index 8cfc5da..5b1d411 100644
--- a/audio/softsynth/mt32/BReverbModel.h
+++ b/audio/softsynth/mt32/BReverbModel.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -20,101 +20,27 @@
 
 #include "globals.h"
 #include "internals.h"
+#include "Enumerations.h"
 #include "Types.h"
 
 namespace MT32Emu {
 
-struct BReverbSettings {
-	const Bit32u numberOfAllpasses;
-	const Bit32u * const allpassSizes;
-	const Bit32u numberOfCombs;
-	const Bit32u * const combSizes;
-	const Bit32u * const outLPositions;
-	const Bit32u * const outRPositions;
-	const Bit8u * const filterFactors;
-	const Bit8u * const feedbackFactors;
-	const Bit8u * const dryAmps;
-	const Bit8u * const wetLevels;
-	const Bit8u lpfAmp;
-};
-
-class RingBuffer {
-protected:
-	Sample *buffer;
-	const Bit32u size;
-	Bit32u index;
-
-public:
-	RingBuffer(const Bit32u size);
-	virtual ~RingBuffer();
-	Sample next();
-	bool isEmpty() const;
-	void mute();
-};
-
-class AllpassFilter : public RingBuffer {
-public:
-	AllpassFilter(const Bit32u size);
-	Sample process(const Sample in);
-};
-
-class CombFilter : public RingBuffer {
-protected:
-	const Bit8u filterFactor;
-	Bit8u feedbackFactor;
-
-public:
-	CombFilter(const Bit32u size, const Bit8u useFilterFactor);
-	virtual void process(const Sample in);
-	Sample getOutputAt(const Bit32u outIndex) const;
-	void setFeedbackFactor(const Bit8u useFeedbackFactor);
-};
-
-class DelayWithLowPassFilter : public CombFilter {
-	Bit8u amp;
-
-public:
-	DelayWithLowPassFilter(const Bit32u useSize, const Bit8u useFilterFactor, const Bit8u useAmp);
-	void process(const Sample in);
-	void setFeedbackFactor(const Bit8u) {}
-};
-
-class TapDelayCombFilter : public CombFilter {
-	Bit32u outL;
-	Bit32u outR;
-
-public:
-	TapDelayCombFilter(const Bit32u useSize, const Bit8u useFilterFactor);
-	void process(const Sample in);
-	Sample getLeftOutput() const;
-	Sample getRightOutput() const;
-	void setOutputPositions(const Bit32u useOutL, const Bit32u useOutR);
-};
-
 class BReverbModel {
-	AllpassFilter **allpasses;
-	CombFilter **combs;
-
-	const BReverbSettings ¤tSettings;
-	const bool tapDelayMode;
-	Bit8u dryAmp;
-	Bit8u wetLevel;
-
-	static const BReverbSettings &getCM32L_LAPCSettings(const ReverbMode mode);
-	static const BReverbSettings &getMT32Settings(const ReverbMode mode);
-
 public:
-	BReverbModel(const ReverbMode mode, const bool mt32CompatibleModel = false);
-	~BReverbModel();
+	static BReverbModel *createBReverbModel(const ReverbMode mode, const bool mt32CompatibleModel, const RendererType rendererType);
+
+	virtual ~BReverbModel() {}
+	virtual bool isOpen() const = 0;
 	// After construction or a close(), open() must be called at least once before any other call (with the exception of close()).
-	void open();
+	virtual void open() = 0;
 	// 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, Bit32u numSamples);
-	bool isActive() const;
-	bool isMT32Compatible(const ReverbMode mode) const;
+	virtual void close() = 0;
+	virtual void mute() = 0;
+	virtual void setParameters(Bit8u time, Bit8u level) = 0;
+	virtual bool isActive() const = 0;
+	virtual bool isMT32Compatible(const ReverbMode mode) const = 0;
+	virtual bool process(const IntSample *inLeft, const IntSample *inRight, IntSample *outLeft, IntSample *outRight, Bit32u numSamples) = 0;
+	virtual bool process(const FloatSample *inLeft, const FloatSample *inRight, FloatSample *outLeft, FloatSample *outRight, Bit32u numSamples) = 0;
 };
 
 } // namespace MT32Emu
diff --git a/audio/softsynth/mt32/Enumerations.h b/audio/softsynth/mt32/Enumerations.h
index 9b0a35d..bb580ca 100644
--- a/audio/softsynth/mt32/Enumerations.h
+++ b/audio/softsynth/mt32/Enumerations.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -35,6 +35,12 @@
 #define MT32EMU_PARTIAL_STATE_NAME mt32emu_partial_state
 #define MT32EMU_PARTIAL_STATE(ident) MT32EMU_PS_##ident
 
+#define MT32EMU_SAMPLERATE_CONVERSION_QUALITY_NAME mt32emu_samplerate_conversion_quality
+#define MT32EMU_SAMPLERATE_CONVERSION_QUALITY(ident) MT32EMU_SRCQ_##ident
+
+#define MT32EMU_RENDERER_TYPE_NAME mt32emu_renderer_type
+#define MT32EMU_RENDERER_TYPE(ident) MT32EMU_RT_##ident
+
 #else /* #ifdef MT32EMU_C_ENUMERATIONS */
 
 #define MT32EMU_CPP_ENUMERATIONS_H
@@ -51,6 +57,12 @@
 #define MT32EMU_PARTIAL_STATE_NAME PartialState
 #define MT32EMU_PARTIAL_STATE(ident) PartialState_##ident
 
+#define MT32EMU_SAMPLERATE_CONVERSION_QUALITY_NAME SamplerateConversionQuality
+#define MT32EMU_SAMPLERATE_CONVERSION_QUALITY(ident) SamplerateConversionQuality_##ident
+
+#define MT32EMU_RENDERER_TYPE_NAME RendererType
+#define MT32EMU_RENDERER_TYPE(ident) RendererType_##ident
+
 namespace MT32Emu {
 
 #endif /* #ifdef MT32EMU_C_ENUMERATIONS */
@@ -73,7 +85,6 @@ enum MT32EMU_DAC_INPUT_MODE_NAME {
 	 * 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.
-	 * Output gain is ignored for both LA32 and reverb output.
 	 * Perfect for developers while debugging :)
 	 */
 	MT32EMU_DAC_INPUT_MODE(PURE),
@@ -134,6 +145,21 @@ enum MT32EMU_PARTIAL_STATE_NAME {
 	MT32EMU_PARTIAL_STATE(RELEASE)
 };
 
+enum MT32EMU_SAMPLERATE_CONVERSION_QUALITY_NAME {
+	/** Use this only when the speed is more important than the audio quality. */
+	MT32EMU_SAMPLERATE_CONVERSION_QUALITY(FASTEST),
+	MT32EMU_SAMPLERATE_CONVERSION_QUALITY(FAST),
+	MT32EMU_SAMPLERATE_CONVERSION_QUALITY(GOOD),
+	MT32EMU_SAMPLERATE_CONVERSION_QUALITY(BEST)
+};
+
+enum MT32EMU_RENDERER_TYPE_NAME {
+	/** Use 16-bit signed samples in the renderer and the accurate wave generator model based on logarithmic fixed-point computations and LUTs. Maximum emulation accuracy and speed. */
+	MT32EMU_RENDERER_TYPE(BIT16S),
+	/** Use float samples in the renderer and simplified wave generator model. Maximum output quality and minimum noise. */
+	MT32EMU_RENDERER_TYPE(FLOAT)
+};
+
 #ifndef MT32EMU_C_ENUMERATIONS
 
 } // namespace MT32Emu
@@ -152,4 +178,10 @@ enum MT32EMU_PARTIAL_STATE_NAME {
 #undef MT32EMU_PARTIAL_STATE_NAME
 #undef MT32EMU_PARTIAL_STATE
 
+#undef MT32EMU_SAMPLERATE_CONVERSION_QUALITY_NAME
+#undef MT32EMU_SAMPLERATE_CONVERSION_QUALITY
+
+#undef MT32EMU_RENDERER_TYPE_NAME
+#undef MT32EMU_RENDERER_TYPE
+
 #endif /* #if (!defined MT32EMU_CPP_ENUMERATIONS_H && !defined MT32EMU_C_ENUMERATIONS) || (!defined MT32EMU_C_ENUMERATIONS_H && defined MT32EMU_C_ENUMERATIONS) */
diff --git a/audio/softsynth/mt32/File.cpp b/audio/softsynth/mt32/File.cpp
index 0716482..a5967b4 100644
--- a/audio/softsynth/mt32/File.cpp
+++ b/audio/softsynth/mt32/File.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/File.h b/audio/softsynth/mt32/File.h
index c9a7d58..91a0a7f 100644
--- a/audio/softsynth/mt32/File.h
+++ b/audio/softsynth/mt32/File.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/FileStream.cpp b/audio/softsynth/mt32/FileStream.cpp
index 768c5fc..081f41a 100644
--- a/audio/softsynth/mt32/FileStream.cpp
+++ b/audio/softsynth/mt32/FileStream.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -15,12 +15,26 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#ifdef MT32EMU_SHARED
+#include <locale>
+#endif
+
 #include "internals.h"
 
 #include "FileStream.h"
 
 namespace MT32Emu {
 
+static inline void configureSystemLocale() {
+#ifdef MT32EMU_SHARED
+	static bool configured = false;
+
+	if (configured) return;
+	configured = true;
+	std::locale::global(std::locale(""));
+#endif
+}
+
 using std::ios_base;
 
 FileStream::FileStream() : ifsp(*new std::ifstream), data(NULL), size(0)
@@ -70,6 +84,7 @@ const Bit8u *FileStream::getData() {
 }
 
 bool FileStream::open(const char *filename) {
+	configureSystemLocale();
 	ifsp.clear();
 	ifsp.open(filename, ios_base::in | ios_base::binary);
 	return !ifsp.fail();
diff --git a/audio/softsynth/mt32/FileStream.h b/audio/softsynth/mt32/FileStream.h
index 2de6e80..ea5de69 100644
--- a/audio/softsynth/mt32/FileStream.h
+++ b/audio/softsynth/mt32/FileStream.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp b/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp
index 824204e..6ff4aa3 100644
--- a/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp
+++ b/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -15,11 +15,13 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef MT32EMU_LA32_WAVE_GENERATOR_CPP
-#error This file should be included from LA32WaveGenerator.cpp only.
-#endif
+#include <cstddef>
 
+#include "internals.h"
+
+#include "LA32FloatWaveGenerator.h"
 #include "mmath.h"
+#include "Tables.h"
 
 namespace MT32Emu {
 
@@ -27,7 +29,7 @@ static const float MIDDLE_CUTOFF_VALUE = 128.0f;
 static const float RESONANCE_DECAY_THRESHOLD_CUTOFF_VALUE = 144.0f;
 static const float MAX_CUTOFF_VALUE = 240.0f;
 
-float LA32WaveGenerator::getPCMSample(unsigned int position) {
+float LA32FloatWaveGenerator::getPCMSample(unsigned int position) {
 	if (position >= pcmWaveLength) {
 		if (!pcmWaveLooped) {
 			return 0;
@@ -39,7 +41,7 @@ float LA32WaveGenerator::getPCMSample(unsigned int position) {
 	return ((pcmSample & 32768) == 0) ? sampleValue : -sampleValue;
 }
 
-void LA32WaveGenerator::initSynth(const bool useSawtoothWaveform, const Bit8u usePulseWidth, const Bit8u useResonance) {
+void LA32FloatWaveGenerator::initSynth(const bool useSawtoothWaveform, const Bit8u usePulseWidth, const Bit8u useResonance) {
 	sawtoothWaveform = useSawtoothWaveform;
 	pulseWidth = usePulseWidth;
 	resonance = useResonance;
@@ -51,7 +53,7 @@ void LA32WaveGenerator::initSynth(const bool useSawtoothWaveform, const Bit8u us
 	active = true;
 }
 
-void LA32WaveGenerator::initPCM(const Bit16s * const usePCMWaveAddress, const Bit32u usePCMWaveLength, const bool usePCMWaveLooped, const bool usePCMWaveInterpolated) {
+void LA32FloatWaveGenerator::initPCM(const Bit16s * const usePCMWaveAddress, const Bit32u usePCMWaveLength, const bool usePCMWaveLooped, const bool usePCMWaveInterpolated) {
 	pcmWaveAddress = usePCMWaveAddress;
 	pcmWaveLength = usePCMWaveLength;
 	pcmWaveLooped = usePCMWaveLooped;
@@ -64,7 +66,7 @@ void LA32WaveGenerator::initPCM(const Bit16s * const usePCMWaveAddress, const Bi
 // ampVal - Logarithmic amp of the wave generator
 // pitch - Logarithmic frequency of the resulting wave
 // cutoffRampVal - Composed of the base cutoff in range [78..178] left-shifted by 18 bits and the TVF modifier
-float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pitch, const Bit32u cutoffRampVal) {
+float LA32FloatWaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pitch, const Bit32u cutoffRampVal) {
 	if (!active) {
 		return 0.0f;
 	}
@@ -87,7 +89,7 @@ float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pi
 	if (isPCMWave()) {
 		// Render PCM waveform
 		int len = pcmWaveLength;
-		int intPCMPosition = (int)pcmPosition;
+		int intPCMPosition = int(pcmPosition);
 		if (intPCMPosition >= len && !pcmWaveLooped) {
 			// We're now past the end of a non-looping PCM waveform so it's time to die.
 			deactivate();
@@ -108,7 +110,7 @@ float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pi
 
 		float newPCMPosition = pcmPosition + positionDelta;
 		if (pcmWaveLooped) {
-			newPCMPosition = fmod(newPCMPosition, (float)pcmWaveLength);
+			newPCMPosition = fmod(newPCMPosition, float(pcmWaveLength));
 		}
 		pcmPosition = newPCMPosition;
 	} else {
@@ -163,15 +165,9 @@ float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pi
 			hLen = 0.0f;
 		}
 
-		// Ignore pulsewidths too high for given freq and cutoff
-		float lLen = waveLen - hLen - 2 * cosineLen;
-		if (lLen < 0.0f) {
-			lLen = 0.0f;
-		}
-
 		// Correct resAmp for cutoff in range 50..66
-		if ((cutoffVal >= 128.0f) && (cutoffVal < 144.0f)) {
-			resAmp *= sin(FLOAT_PI * (cutoffVal - 128.0f) / 32.0f);
+		if ((cutoffVal >= MIDDLE_CUTOFF_VALUE) && (cutoffVal < RESONANCE_DECAY_THRESHOLD_CUTOFF_VALUE)) {
+			resAmp *= sin(FLOAT_PI * (cutoffVal - MIDDLE_CUTOFF_VALUE) / 32.0f);
 		}
 
 		// Produce filtered square wave with 2 cosine waves on slopes
@@ -195,11 +191,11 @@ float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pi
 			sample = -1.f;
 		}
 
-		if (cutoffVal < 128.0f) {
+		if (cutoffVal < MIDDLE_CUTOFF_VALUE) {
 
 			// Attenuate samples below cutoff 50
 			// Found by sample analysis
-			sample *= EXP2F(-0.125f * (128.0f - cutoffVal));
+			sample *= EXP2F(-0.125f * (MIDDLE_CUTOFF_VALUE - cutoffVal));
 		} else {
 
 			// Add resonance sine. Effective for cutoff > 50 only
@@ -273,26 +269,26 @@ float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pi
 	return sample;
 }
 
-void LA32WaveGenerator::deactivate() {
+void LA32FloatWaveGenerator::deactivate() {
 	active = false;
 }
 
-bool LA32WaveGenerator::isActive() const {
+bool LA32FloatWaveGenerator::isActive() const {
 	return active;
 }
 
-bool LA32WaveGenerator::isPCMWave() const {
+bool LA32FloatWaveGenerator::isPCMWave() const {
 	return pcmWaveAddress != NULL;
 }
 
-void LA32PartialPair::init(const bool useRingModulated, const bool useMixed) {
+void LA32FloatPartialPair::init(const bool useRingModulated, const bool useMixed) {
 	ringModulated = useRingModulated;
 	mixed = useMixed;
 	masterOutputSample = 0.0f;
 	slaveOutputSample = 0.0f;
 }
 
-void LA32PartialPair::initSynth(const PairType useMaster, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) {
+void LA32FloatPartialPair::initSynth(const PairType useMaster, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) {
 	if (useMaster == MASTER) {
 		master.initSynth(sawtoothWaveform, pulseWidth, resonance);
 	} else {
@@ -300,7 +296,7 @@ void LA32PartialPair::initSynth(const PairType useMaster, const bool sawtoothWav
 	}
 }
 
-void LA32PartialPair::initPCM(const PairType useMaster, const Bit16s *pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped) {
+void LA32FloatPartialPair::initPCM(const PairType useMaster, const Bit16s *pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped) {
 	if (useMaster == MASTER) {
 		master.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, true);
 	} else {
@@ -308,7 +304,7 @@ void LA32PartialPair::initPCM(const PairType useMaster, const Bit16s *pcmWaveAdd
 	}
 }
 
-void LA32PartialPair::generateNextSample(const PairType useMaster, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff) {
+void LA32FloatPartialPair::generateNextSample(const PairType useMaster, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff) {
 	if (useMaster == MASTER) {
 		masterOutputSample = master.generateNextSample(amp, pitch, cutoff);
 	} else {
@@ -325,9 +321,14 @@ static inline float produceDistortedSample(float sample) {
 	return sample;
 }
 
-float LA32PartialPair::nextOutSample() {
+float LA32FloatPartialPair::nextOutSample() {
+	// Note, LA32FloatWaveGenerator produces each sample normalised in terms of a single playing partial,
+	// so the unity sample corresponds to the internal LA32 logarithmic fixed-point unity sample.
+	// However, each logarithmic sample is then unlogged to a 14-bit signed integer value, i.e. the max absolute value is 8192.
+	// Thus, considering that samples are further mapped to a 16-bit signed integer,
+	// we apply a conversion factor 0.25 to produce properly normalised float samples.
 	if (!ringModulated) {
-		return masterOutputSample + slaveOutputSample;
+		return 0.25f * (masterOutputSample + slaveOutputSample);
 	}
 	/*
 	 * SEMI-CONFIRMED: Ring modulation model derived from sample analysis of specially constructed patches which exploit distortion.
@@ -338,10 +339,10 @@ float LA32PartialPair::nextOutSample() {
 	 * Most probably the overflow is caused by limited precision of the multiplication circuit as the very similar distortion occurs with panning.
 	 */
 	float ringModulatedSample = produceDistortedSample(masterOutputSample) * produceDistortedSample(slaveOutputSample);
-	return mixed ? masterOutputSample + ringModulatedSample : ringModulatedSample;
+	return 0.25f * (mixed ? masterOutputSample + ringModulatedSample : ringModulatedSample);
 }
 
-void LA32PartialPair::deactivate(const PairType useMaster) {
+void LA32FloatPartialPair::deactivate(const PairType useMaster) {
 	if (useMaster == MASTER) {
 		master.deactivate();
 		masterOutputSample = 0.0f;
@@ -351,7 +352,7 @@ void LA32PartialPair::deactivate(const PairType useMaster) {
 	}
 }
 
-bool LA32PartialPair::isActive(const PairType useMaster) const {
+bool LA32FloatPartialPair::isActive(const PairType useMaster) const {
 	return useMaster == MASTER ? master.isActive() : slave.isActive();
 }
 
diff --git a/audio/softsynth/mt32/LA32FloatWaveGenerator.h b/audio/softsynth/mt32/LA32FloatWaveGenerator.h
index 89b6fe4..7e92d0a 100644
--- a/audio/softsynth/mt32/LA32FloatWaveGenerator.h
+++ b/audio/softsynth/mt32/LA32FloatWaveGenerator.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -15,9 +15,13 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef MT32EMU_LA32_WAVE_GENERATOR_H
-#error This file should be included from LA32WaveGenerator.h only.
-#endif
+#ifndef MT32EMU_LA32_FLOAT_WAVE_GENERATOR_H
+#define MT32EMU_LA32_FLOAT_WAVE_GENERATOR_H
+
+#include "globals.h"
+#include "internals.h"
+#include "Types.h"
+#include "LA32WaveGenerator.h"
 
 namespace MT32Emu {
 
@@ -29,7 +33,7 @@ namespace MT32Emu {
  * The beginning and the ending of the resonant sine is multiplied by a cosine window.
  * To synthesise sawtooth waves, the resulting square wave is multiplied by synchronous cosine wave.
  */
-class LA32WaveGenerator {
+class LA32FloatWaveGenerator {
 	//***************************************************************************
 	//  The local copy of partial parameters below
 	//***************************************************************************
@@ -88,23 +92,17 @@ public:
 
 	// Return true if the WG engine generates PCM wave samples
 	bool isPCMWave() const;
-}; // class LA32WaveGenerator
+}; // class LA32FloatWaveGenerator
 
-// LA32PartialPair contains a structure of two partials being mixed / ring modulated
-class LA32PartialPair {
-	LA32WaveGenerator master;
-	LA32WaveGenerator slave;
+class LA32FloatPartialPair : public LA32PartialPair {
+	LA32FloatWaveGenerator master;
+	LA32FloatWaveGenerator slave;
 	bool ringModulated;
 	bool mixed;
 	float masterOutputSample;
 	float slaveOutputSample;
 
 public:
-	enum PairType {
-		MASTER,
-		SLAVE
-	};
-
 	// ringModulated should be set to false for the structures with mixing or stereo output
 	// ringModulated should be set to true for the structures with ring modulation
 	// mixed is used for the structures with ring modulation and indicates whether the master partial output is mixed to the ring modulator output
@@ -127,6 +125,8 @@ public:
 
 	// Return active state of the WG engine
 	bool isActive(const PairType master) const;
-}; // class LA32PartialPair
+}; // class LA32FloatPartialPair
 
 } // namespace MT32Emu
+
+#endif // #ifndef MT32EMU_LA32_FLOAT_WAVE_GENERATOR_H
diff --git a/audio/softsynth/mt32/LA32Ramp.cpp b/audio/softsynth/mt32/LA32Ramp.cpp
index a4da4f5..9dcf143 100644
--- a/audio/softsynth/mt32/LA32Ramp.cpp
+++ b/audio/softsynth/mt32/LA32Ramp.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -56,8 +56,8 @@ We haven't fully explored:
 namespace MT32Emu {
 
 // SEMI-CONFIRMED from sample analysis.
-const int TARGET_MULT = 0x40000;
-const unsigned int MAX_CURRENT = 0xFF * TARGET_MULT;
+const unsigned int TARGET_SHIFTS = 18;
+const unsigned int MAX_CURRENT = 0xFF << TARGET_SHIFTS;
 
 // We simulate the delay in handling "target was reached" interrupts by waiting
 // this many samples before setting interruptRaised.
@@ -96,7 +96,7 @@ void LA32Ramp::startRamp(Bit8u target, Bit8u increment) {
 		largeIncrement++;
 	}
 
-	largeTarget = target * TARGET_MULT;
+	largeTarget = target << TARGET_SHIFTS;
 	interruptCountdown = 0;
 	interruptRaised = false;
 }
@@ -152,4 +152,13 @@ void LA32Ramp::reset() {
 	interruptRaised = false;
 }
 
+// This is actually beyond the LA32 ramp interface.
+// Instead of polling the current value, MCU receives an interrupt when a ramp completes.
+// However, this is a simple way to work around the specific behaviour of TVA
+// when in sustain phase which one normally wants to avoid.
+// See TVA::recalcSustain() for details.
+bool LA32Ramp::isBelowCurrent(Bit8u target) const {
+	return Bit32u(target << TARGET_SHIFTS) < current;
+}
+
 } // namespace MT32Emu
diff --git a/audio/softsynth/mt32/LA32Ramp.h b/audio/softsynth/mt32/LA32Ramp.h
index 5e4ddf7..959a1ad 100644
--- a/audio/softsynth/mt32/LA32Ramp.h
+++ b/audio/softsynth/mt32/LA32Ramp.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -39,6 +39,7 @@ public:
 	Bit32u nextValue();
 	bool checkInterrupt();
 	void reset();
+	bool isBelowCurrent(Bit8u target) const;
 };
 
 } // namespace MT32Emu
diff --git a/audio/softsynth/mt32/LA32WaveGenerator.cpp b/audio/softsynth/mt32/LA32WaveGenerator.cpp
index a9c425b..f6f6928 100644
--- a/audio/softsynth/mt32/LA32WaveGenerator.cpp
+++ b/audio/softsynth/mt32/LA32WaveGenerator.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -22,12 +22,6 @@
 #include "LA32WaveGenerator.h"
 #include "Tables.h"
 
-#if MT32EMU_USE_FLOAT_SAMPLES
-#define MT32EMU_LA32_WAVE_GENERATOR_CPP
-#include "LA32FloatWaveGenerator.cpp"
-#undef MT32EMU_LA32_WAVE_GENERATOR_CPP
-#else
-
 namespace MT32Emu {
 
 static const Bit32u SINE_SEGMENT_RELATIVE_LENGTH = 1 << 18;
@@ -343,12 +337,12 @@ Bit32u LA32WaveGenerator::getPCMInterpolationFactor() const {
 	return pcmInterpolationFactor;
 }
 
-void LA32PartialPair::init(const bool useRingModulated, const bool useMixed) {
+void LA32IntPartialPair::init(const bool useRingModulated, const bool useMixed) {
 	ringModulated = useRingModulated;
 	mixed = useMixed;
 }
 
-void LA32PartialPair::initSynth(const PairType useMaster, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) {
+void LA32IntPartialPair::initSynth(const PairType useMaster, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) {
 	if (useMaster == MASTER) {
 		master.initSynth(sawtoothWaveform, pulseWidth, resonance);
 	} else {
@@ -356,7 +350,7 @@ void LA32PartialPair::initSynth(const PairType useMaster, const bool sawtoothWav
 	}
 }
 
-void LA32PartialPair::initPCM(const PairType useMaster, const Bit16s *pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped) {
+void LA32IntPartialPair::initPCM(const PairType useMaster, const Bit16s *pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped) {
 	if (useMaster == MASTER) {
 		master.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, true);
 	} else {
@@ -364,7 +358,7 @@ void LA32PartialPair::initPCM(const PairType useMaster, const Bit16s *pcmWaveAdd
 	}
 }
 
-void LA32PartialPair::generateNextSample(const PairType useMaster, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff) {
+void LA32IntPartialPair::generateNextSample(const PairType useMaster, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff) {
 	if (useMaster == MASTER) {
 		master.generateNextSample(amp, pitch, cutoff);
 	} else {
@@ -372,7 +366,7 @@ void LA32PartialPair::generateNextSample(const PairType useMaster, const Bit32u
 	}
 }
 
-Bit16s LA32PartialPair::unlogAndMixWGOutput(const LA32WaveGenerator &wg) {
+Bit16s LA32IntPartialPair::unlogAndMixWGOutput(const LA32WaveGenerator &wg) {
 	if (!wg.isActive()) {
 		return 0;
 	}
@@ -384,22 +378,16 @@ Bit16s LA32PartialPair::unlogAndMixWGOutput(const LA32WaveGenerator &wg) {
 	return firstSample + secondSample;
 }
 
-Bit16s LA32PartialPair::nextOutSample() {
+static inline Bit16s produceDistortedSample(Bit16s sample) {
+	return ((sample & 0x2000) == 0) ? Bit16s(sample & 0x1fff) : Bit16s(sample | ~0x1fff);
+}
+
+Bit16s LA32IntPartialPair::nextOutSample() {
 	if (!ringModulated) {
 		return unlogAndMixWGOutput(master) + unlogAndMixWGOutput(slave);
 	}
 
-	/*
-	 * SEMI-CONFIRMED: Ring modulation model derived from sample analysis of specially constructed patches which exploit distortion.
-	 * LA32 ring modulator found to produce distorted output in case if the absolute value of maximal amplitude of one of the input partials exceeds 8191.
-	 * This is easy to reproduce using synth partials with resonance values close to the maximum. It looks like an integer overflow happens in this case.
-	 * As the distortion is strictly bound to the amplitude of the complete mixed square + resonance wave in the linear space,
-	 * it is reasonable to assume the ring modulation is performed also in the linear space by sample multiplication.
-	 * Most probably the overflow is caused by limited precision of the multiplication circuit as the very similar distortion occurs with panning.
-	 */
-	Bit16s nonOverdrivenMasterSample = unlogAndMixWGOutput(master); // Store master partial sample for further mixing
-	Bit16s masterSample = nonOverdrivenMasterSample << 2;
-	masterSample >>= 2;
+	Bit16s masterSample = unlogAndMixWGOutput(master); // Store master partial sample for further mixing
 
 	/* SEMI-CONFIRMED from sample analysis:
 	 * We observe that for partial structures with ring modulation the interpolation is not applied to the slave PCM partial.
@@ -407,13 +395,20 @@ Bit16s LA32PartialPair::nextOutSample() {
 	 * is borrowed by the ring modulation circuit (or the LA32 chip has a similar lack of resources assigned to each partial pair).
 	 */
 	Bit16s slaveSample = slave.isPCMWave() ? LA32Utilites::unlog(slave.getOutputLogSample(true)) : unlogAndMixWGOutput(slave);
-	slaveSample <<= 2;
-	slaveSample >>= 2;
-	Bit16s ringModulatedSample = Bit16s((Bit32s(masterSample) * Bit32s(slaveSample)) >> 13);
-	return mixed ? nonOverdrivenMasterSample + ringModulatedSample : ringModulatedSample;
+
+	/* SEMI-CONFIRMED: Ring modulation model derived from sample analysis of specially constructed patches which exploit distortion.
+	 * LA32 ring modulator found to produce distorted output in case if the absolute value of maximal amplitude of one of the input partials exceeds 8191.
+	 * This is easy to reproduce using synth partials with resonance values close to the maximum. It looks like an integer overflow happens in this case.
+	 * As the distortion is strictly bound to the amplitude of the complete mixed square + resonance wave in the linear space,
+	 * it is reasonable to assume the ring modulation is performed also in the linear space by sample multiplication.
+	 * Most probably the overflow is caused by limited precision of the multiplication circuit as the very similar distortion occurs with panning.
+	 */
+	Bit16s ringModulatedSample = Bit16s((Bit32s(produceDistortedSample(masterSample)) * Bit32s(produceDistortedSample(slaveSample))) >> 13);
+
+	return mixed ? masterSample + ringModulatedSample : ringModulatedSample;
 }
 
-void LA32PartialPair::deactivate(const PairType useMaster) {
+void LA32IntPartialPair::deactivate(const PairType useMaster) {
 	if (useMaster == MASTER) {
 		master.deactivate();
 	} else {
@@ -421,10 +416,8 @@ void LA32PartialPair::deactivate(const PairType useMaster) {
 	}
 }
 
-bool LA32PartialPair::isActive(const PairType useMaster) const {
+bool LA32IntPartialPair::isActive(const PairType useMaster) const {
 	return useMaster == MASTER ? master.isActive() : slave.isActive();
 }
 
 } // namespace MT32Emu
-
-#endif // #if MT32EMU_USE_FLOAT_SAMPLES
diff --git a/audio/softsynth/mt32/LA32WaveGenerator.h b/audio/softsynth/mt32/LA32WaveGenerator.h
index 6a40e44..c206daa 100644
--- a/audio/softsynth/mt32/LA32WaveGenerator.h
+++ b/audio/softsynth/mt32/LA32WaveGenerator.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -22,10 +22,6 @@
 #include "internals.h"
 #include "Types.h"
 
-#if MT32EMU_USE_FLOAT_SAMPLES
-#include "LA32FloatWaveGenerator.h"
-#else
-
 namespace MT32Emu {
 
 /**
@@ -208,6 +204,30 @@ public:
 
 // LA32PartialPair contains a structure of two partials being mixed / ring modulated
 class LA32PartialPair {
+public:
+	enum PairType {
+		MASTER,
+		SLAVE
+	};
+
+	virtual ~LA32PartialPair() {}
+
+	// ringModulated should be set to false for the structures with mixing or stereo output
+	// ringModulated should be set to true for the structures with ring modulation
+	// mixed is used for the structures with ring modulation and indicates whether the master partial output is mixed to the ring modulator output
+	virtual void init(const bool ringModulated, const bool mixed) = 0;
+
+	// Initialise the WG engine for generation of synth partial samples and set up the invariant parameters
+	virtual void initSynth(const PairType master, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) = 0;
+
+	// Initialise the WG engine for generation of PCM partial samples and set up the invariant parameters
+	virtual void initPCM(const PairType master, const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped) = 0;
+
+	// Deactivate the WG engine
+	virtual void deactivate(const PairType master) = 0;
+}; // class LA32PartialPair
+
+class LA32IntPartialPair : public LA32PartialPair {
 	LA32WaveGenerator master;
 	LA32WaveGenerator slave;
 	bool ringModulated;
@@ -216,11 +236,6 @@ class LA32PartialPair {
 	static Bit16s unlogAndMixWGOutput(const LA32WaveGenerator &wg);
 
 public:
-	enum PairType {
-		MASTER,
-		SLAVE
-	};
-
 	// ringModulated should be set to false for the structures with mixing or stereo output
 	// ringModulated should be set to true for the structures with ring modulation
 	// mixed is used for the structures with ring modulation and indicates whether the master partial output is mixed to the ring modulator output
@@ -235,7 +250,8 @@ public:
 	// Update parameters with respect to TVP, TVA and TVF, and generate next sample
 	void generateNextSample(const PairType master, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff);
 
-	// Perform mixing / ring modulation and return the result
+	// Perform mixing / ring modulation of WG output and return the result
+	// Although, LA32 applies panning itself, we assume it is applied in the mixer, not within a pair
 	Bit16s nextOutSample();
 
 	// Deactivate the WG engine
@@ -243,10 +259,8 @@ public:
 
 	// Return active state of the WG engine
 	bool isActive(const PairType master) const;
-}; // class LA32PartialPair
+}; // class LA32IntPartialPair
 
 } // namespace MT32Emu
 
-#endif // #if MT32EMU_USE_FLOAT_SAMPLES
-
 #endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H
diff --git a/audio/softsynth/mt32/MemoryRegion.h b/audio/softsynth/mt32/MemoryRegion.h
index f8d7da1..807f147 100644
--- a/audio/softsynth/mt32/MemoryRegion.h
+++ b/audio/softsynth/mt32/MemoryRegion.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/MidiEventQueue.h b/audio/softsynth/mt32/MidiEventQueue.h
index 1a5ff0a..c5174d6 100644
--- a/audio/softsynth/mt32/MidiEventQueue.h
+++ b/audio/softsynth/mt32/MidiEventQueue.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/MidiStreamParser.cpp b/audio/softsynth/mt32/MidiStreamParser.cpp
index f9ead33..a426a20 100644
--- a/audio/softsynth/mt32/MidiStreamParser.cpp
+++ b/audio/softsynth/mt32/MidiStreamParser.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -119,7 +119,7 @@ void MidiStreamParserImpl::parseStream(const Bit8u *stream, Bit32u length) {
 
 void MidiStreamParserImpl::processShortMessage(const Bit32u message) {
 	// Adds running status to the MIDI message if it doesn't contain one
-	Bit8u status = Bit8u(message);
+	Bit8u status = Bit8u(message & 0xFF);
 	if (0xF8 <= status) {
 		midiReceiver.handleSystemRealtimeMessage(status);
 	} else if (processStatusByte(status)) {
diff --git a/audio/softsynth/mt32/MidiStreamParser.h b/audio/softsynth/mt32/MidiStreamParser.h
index bd31b77..881ec03 100644
--- a/audio/softsynth/mt32/MidiStreamParser.h
+++ b/audio/softsynth/mt32/MidiStreamParser.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp
index 9b5119e..9c85ce5 100644
--- a/audio/softsynth/mt32/Part.cpp
+++ b/audio/softsynth/mt32/Part.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -340,10 +340,13 @@ void RhythmPart::setPan(unsigned int midiPan) {
 void Part::setPan(unsigned int midiPan) {
 	// NOTE: Panning is inverted compared to GM.
 
-	// CM-32L: Divide by 8.5
-	patchTemp->panpot = Bit8u((midiPan << 3) / 68);
-	// FIXME: MT-32: Divide by 9
-	//patchTemp->panpot = Bit8u(midiPan / 9);
+	if (synth->controlROMFeatures->quirkPanMult) {
+		// MT-32: Divide by 9
+		patchTemp->panpot = Bit8u(midiPan / 9);
+	} else {
+		// CM-32L: Divide by 8.5
+		patchTemp->panpot = Bit8u((midiPan << 3) / 68);
+	}
 
 	//synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot);
 }
@@ -352,6 +355,10 @@ void Part::setPan(unsigned int midiPan) {
  * Applies key shift to a MIDI key and converts it into an internal key value in the range 12-108.
  */
 unsigned int Part::midiKeyToKey(unsigned int midiKey) {
+	if (synth->controlROMFeatures->quirkKeyShift) {
+		// NOTE: On MT-32 GEN0, key isn't adjusted, and keyShift is applied further in TVP, unlike newer units:
+		return midiKey;
+	}
 	int key = midiKey + patchTemp->patch.keyShift;
 	if (key < 36) {
 		// After keyShift is applied, key < 36, so move up by octaves
diff --git a/audio/softsynth/mt32/Part.h b/audio/softsynth/mt32/Part.h
index f517158..a4de106 100644
--- a/audio/softsynth/mt32/Part.h
+++ b/audio/softsynth/mt32/Part.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp
index 6afef36..60c06b7 100644
--- a/audio/softsynth/mt32/Partial.cpp
+++ b/audio/softsynth/mt32/Partial.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -21,6 +21,7 @@
 
 #include "Partial.h"
 #include "Part.h"
+#include "PartialManager.h"
 #include "Poly.h"
 #include "Synth.h"
 #include "Tables.h"
@@ -33,10 +34,26 @@ namespace MT32Emu {
 static const Bit8u PAN_NUMERATOR_MASTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7};
 static const Bit8u PAN_NUMERATOR_SLAVE[]  = {0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7};
 
-static const Bit32s PAN_FACTORS[] = {0, 18, 37, 55, 73, 91, 110, 128, 146, 165, 183, 201, 219, 238, 256};
+// We assume the pan is applied using the same 13-bit multiplier circuit that is also used for ring modulation
+// because of the observed sample overflow, so the panSetting values are likely mapped in a similar way via a LUT.
+// FIXME: Sample analysis suggests that the use of panSetting is linear, but there are some quirks that still need to be resolved.
+static Bit32s getPANFactor(Bit32s panSetting) {
+	static const Bit32u PAN_FACTORS_COUNT = 15;
+	static Bit32s PAN_FACTORS[PAN_FACTORS_COUNT];
+	static bool firstRun = true;
+
+	if (firstRun) {
+		firstRun = false;
+		for (Bit32u i = 1; i < PAN_FACTORS_COUNT; i++) {
+			PAN_FACTORS[i] = Bit32s(0.5 + i * 8192.0 / double(PAN_FACTORS_COUNT - 1));
+		}
+	}
+	return PAN_FACTORS[panSetting];
+}
 
-Partial::Partial(Synth *useSynth, int useDebugPartialNum) :
-	synth(useSynth), debugPartialNum(useDebugPartialNum), sampleNum(0) {
+Partial::Partial(Synth *useSynth, int usePartialIndex) :
+	synth(useSynth), partialIndex(usePartialIndex), sampleNum(0),
+	floatMode(useSynth->getSelectedRendererType() == RendererType_FLOAT) {
 	// Initialisation of tva, tvp and tvf uses 'this' pointer
 	// and thus should not be in the initializer list to avoid a compiler warning
 	tva = new TVA(this, &ampRamp);
@@ -45,9 +62,20 @@ Partial::Partial(Synth *useSynth, int useDebugPartialNum) :
 	ownerPart = -1;
 	poly = NULL;
 	pair = NULL;
+	switch (synth->getSelectedRendererType()) {
+	case RendererType_BIT16S:
+		la32Pair = new LA32IntPartialPair;
+		break;
+	case RendererType_FLOAT:
+		la32Pair = new LA32FloatPartialPair;
+		break;
+	default:
+		la32Pair = NULL;
+	}
 }
 
 Partial::~Partial() {
+	delete la32Pair;
 	delete tva;
 	delete tvp;
 	delete tvf;
@@ -55,7 +83,7 @@ Partial::~Partial() {
 
 // Only used for debugging purposes
 int Partial::debugGetPartialNum() const {
-	return debugPartialNum;
+	return partialIndex;
 }
 
 // Only used for debugging purposes
@@ -85,6 +113,7 @@ void Partial::deactivate() {
 		return;
 	}
 	ownerPart = -1;
+	synth->partialManager->partialDeactivated(partialIndex);
 	if (poly != NULL) {
 		poly->partialDeactivated(this);
 	}
@@ -93,9 +122,9 @@ void Partial::deactivate() {
 	synth->printPartialUsage(sampleNum);
 #endif
 	if (isRingModulatingSlave()) {
-		pair->la32Pair.deactivate(LA32PartialPair::SLAVE);
+		pair->la32Pair->deactivate(LA32PartialPair::SLAVE);
 	} else {
-		la32Pair.deactivate(LA32PartialPair::MASTER);
+		la32Pair->deactivate(LA32PartialPair::MASTER);
 		if (hasRingModulatingSlave()) {
 			pair->deactivate();
 			pair = NULL;
@@ -108,7 +137,7 @@ void Partial::deactivate() {
 
 void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *usePatchCache, const MemParams::RhythmTemp *rhythmTemp, Partial *pairPartial) {
 	if (usePoly == NULL || usePatchCache == NULL) {
-		synth->printDebug("[Partial %d] *** Error: Starting partial for owner %d, usePoly=%s, usePatchCache=%s", debugPartialNum, ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", usePatchCache == NULL ? "*** NULL ***" : "OK");
+		synth->printDebug("[Partial %d] *** Error: Starting partial for owner %d, usePoly=%s, usePatchCache=%s", partialIndex, ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", usePatchCache == NULL ? "*** NULL ***" : "OK");
 		return;
 	}
 	patchCache = usePatchCache;
@@ -137,17 +166,17 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us
 	leftPanValue = synth->reversedStereoEnabled ? 14 - panSetting : panSetting;
 	rightPanValue = 14 - leftPanValue;
 
-#if !MT32EMU_USE_FLOAT_SAMPLES
-	leftPanValue = PAN_FACTORS[leftPanValue];
-	rightPanValue = PAN_FACTORS[rightPanValue];
-#endif
+	if (!floatMode) {
+		leftPanValue = getPANFactor(leftPanValue);
+		rightPanValue = getPANFactor(rightPanValue);
+	}
 
 	// SEMI-CONFIRMED: From sample analysis:
 	// Found that timbres with 3 or 4 partials (i.e. one using two partial pairs) are mixed in two different ways.
 	// Either partial pairs are added or subtracted, it depends on how the partial pairs are allocated.
 	// It seems that partials are grouped into quarters and if the partial pairs are allocated in different quarters the subtraction happens.
 	// Though, this matters little for the majority of timbres, it becomes crucial for timbres which contain several partials that sound very close.
-	// In this case that timbre can sound totally different depending of the way it is mixed up.
+	// In this case that timbre can sound totally different depending on the way it is mixed up.
 	// Most easily this effect can be displayed with the help of a special timbre consisting of several identical square wave partials (3 or 4).
 	// Say, it is 3-partial timbre. Just play any two notes simultaneously and the polys very probably are mixed differently.
 	// Moreover, the partial allocator retains the last partial assignment it did and all the subsequent notes will sound the same as the last released one.
@@ -155,8 +184,7 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us
 	// whole-quarter assignment or after some partials got aborted, even 4-partial timbres can be found sounding differently.
 	// This behaviour is also confirmed with two more special timbres: one with identical sawtooth partials, and one with PCM wave 02.
 	// For my personal taste, this behaviour rather enriches the sounding and should be emulated.
-	// Also, the current partial allocator model probably needs to be refined.
-	if (debugPartialNum & 8) {
+	if (partialIndex & 4) {
 		leftPanValue = -leftPanValue;
 		rightPanValue = -rightPanValue;
 	}
@@ -192,11 +220,11 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us
 	LA32PartialPair *useLA32Pair;
 	if (isRingModulatingSlave()) {
 		pairType = LA32PartialPair::SLAVE;
-		useLA32Pair = &pair->la32Pair;
+		useLA32Pair = pair->la32Pair;
 	} else {
 		pairType = LA32PartialPair::MASTER;
-		la32Pair.init(hasRingModulatingSlave(), mixType == 1);
-		useLA32Pair = &la32Pair;
+		la32Pair->init(hasRingModulatingSlave(), mixType == 1);
+		useLA32Pair = la32Pair;
 	}
 	if (isPCM()) {
 		useLA32Pair->initPCM(pairType, &synth->pcmROMData[pcmWave->addr], pcmWave->len, pcmWave->loop);
@@ -204,7 +232,7 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us
 		useLA32Pair->initSynth(pairType, (patchCache->waveform & 1) != 0, pulseWidthVal, patchCache->srcPartial.tvf.resonance + 1);
 	}
 	if (!hasRingModulatingSlave()) {
-		la32Pair.deactivate(LA32PartialPair::SLAVE);
+		la32Pair->deactivate(LA32PartialPair::SLAVE);
 	}
 }
 
@@ -245,6 +273,10 @@ bool Partial::isRingModulatingSlave() const {
 	return pair != NULL && structurePosition == 1 && (mixType == 1 || mixType == 2);
 }
 
+bool Partial::isRingModulatingNoMix() const {
+	return pair != NULL && ((structurePosition == 1 && mixType == 1) || mixType == 2);
+}
+
 bool Partial::isPCM() const {
 	return pcmWave != NULL;
 }
@@ -271,63 +303,90 @@ void Partial::backupCache(const PatchCache &cache) {
 	}
 }
 
-bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, Bit32u length) {
+bool Partial::canProduceOutput() {
 	if (!isActive() || alreadyOutputed || isRingModulatingSlave()) {
 		return false;
 	}
 	if (poly == NULL) {
-		synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::produceOutput()!", debugPartialNum);
+		synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::produceOutput()!", partialIndex);
 		return false;
 	}
-	alreadyOutputed = true;
+	return true;
+}
 
-	for (sampleNum = 0; sampleNum < length; sampleNum++) {
-		if (!tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::MASTER)) {
-			deactivate();
-			break;
-		}
-		la32Pair.generateNextSample(LA32PartialPair::MASTER, getAmpValue(), tvp->nextPitch(), getCutoffValue());
-		if (hasRingModulatingSlave()) {
-			la32Pair.generateNextSample(LA32PartialPair::SLAVE, pair->getAmpValue(), pair->tvp->nextPitch(), pair->getCutoffValue());
-			if (!pair->tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::SLAVE)) {
-				pair->deactivate();
-				if (mixType == 2) {
-					deactivate();
-					break;
-				}
+template <class LA32PairImpl>
+bool Partial::generateNextSample(LA32PairImpl *la32PairImpl) {
+	if (!tva->isPlaying() || !la32PairImpl->isActive(LA32PartialPair::MASTER)) {
+		deactivate();
+		return false;
+	}
+	la32PairImpl->generateNextSample(LA32PartialPair::MASTER, getAmpValue(), tvp->nextPitch(), getCutoffValue());
+	if (hasRingModulatingSlave()) {
+		la32PairImpl->generateNextSample(LA32PartialPair::SLAVE, pair->getAmpValue(), pair->tvp->nextPitch(), pair->getCutoffValue());
+		if (!pair->tva->isPlaying() || !la32PairImpl->isActive(LA32PartialPair::SLAVE)) {
+			pair->deactivate();
+			if (mixType == 2) {
+				deactivate();
+				return false;
 			}
 		}
+	}
+	return true;
+}
 
-		// Although, LA32 applies panning itself, we assume here it is applied in the mixer, not within a pair.
-		// Applying the pan value in the log-space looks like a waste of unlog resources. Though, it needs clarification.
-		Sample sample = la32Pair.nextOutSample();
-
-		// FIXME: Sample analysis suggests that the use of panVal is linear, but there are some quirks that still need to be resolved.
-#if MT32EMU_USE_FLOAT_SAMPLES
-		Sample leftOut = (sample * (float)leftPanValue) / 14.0f;
-		Sample rightOut = (sample * (float)rightPanValue) / 14.0f;
-		*(leftBuf++) += leftOut;
-		*(rightBuf++) += rightOut;
-#else
-		// FIXME: Dividing by 7 (or by 14 in a Mok-friendly way) looks of course pointless. Need clarification.
-		// FIXME2: LA32 may produce distorted sound in case if the absolute value of maximal amplitude of the input exceeds 8191
-		// when the panning value is non-zero. Most probably the distortion occurs in the same way it does with ring modulation,
-		// and it seems to be caused by limited precision of the common multiplication circuit.
-		// From analysis of this overflow, it is obvious that the right channel output is actually found
-		// by subtraction of the left channel output from the input.
-		// Though, it is unknown whether this overflow is exploited somewhere.
-		Sample leftOut = Sample((sample * leftPanValue) >> 8);
-		Sample rightOut = Sample((sample * rightPanValue) >> 8);
-		*leftBuf = Synth::clipSampleEx(SampleEx(*leftBuf) + SampleEx(leftOut));
-		*rightBuf = Synth::clipSampleEx(SampleEx(*rightBuf) + SampleEx(rightOut));
-		leftBuf++;
-		rightBuf++;
-#endif
+void Partial::produceAndMixSample(IntSample *&leftBuf, IntSample *&rightBuf, LA32IntPartialPair *la32IntPair) {
+	IntSampleEx sample = la32IntPair->nextOutSample();
+
+	// FIXME: LA32 may produce distorted sound in case if the absolute value of maximal amplitude of the input exceeds 8191
+	// when the panning value is non-zero. Most probably the distortion occurs in the same way it does with ring modulation,
+	// and it seems to be caused by limited precision of the common multiplication circuit.
+	// From analysis of this overflow, it is obvious that the right channel output is actually found
+	// by subtraction of the left channel output from the input.
+	// Though, it is unknown whether this overflow is exploited somewhere.
+
+	IntSampleEx leftOut = ((sample * leftPanValue) >> 13) + IntSampleEx(*leftBuf);
+	IntSampleEx rightOut = ((sample * rightPanValue) >> 13) + IntSampleEx(*rightBuf);
+	*(leftBuf++) = Synth::clipSampleEx(leftOut);
+	*(rightBuf++) = Synth::clipSampleEx(rightOut);
+}
+
+void Partial::produceAndMixSample(FloatSample *&leftBuf, FloatSample *&rightBuf, LA32FloatPartialPair *la32FloatPair) {
+	FloatSample sample = la32FloatPair->nextOutSample();
+	FloatSample leftOut = (sample * leftPanValue) / 14.0f;
+	FloatSample rightOut = (sample * rightPanValue) / 14.0f;
+	*(leftBuf++) += leftOut;
+	*(rightBuf++) += rightOut;
+}
+
+template <class Sample, class LA32PairImpl>
+bool Partial::doProduceOutput(Sample *leftBuf, Sample *rightBuf, Bit32u length, LA32PairImpl *la32PairImpl) {
+	if (!canProduceOutput()) return false;
+	alreadyOutputed = true;
+
+	for (sampleNum = 0; sampleNum < length; sampleNum++) {
+		if (!generateNextSample(la32PairImpl)) break;
+		produceAndMixSample(leftBuf, rightBuf, la32PairImpl);
 	}
 	sampleNum = 0;
 	return true;
 }
 
+bool Partial::produceOutput(IntSample *leftBuf, IntSample *rightBuf, Bit32u length) {
+	if (floatMode) {
+		synth->printDebug("Partial: Invalid call to produceOutput()! Renderer = %d\n", synth->getSelectedRendererType());
+		return false;
+	}
+	return doProduceOutput(leftBuf, rightBuf, length, static_cast<LA32IntPartialPair *>(la32Pair));
+}
+
+bool Partial::produceOutput(FloatSample *leftBuf, FloatSample *rightBuf, Bit32u length) {
+	if (!floatMode) {
+		synth->printDebug("Partial: Invalid call to produceOutput()! Renderer = %d\n", synth->getSelectedRendererType());
+		return false;
+	}
+	return doProduceOutput(leftBuf, rightBuf, length, static_cast<LA32FloatPartialPair *>(la32Pair));
+}
+
 bool Partial::shouldReverb() {
 	if (!isActive()) {
 		return false;
diff --git a/audio/softsynth/mt32/Partial.h b/audio/softsynth/mt32/Partial.h
index 187665d..e297710 100644
--- a/audio/softsynth/mt32/Partial.h
+++ b/audio/softsynth/mt32/Partial.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -24,6 +24,7 @@
 #include "Structures.h"
 #include "LA32Ramp.h"
 #include "LA32WaveGenerator.h"
+#include "LA32FloatWaveGenerator.h"
 
 namespace MT32Emu {
 
@@ -39,7 +40,7 @@ struct ControlROMPCMStruct;
 class Partial {
 private:
 	Synth *synth;
-	const int debugPartialNum; // Only used for debugging
+	const int partialIndex; // Index of this Partial in the global partial table
 	// Number of the sample currently being rendered by produceOutput(), or 0 if no run is in progress
 	// This is only kept available for debugging purposes.
 	Bit32u sampleNum;
@@ -72,7 +73,8 @@ private:
 	LA32Ramp cutoffModifierRamp;
 
 	// TODO: This should be owned by PartialPair
-	LA32PartialPair la32Pair;
+	LA32PartialPair *la32Pair;
+	const bool floatMode;
 
 	const PatchCache *patchCache;
 	PatchCache cachebackup;
@@ -80,6 +82,14 @@ private:
 	Bit32u getAmpValue();
 	Bit32u getCutoffValue();
 
+	template <class Sample, class LA32PairImpl>
+	bool doProduceOutput(Sample *leftBuf, Sample *rightBuf, Bit32u length, LA32PairImpl *la32PairImpl);
+	bool canProduceOutput();
+	template <class LA32PairImpl>
+	bool generateNextSample(LA32PairImpl *la32PairImpl);
+	void produceAndMixSample(IntSample *&leftBuf, IntSample *&rightBuf, LA32IntPartialPair *la32IntPair);
+	void produceAndMixSample(FloatSample *&leftBuf, FloatSample *&rightBuf, LA32FloatPartialPair *la32FloatPair);
+
 public:
 	bool alreadyOutputed;
 
@@ -98,6 +108,7 @@ public:
 	void startAbort();
 	void startDecayAll();
 	bool shouldReverb();
+	bool isRingModulatingNoMix() const;
 	bool hasRingModulatingSlave() const;
 	bool isRingModulatingSlave() const;
 	bool isPCM() const;
@@ -108,9 +119,10 @@ public:
 	void backupCache(const PatchCache &cache);
 
 	// Returns true only if data written to buffer
-	// This function (unlike the one below it) returns processed stereo samples
+	// These functions produce processed stereo samples
 	// made from combining this single partial with its pair, if it has one.
-	bool produceOutput(Sample *leftBuf, Sample *rightBuf, Bit32u length);
+	bool produceOutput(IntSample *leftBuf, IntSample *rightBuf, Bit32u length);
+	bool produceOutput(FloatSample *leftBuf, FloatSample *rightBuf, Bit32u length);
 }; // class Partial
 
 } // namespace MT32Emu
diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp
index 7c702f7..aeba5ce 100644
--- a/audio/softsynth/mt32/PartialManager.cpp
+++ b/audio/softsynth/mt32/PartialManager.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -31,11 +31,14 @@ namespace MT32Emu {
 PartialManager::PartialManager(Synth *useSynth, Part **useParts) {
 	synth = useSynth;
 	parts = useParts;
-	partialTable = new Partial *[synth->getPartialCount()];
+	inactivePartialCount = synth->getPartialCount();
+	partialTable = new Partial *[inactivePartialCount];
+	inactivePartials = new int[inactivePartialCount];
 	freePolys = new Poly *[synth->getPartialCount()];
 	firstFreePolyIndex = 0;
 	for (unsigned int i = 0; i < synth->getPartialCount(); i++) {
 		partialTable[i] = new Partial(synth, i);
+		inactivePartials[i] = inactivePartialCount - i - 1;
 		freePolys[i] = new Poly();
 	}
 }
@@ -46,6 +49,7 @@ PartialManager::~PartialManager(void) {
 		if (freePolys[i] != NULL) delete freePolys[i];
 	}
 	delete[] partialTable;
+	delete[] inactivePartials;
 	delete[] freePolys;
 }
 
@@ -59,7 +63,11 @@ bool PartialManager::shouldReverb(int i) {
 	return partialTable[i]->shouldReverb();
 }
 
-bool PartialManager::produceOutput(int i, Sample *leftBuf, Sample *rightBuf, Bit32u bufferLength) {
+bool PartialManager::produceOutput(int i, IntSample *leftBuf, IntSample *rightBuf, Bit32u bufferLength) {
+	return partialTable[i]->produceOutput(leftBuf, rightBuf, bufferLength);
+}
+
+bool PartialManager::produceOutput(int i, FloatSample *leftBuf, FloatSample *rightBuf, Bit32u bufferLength) {
 	return partialTable[i]->produceOutput(leftBuf, rightBuf, bufferLength);
 }
 
@@ -79,29 +87,21 @@ unsigned int PartialManager::setReserve(Bit8u *rset) {
 }
 
 Partial *PartialManager::allocPartial(int partNum) {
-	Partial *outPartial = NULL;
-
-	// Get the first inactive partial
-	for (unsigned int partialNum = 0; partialNum < synth->getPartialCount(); partialNum++) {
-		if (!partialTable[partialNum]->isActive()) {
-			outPartial = partialTable[partialNum];
-			break;
-		}
+	if (inactivePartialCount > 0) {
+		Partial *partial = partialTable[inactivePartials[--inactivePartialCount]];
+		partial->activate(partNum);
+		return partial;
 	}
-	if (outPartial != NULL) {
-		outPartial->activate(partNum);
+	synth->printDebug("PartialManager Error: No inactive partials to allocate for part %d, current partial state:\n", partNum);
+	for (Bit32u i = 0; i < synth->getPartialCount(); i++) {
+		const Partial *partial = partialTable[i];
+		synth->printDebug("[Partial %d]: activation=%d, owner part=%d\n", i, partial->isActive(), partial->getOwnerPart());
 	}
-	return outPartial;
+	return NULL;
 }
 
-unsigned int PartialManager::getFreePartialCount(void) {
-	int count = 0;
-	for (unsigned int i = 0; i < synth->getPartialCount(); i++) {
-		if (!partialTable[i]->isActive()) {
-			count++;
-		}
-	}
-	return count;
+unsigned int PartialManager::getFreePartialCount() {
+	return inactivePartialCount;
 }
 
 // This function is solely used to gather data for debug output at the moment.
@@ -275,7 +275,7 @@ Poly *PartialManager::assignPolyToPart(Part *part) {
 
 void PartialManager::polyFreed(Poly *poly) {
 	if (0 == firstFreePolyIndex) {
-		synth->printDebug("Cannot return freed poly, currently active polys:\n");
+		synth->printDebug("PartialManager Error: Cannot return freed poly, currently active polys:\n");
 		for (Bit32u partNum = 0; partNum < 9; partNum++) {
 			const Poly *activePoly = synth->getPart(partNum)->getFirstActivePoly();
 			Bit32u polyCount = 0;
@@ -285,10 +285,23 @@ void PartialManager::polyFreed(Poly *poly) {
 			}
 			synth->printDebug("Part: %i, active poly count: %i\n", partNum, polyCount);
 		}
+	} else {
+		firstFreePolyIndex--;
+		freePolys[firstFreePolyIndex] = poly;
 	}
 	poly->setPart(NULL);
-	firstFreePolyIndex--;
-	freePolys[firstFreePolyIndex] = poly;
+}
+
+void PartialManager::partialDeactivated(int partialIndex) {
+	if (inactivePartialCount < synth->getPartialCount()) {
+		inactivePartials[inactivePartialCount++] = partialIndex;
+		return;
+	}
+	synth->printDebug("PartialManager Error: Cannot return deactivated partial %d, current partial state:\n", partialIndex);
+	for (Bit32u i = 0; i < synth->getPartialCount(); i++) {
+		const Partial *partial = partialTable[i];
+		synth->printDebug("[Partial %d]: activation=%d, owner part=%d\n", i, partial->isActive(), partial->getOwnerPart());
+	}
 }
 
 } // namespace MT32Emu
diff --git a/audio/softsynth/mt32/PartialManager.h b/audio/softsynth/mt32/PartialManager.h
index b2908a5..deded8f 100644
--- a/audio/softsynth/mt32/PartialManager.h
+++ b/audio/softsynth/mt32/PartialManager.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -37,6 +37,8 @@ private:
 	Partial **partialTable;
 	Bit8u numReservedPartialsForPart[9];
 	Bit32u firstFreePolyIndex;
+	int *inactivePartials; // Holds indices of inactive Partials in the Partial table
+	Bit32u inactivePartialCount;
 
 	bool abortFirstReleasingPolyWhereReserveExceeded(int minPart);
 	bool abortFirstPolyPreferHeldWhereReserveExceeded(int minPart);
@@ -45,17 +47,19 @@ public:
 	PartialManager(Synth *synth, Part **parts);
 	~PartialManager();
 	Partial *allocPartial(int partNum);
-	unsigned int getFreePartialCount(void);
+	unsigned int getFreePartialCount();
 	void getPerPartPartialUsage(unsigned int perPartPartialUsage[9]);
 	bool freePartials(unsigned int needed, int partNum);
 	unsigned int setReserve(Bit8u *rset);
 	void deactivateAll();
-	bool produceOutput(int i, Sample *leftBuf, Sample *rightBuf, Bit32u bufferLength);
+	bool produceOutput(int i, IntSample *leftBuf, IntSample *rightBuf, Bit32u bufferLength);
+	bool produceOutput(int i, FloatSample *leftBuf, FloatSample *rightBuf, Bit32u bufferLength);
 	bool shouldReverb(int i);
 	void clearAlreadyOutputed();
 	const Partial *getPartial(unsigned int partialNum) const;
 	Poly *assignPolyToPart(Part *part);
 	void polyFreed(Poly *poly);
+	void partialDeactivated(int partialIndex);
 }; // class PartialManager
 
 } // namespace MT32Emu
diff --git a/audio/softsynth/mt32/Poly.cpp b/audio/softsynth/mt32/Poly.cpp
index 9a3948c..44b8d24 100644
--- a/audio/softsynth/mt32/Poly.cpp
+++ b/audio/softsynth/mt32/Poly.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/Poly.h b/audio/softsynth/mt32/Poly.h
index 4b28323..b2d4ece 100644
--- a/audio/softsynth/mt32/Poly.h
+++ b/audio/softsynth/mt32/Poly.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/ROMInfo.cpp b/audio/softsynth/mt32/ROMInfo.cpp
index ce78e69..8c813a4 100644
--- a/audio/softsynth/mt32/ROMInfo.cpp
+++ b/audio/softsynth/mt32/ROMInfo.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -31,6 +31,7 @@ static const ROMInfo *getKnownROMInfoFromList(Bit32u index) {
 	static const ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL};
 	static const ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL};
 
+	static const ROMInfo CTRL_MT32_V2_04 = {131072, "2c16432b6c73dd2a3947cba950a0f4c19d6180eb", ROMInfo::Control, "ctrl_mt32_2_04", "MT-32 Control v2.04", ROMInfo::Full, NULL};
 	static const ROMInfo CTRL_CM32L_V1_00 = {65536, "73683d585cd6948cc19547942ca0e14a0319456d", ROMInfo::Control, "ctrl_cm32l_1_00", "CM-32L/LAPC-I Control v1.00", ROMInfo::Full, NULL};
 	static const ROMInfo CTRL_CM32L_V1_02 = {65536, "a439fbb390da38cada95a7cbb1d6ca199cd66ef8", ROMInfo::Control, "ctrl_cm32l_1_02", "CM-32L/LAPC-I Control v1.02", ROMInfo::Full, NULL};
 
@@ -43,6 +44,7 @@ static const ROMInfo *getKnownROMInfoFromList(Bit32u index) {
 		&CTRL_MT32_V1_06,
 		&CTRL_MT32_V1_07,
 		&CTRL_MT32_BLUER,
+		&CTRL_MT32_V2_04,
 		&CTRL_CM32L_V1_00,
 		&CTRL_CM32L_V1_02,
 		&PCM_MT32,
diff --git a/audio/softsynth/mt32/ROMInfo.h b/audio/softsynth/mt32/ROMInfo.h
index 8a5ad14..cd4a1c5 100644
--- a/audio/softsynth/mt32/ROMInfo.h
+++ b/audio/softsynth/mt32/ROMInfo.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/SampleRateConverter.cpp b/audio/softsynth/mt32/SampleRateConverter.cpp
index 70f860f..73f7963 100644
--- a/audio/softsynth/mt32/SampleRateConverter.cpp
+++ b/audio/softsynth/mt32/SampleRateConverter.cpp
@@ -14,13 +14,15 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <cstddef>
+
 #include "SampleRateConverter.h"
 
 #if MT32EMU_WITH_LIBSOXR_RESAMPLER
 #include "srchelper/SoxrAdapter.h"
 #elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
 #include "srchelper/SamplerateAdapter.h"
-#else
+#elif MT32EMU_WITH_INTERNAL_RESAMPLER
 #include "srchelper/InternalResampler.h"
 #endif
 
@@ -28,13 +30,16 @@
 
 using namespace MT32Emu;
 
-static inline void *createDelegate(Synth &synth, double targetSampleRate, SampleRateConverter::Quality quality) {
+static inline void *createDelegate(Synth &synth, double targetSampleRate, SamplerateConversionQuality quality) {
 #if MT32EMU_WITH_LIBSOXR_RESAMPLER
 	return new SoxrAdapter(synth, targetSampleRate, quality);
 #elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
 	return new SamplerateAdapter(synth, targetSampleRate, quality);
-#else
+#elif MT32EMU_WITH_INTERNAL_RESAMPLER
 	return new InternalResampler(synth, targetSampleRate, quality);
+#else
+	(void)synth, (void)targetSampleRate, (void)quality;
+	return NULL;
 #endif
 }
 
@@ -47,34 +52,58 @@ AnalogOutputMode SampleRateConverter::getBestAnalogOutputMode(double targetSampl
 	return AnalogOutputMode_COARSE;
 }
 
-SampleRateConverter::SampleRateConverter(Synth &useSynth, double targetSampleRate, Quality useQuality) :
+double SampleRateConverter::getSupportedOutputSampleRate(double desiredSampleRate) {
+#if MT32EMU_WITH_LIBSOXR_RESAMPLER || MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER || MT32EMU_WITH_INTERNAL_RESAMPLER
+	return desiredSampleRate > 0 ? desiredSampleRate : 0;
+#else
+	(void)desiredSampleRate;
+	return 0;
+#endif
+}
+
+SampleRateConverter::SampleRateConverter(Synth &useSynth, double targetSampleRate, SamplerateConversionQuality useQuality) :
 	synthInternalToTargetSampleRateRatio(SAMPLE_RATE / targetSampleRate),
-	srcDelegate(createDelegate(useSynth, targetSampleRate, useQuality))
+	useSynthDelegate(useSynth.getStereoOutputSampleRate() == targetSampleRate),
+	srcDelegate(useSynthDelegate ? &useSynth : createDelegate(useSynth, targetSampleRate, useQuality))
 {}
 
 SampleRateConverter::~SampleRateConverter() {
+	if (!useSynthDelegate) {
 #if MT32EMU_WITH_LIBSOXR_RESAMPLER
-	delete static_cast<SoxrAdapter *>(srcDelegate);
+		delete static_cast<SoxrAdapter *>(srcDelegate);
 #elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
-	delete static_cast<SamplerateAdapter *>(srcDelegate);
-#else
-	delete static_cast<InternalResampler *>(srcDelegate);
+		delete static_cast<SamplerateAdapter *>(srcDelegate);
+#elif MT32EMU_WITH_INTERNAL_RESAMPLER
+		delete static_cast<InternalResampler *>(srcDelegate);
 #endif
+	}
 }
 
 void SampleRateConverter::getOutputSamples(float *buffer, unsigned int length) {
+	if (useSynthDelegate) {
+		static_cast<Synth *>(srcDelegate)->render(buffer, length);
+		return;
+	}
+
 #if MT32EMU_WITH_LIBSOXR_RESAMPLER
 	static_cast<SoxrAdapter *>(srcDelegate)->getOutputSamples(buffer, length);
 #elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
 	static_cast<SamplerateAdapter *>(srcDelegate)->getOutputSamples(buffer, length);
-#else
+#elif MT32EMU_WITH_INTERNAL_RESAMPLER
 	static_cast<InternalResampler *>(srcDelegate)->getOutputSamples(buffer, length);
+#else
+	Synth::muteSampleBuffer(buffer, length);
 #endif
 }
 
 void SampleRateConverter::getOutputSamples(Bit16s *outBuffer, unsigned int length) {
 	static const unsigned int CHANNEL_COUNT = 2;
 
+	if (useSynthDelegate) {
+		static_cast<Synth *>(srcDelegate)->render(outBuffer, length);
+		return;
+	}
+
 	float floatBuffer[CHANNEL_COUNT * MAX_SAMPLES_PER_RUN];
 	while (length > 0) {
 		const unsigned int size = MAX_SAMPLES_PER_RUN < length ? MAX_SAMPLES_PER_RUN : length;
diff --git a/audio/softsynth/mt32/SampleRateConverter.h b/audio/softsynth/mt32/SampleRateConverter.h
index 24bce08..e895011 100644
--- a/audio/softsynth/mt32/SampleRateConverter.h
+++ b/audio/softsynth/mt32/SampleRateConverter.h
@@ -14,8 +14,8 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef SAMPLE_RATE_CONVERTER_H
-#define SAMPLE_RATE_CONVERTER_H
+#ifndef MT32EMU_SAMPLE_RATE_CONVERTER_H
+#define MT32EMU_SAMPLE_RATE_CONVERTER_H
 
 #include "globals.h"
 #include "Types.h"
@@ -33,21 +33,17 @@ class Synth;
  */
 class MT32EMU_EXPORT SampleRateConverter {
 public:
-	enum Quality {
-		// Use this only when the speed is more important than the audio quality.
-		FASTEST,
-		FAST,
-		GOOD,
-		BEST
-	};
-
 	// Returns the value of AnalogOutputMode for which the output signal may retain its full frequency spectrum
 	// at the sample rate specified by the targetSampleRate argument.
 	static AnalogOutputMode getBestAnalogOutputMode(double targetSampleRate);
 
+	// Returns the sample rate supported by the sample rate conversion implementation currently in effect
+	// that is closest to the one specified by the desiredSampleRate argument.
+	static double getSupportedOutputSampleRate(double desiredSampleRate);
+
 	// Creates a SampleRateConverter instance that converts output signal from the synth to the given sample rate
 	// with the specified conversion quality.
-	SampleRateConverter(Synth &synth, double targetSampleRate, Quality quality);
+	SampleRateConverter(Synth &synth, double targetSampleRate, SamplerateConversionQuality quality);
 	~SampleRateConverter();
 
 	// Fills the provided output buffer with the results of the sample rate conversion.
@@ -70,9 +66,10 @@ public:
 
 private:
 	const double synthInternalToTargetSampleRateRatio;
+	const bool useSynthDelegate;
 	void * const srcDelegate;
 }; // class SampleRateConverter
 
 } // namespace MT32Emu
 
-#endif // SAMPLE_RATE_CONVERTER_H
+#endif // MT32EMU_SAMPLE_RATE_CONVERTER_H
diff --git a/audio/softsynth/mt32/Structures.h b/audio/softsynth/mt32/Structures.h
index 6dcb9c8..d116aae 100644
--- a/audio/softsynth/mt32/Structures.h
+++ b/audio/softsynth/mt32/Structures.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -184,7 +184,13 @@ struct SoundGroup {
 #endif
 
 struct ControlROMFeatureSet {
+	unsigned int quirkBasePitchOverflow : 1;
 	unsigned int quirkPitchEnvelopeOverflow : 1;
+	unsigned int quirkRingModulationNoMix : 1;
+	unsigned int quirkTVAZeroEnvLevels : 1;
+	unsigned int quirkPanMult : 1;
+	unsigned int quirkKeyShift : 1;
+	unsigned int quirkTVFBaseCutoffLimit : 1;
 
 	// Features below don't actually depend on control ROM version, which is used to identify hardware model
 	unsigned int defaultReverbMT32Compatible : 1;
diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp
index 3a478b5..62810ba 100644
--- a/audio/softsynth/mt32/Synth.cpp
+++ b/audio/softsynth/mt32/Synth.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -32,25 +32,50 @@
 #include "ROMInfo.h"
 #include "TVA.h"
 
+#if MT32EMU_MONITOR_SYSEX > 0
+#include "mmath.h"
+#endif
+
 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;
 
 // FIXME: there should be more specific feature sets for various MT-32 control ROM versions
-static const ControlROMFeatureSet OLD_MT32_COMPATIBLE = { true, true, true };
-static const ControlROMFeatureSet CM32L_COMPATIBLE = { false, false, false };
+static const ControlROMFeatureSet OLD_MT32_COMPATIBLE = {
+	true, // quirkBasePitchOverflow
+	true, // quirkPitchEnvelopeOverflow
+	true, // quirkRingModulationNoMix
+	true, // quirkTVAZeroEnvLevels
+	true, // quirkPanMult
+	true, // quirkKeyShift
+	true, // quirkTVFBaseCutoffLimit
+	true, // defaultReverbMT32Compatible
+	true // oldMT32AnalogLPF
+};
+static const ControlROMFeatureSet CM32L_COMPATIBLE = {
+	false, // quirkBasePitchOverflow
+	false, // quirkPitchEnvelopeOverflow
+	false, // quirkRingModulationNoMix
+	false, // quirkTVAZeroEnvLevels
+	false, // quirkPanMult
+	false, // quirkKeyShift
+	false, // quirkTVFBaseCutoffLimit
+	false, // defaultReverbMT32Compatible
+	false // oldMT32AnalogLPF
+};
 
-static const ControlROMMap ControlROMMaps[7] = {
+static const ControlROMMap ControlROMMaps[8] = {
 	//     ID                Features        PCMmap  PCMc  tmbrA  tmbrAO, tmbrAC tmbrB   tmbrBO  tmbrBC tmbrR   trC rhythm rhyC  rsrv   panpot   prog   rhyMax  patMax  sysMax  timMax  sndGrp sGC
 	{ "ctrl_mt32_1_04", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x7064, 19 },
 	{ "ctrl_mt32_1_05", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x70CA, 19 },
 	{ "ctrl_mt32_1_06", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57F4, 0x57E2, 0x5264, 0x5270, 0x5280, 0x521C, 0x70CA, 19 },
 	{ "ctrl_mt32_1_07", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57CC, 0x57BA, 0x523C, 0x5248, 0x5258, 0x51F4, 0x70B0, 19 }, // MT-32 revision 1
 	{"ctrl_mt32_bluer", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x741C, 85, 0x57E5, 0x5800, 0x57EE, 0x5270, 0x527C, 0x528C, 0x5228, 0x70CE, 19 }, // MT-32 Blue Ridge mod
+	{"ctrl_mt32_2_04",   CM32L_COMPATIBLE,   0x8100, 128, 0x8000, 0x8000, true,  0x8080, 0x8000, true,  0x8500, 30, 0x8580, 85, 0x4F5D, 0x4F78, 0x4F66, 0x4899, 0x489D, 0x48B6, 0x48CD, 0x5A58, 19 },
 	{"ctrl_cm32l_1_00",  CM32L_COMPATIBLE,   0x8100, 256, 0x8000, 0x8000, true,  0x8080, 0x8000, true,  0x8500, 64, 0x8580, 85, 0x4F65, 0x4F80, 0x4F6E, 0x48A1, 0x48A5, 0x48BE, 0x48D5, 0x5A6C, 19 },
 	{"ctrl_cm32l_1_02",  CM32L_COMPATIBLE,   0x8100, 256, 0x8000, 0x8000, true,  0x8080, 0x8000, true,  0x8500, 64, 0x8580, 85, 0x4F93, 0x4FAE, 0x4F9C, 0x48CB, 0x48CF, 0x48E8, 0x48FF, 0x5A96, 19 }  // CM-32L
-	// (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp)
+	// (Note that old MT-32 ROMs actually have 86 entries for rhythmTemp)
 };
 
 static const PartialState PARTIAL_PHASE_TO_STATE[8] = {
@@ -63,80 +88,120 @@ static inline PartialState getPartialState(PartialManager *partialManager, unsig
 	return partial->isActive() ? PARTIAL_PHASE_TO_STATE[partial->getTVA()->getPhase()] : PartialState_INACTIVE;
 }
 
-class SampleFormatConverter {
+template <class I, class O>
+static inline void convertSampleFormat(const I *inBuffer, O *outBuffer, const Bit32u len) {
+	if (inBuffer == NULL || outBuffer == NULL) return;
+
+	const I *inBufferEnd = inBuffer + len;
+	while (inBuffer < inBufferEnd) {
+		*(outBuffer++) = Synth::convertSample(*(inBuffer++));
+	}
+}
+
+class Renderer {
 protected:
-#if MT32EMU_USE_FLOAT_SAMPLES
-	Bit16s *outBuffer;
-#else
-	float *outBuffer;
-#endif
+	Synth &synth;
 
-public:
-	Sample *sampleBuffer;
+	void printDebug(const char *msg) const {
+		synth.printDebug("%s", msg);
+	}
 
-	SampleFormatConverter(Sample *buffer) : outBuffer(NULL), sampleBuffer(buffer) {}
+	bool isActivated() const {
+		return synth.activated;
+	}
 
-	inline bool isConversionNeeded() {
-		return outBuffer != NULL;
+	bool isAbortingPoly() const {
+		return synth.isAbortingPoly();
 	}
 
-	inline void convert(Bit32u len) {
-		if (sampleBuffer == NULL) return;
-		if (outBuffer == NULL) {
-			sampleBuffer += len;
-			return;
-		}
-		Sample *inBuffer = sampleBuffer;
-		while (len--) {
-			*(outBuffer++) = Synth::convertSample(*(inBuffer++));
-		}
+	Analog &getAnalog() const {
+		return *synth.analog;
 	}
 
-	inline void addSilence(Bit32u len) {
-		if (outBuffer != NULL) {
-			Synth::muteSampleBuffer(outBuffer, len);
-			outBuffer += len;
-		} else if (sampleBuffer != NULL) {
-			Synth::muteSampleBuffer(sampleBuffer, len);
-			sampleBuffer += len;
-		}
+	MidiEventQueue &getMidiQueue() {
+		return *synth.midiQueue;
 	}
-};
 
-template <int BUFFER_SIZE_MULTIPLIER = 1>
-class BufferedSampleFormatConverter : public SampleFormatConverter {
-	Sample renderingBuffer[BUFFER_SIZE_MULTIPLIER * MAX_SAMPLES_PER_RUN];
+	PartialManager &getPartialManager() {
+		return *synth.partialManager;
+	}
 
-public:
-#if MT32EMU_USE_FLOAT_SAMPLES
-	BufferedSampleFormatConverter(Bit16s *buffer)
-#else
-	BufferedSampleFormatConverter(float *buffer)
-#endif
-		: SampleFormatConverter(renderingBuffer)
-	{
-		outBuffer = buffer;
-		if (buffer == NULL) sampleBuffer = NULL;
+	BReverbModel &getReverbModel() {
+		return *synth.reverbModel;
 	}
-};
 
-class Renderer {
-	Synth &synth;
+	Bit32u getRenderedSampleCount() {
+		return synth.renderedSampleCount;
+	}
+
+	void incRenderedSampleCount(const Bit32u count) {
+		synth.renderedSampleCount += count;
+	}
+
+public:
+	Renderer(Synth &useSynth) : synth(useSynth) {}
+
+	virtual ~Renderer() {}
+
+	virtual void render(IntSample *stereoStream, Bit32u len) = 0;
+	virtual void render(FloatSample *stereoStream, Bit32u len) = 0;
+	virtual void renderStreams(const DACOutputStreams<IntSample> &streams, Bit32u len) = 0;
+	virtual void renderStreams(const DACOutputStreams<FloatSample> &streams, Bit32u len) = 0;
+};
 
+template <class Sample>
+class RendererImpl : public Renderer {
 	// These buffers are used for building the output streams as they are found at the DAC entrance.
 	// The output is mixed down to stereo interleaved further in the analog circuitry emulation.
 	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];
 
-public:
-	Renderer(Synth &useSynth) : synth(useSynth) {}
+	const DACOutputStreams<Sample> tmpBuffers;
+	DACOutputStreams<Sample> createTmpBuffers() {
+		DACOutputStreams<Sample> buffers = {
+			tmpNonReverbLeft, tmpNonReverbRight,
+			tmpReverbDryLeft, tmpReverbDryRight,
+			tmpReverbWetLeft, tmpReverbWetRight
+		};
+		return buffers;
+	}
 
-	void render(SampleFormatConverter &converter, Bit32u len);
-	void renderStreams(SampleFormatConverter &nonReverbLeft, SampleFormatConverter &nonReverbRight, SampleFormatConverter &reverbDryLeft, SampleFormatConverter &reverbDryRight, SampleFormatConverter &reverbWetLeft, SampleFormatConverter &reverbWetRight, Bit32u len);
+public:
+	RendererImpl(Synth &useSynth) :
+		Renderer(useSynth),
+		tmpBuffers(createTmpBuffers())
+	{}
+
+	void render(IntSample *stereoStream, Bit32u len);
+	void render(FloatSample *stereoStream, Bit32u len);
+	void renderStreams(const DACOutputStreams<IntSample> &streams, Bit32u len);
+	void renderStreams(const DACOutputStreams<FloatSample> &streams, Bit32u len);
+
+	template <class O>
+	void doRenderAndConvert(O *stereoStream, Bit32u len);
+	void doRender(Sample *stereoStream, Bit32u len);
+
+	template <class O>
+	void doRenderAndConvertStreams(const DACOutputStreams<O> &streams, Bit32u len);
+	void doRenderStreams(const DACOutputStreams<Sample> &streams, Bit32u len);
 	void produceLA32Output(Sample *buffer, Bit32u len);
 	void convertSamplesToOutput(Sample *buffer, Bit32u len);
-	void doRenderStreams(DACOutputStreams<Sample> &streams, Bit32u len);
+	void produceStreams(const DACOutputStreams<Sample> &streams, Bit32u len);
+};
+
+class Extensions {
+public:
+	RendererType selectedRendererType;
+	Bit32s masterTunePitchDelta;
+	bool niceAmpRamp;
+
+	// Here we keep the reverse mapping of assigned parts per MIDI channel.
+	// NOTE: value above 8 means that the channel is not assigned
+	Bit8u chantable[16][9];
+
+	// This stores the index of Part in chantable that failed to play and required partial abortion.
+	Bit32u abortingPartIx;
 };
 
 Bit32u Synth::getLibraryVersionInt() {
@@ -161,7 +226,11 @@ Bit32u Synth::getStereoOutputSampleRate(AnalogOutputMode analogOutputMode) {
 	return SAMPLE_RATES[analogOutputMode];
 }
 
-Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams), mt32default(*new MemParams), renderer(*new Renderer(*this)) {
+Synth::Synth(ReportHandler *useReportHandler) :
+	mt32ram(*new MemParams),
+	mt32default(*new MemParams),
+	extensions(*new Extensions)
+{
 	opened = false;
 	reverbOverridden = false;
 	partialCount = DEFAULT_MAX_PARTIALS;
@@ -181,11 +250,14 @@ Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams), mt32def
 	}
 	reverbModel = NULL;
 	analog = NULL;
+	renderer = NULL;
 	setDACInputMode(DACInputMode_NICE);
 	setMIDIDelayMode(MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY);
 	setOutputGain(1.0f);
 	setReverbOutputGain(1.0f);
 	setReversedStereoEnabled(false);
+	setNiceAmpRampEnabled(true);
+	selectRendererType(RendererType_BIT16S);
 
 	patchTempMemoryRegion = NULL;
 	rhythmTempMemoryRegion = NULL;
@@ -205,8 +277,6 @@ Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams), mt32def
 	lastReceivedMIDIEventTimestamp = 0;
 	memset(parts, 0, sizeof(parts));
 	renderedSampleCount = 0;
-
-	reserved = NULL;
 }
 
 Synth::~Synth() {
@@ -216,7 +286,7 @@ Synth::~Synth() {
 	}
 	delete &mt32ram;
 	delete &mt32default;
-	delete &renderer;
+	delete &extensions;
 }
 
 void ReportHandler::showLCDMessage(const char *data) {
@@ -309,12 +379,6 @@ bool Synth::isDefaultReverbMT32Compatible() const {
 }
 
 void Synth::setDACInputMode(DACInputMode mode) {
-#if MT32EMU_USE_FLOAT_SAMPLES
-	// We aren't emulating these in float mode, so better to inform the invoker
-	if ((mode == DACInputMode_GENERATION1) || (mode == DACInputMode_GENERATION2)) {
-		mode = DACInputMode_NICE;
-	}
-#endif
 	dacInputMode = mode;
 }
 
@@ -358,6 +422,14 @@ bool Synth::isReversedStereoEnabled() const {
 	return reversedStereoEnabled;
 }
 
+void Synth::setNiceAmpRampEnabled(bool enabled) {
+	extensions.niceAmpRamp = enabled;
+}
+
+bool Synth::isNiceAmpRampEnabled() const {
+	return extensions.niceAmpRamp;
+}
+
 bool Synth::loadControlROM(const ROMImage &controlROMImage) {
 	File *file = controlROMImage.getFile();
 	const ROMInfo *controlROMInfo = controlROMImage.getROMInfo();
@@ -500,10 +572,10 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, Bit16u count, Bit16u s
 }
 
 void Synth::initReverbModels(bool mt32CompatibleMode) {
-	reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM, mt32CompatibleMode);
-	reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL, mt32CompatibleMode);
-	reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE, mt32CompatibleMode);
-	reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY, mt32CompatibleMode);
+	reverbModels[REVERB_MODE_ROOM] = BReverbModel::createBReverbModel(REVERB_MODE_ROOM, mt32CompatibleMode, getSelectedRendererType());
+	reverbModels[REVERB_MODE_HALL] = BReverbModel::createBReverbModel(REVERB_MODE_HALL, mt32CompatibleMode, getSelectedRendererType());
+	reverbModels[REVERB_MODE_PLATE] = BReverbModel::createBReverbModel(REVERB_MODE_PLATE, mt32CompatibleMode, getSelectedRendererType());
+	reverbModels[REVERB_MODE_TAP_DELAY] = BReverbModel::createBReverbModel(REVERB_MODE_TAP_DELAY, mt32CompatibleMode, getSelectedRendererType());
 #if !MT32EMU_REDUCE_REVERB_MEMORY
 	for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) {
 		reverbModels[i]->open();
@@ -529,6 +601,7 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, B
 	}
 	partialCount = usePartialCount;
 	abortingPoly = NULL;
+	extensions.abortingPartIx = 0;
 
 	// This is to help detect bugs
 	memset(&mt32ram, '?', sizeof(mt32ram));
@@ -650,6 +723,7 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, B
 	bool oldReverbOverridden = reverbOverridden;
 	reverbOverridden = false;
 	refreshSystem();
+	resetMasterTunePitchDelta();
 	reverbOverridden = oldReverbOverridden;
 
 	char(*writableSoundGroupNames)[9] = new char[controlROMMap->soundGroupsCount][9];
@@ -687,10 +761,33 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, B
 
 	midiQueue = new MidiEventQueue();
 
-	analog = new Analog(analogOutputMode, controlROMFeatures->oldMT32AnalogLPF);
+	analog = Analog::createAnalog(analogOutputMode, controlROMFeatures->oldMT32AnalogLPF, getSelectedRendererType());
+#if MT32EMU_MONITOR_INIT
+	static const char *ANALOG_OUTPUT_MODES[] = { "Digital only", "Coarse", "Accurate", "Oversampled2x" };
+	printDebug("Using Analog output mode %s", ANALOG_OUTPUT_MODES[analogOutputMode]);
+#endif
 	setOutputGain(outputGain);
 	setReverbOutputGain(reverbOutputGain);
 
+	switch (getSelectedRendererType()) {
+		case RendererType_BIT16S:
+			renderer = new RendererImpl<IntSample>(*this);
+#if MT32EMU_MONITOR_INIT
+			printDebug("Using integer 16-bit samples in renderer and wave generator");
+#endif
+			break;
+		case RendererType_FLOAT:
+			renderer = new RendererImpl<FloatSample>(*this);
+#if MT32EMU_MONITOR_INIT
+			printDebug("Using float 32-bit samples in renderer and wave generator");
+#endif
+			break;
+		default:
+			printDebug("Synth: Unknown renderer type %i\n", getSelectedRendererType());
+			dispose();
+			return false;
+	}
+
 	opened = true;
 	activated = false;
 
@@ -706,6 +803,9 @@ void Synth::dispose() {
 	delete midiQueue;
 	midiQueue = NULL;
 
+	delete renderer;
+	renderer = NULL;
+
 	delete analog;
 	analog = NULL;
 
@@ -810,13 +910,17 @@ Bit32u Synth::addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp) {
 	return timestamp;
 }
 
+Bit32u Synth::getInternalRenderedSampleCount() const {
+	return renderedSampleCount;
+}
+
 bool Synth::playMsg(Bit32u msg) {
 	return playMsg(msg, renderedSampleCount);
 }
 
 bool Synth::playMsg(Bit32u msg, Bit32u timestamp) {
 	if ((msg & 0xF8) == 0xF8) {
-		reportHandler->onMIDISystemRealtime(Bit8u(msg));
+		reportHandler->onMIDISystemRealtime(Bit8u(msg & 0xFF));
 		return true;
 	}
 	if (midiQueue == NULL) return false;
@@ -859,14 +963,24 @@ void Synth::playMsgNow(Bit32u msg) {
 
 	//printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
 
-	Bit8u part = chantable[chan];
-	if (part > 8) {
+	Bit8u *chanParts = extensions.chantable[chan];
+	if (*chanParts > 8) {
 #if MT32EMU_MONITOR_MIDI > 0
 		printDebug("Play msg on unreg chan %d (%d): code=0x%01x, vel=%d", chan, part, code, velocity);
 #endif
 		return;
 	}
-	playMsgOnPart(part, code, note, velocity);
+	for (Bit32u i = extensions.abortingPartIx; i <= 8; i++) {
+		const Bit32u partNum = chanParts[i];
+		if (partNum > 8) break;
+		playMsgOnPart(partNum, code, note, velocity);
+		if (isAbortingPoly()) {
+			extensions.abortingPartIx = i;
+			break;
+		} else if (extensions.abortingPartIx) {
+			extensions.abortingPartIx = 0;
+		}
+	}
 }
 
 void Synth::playMsgOnPart(Bit8u part, Bit8u code, Bit8u note, Bit8u velocity) {
@@ -1057,7 +1171,7 @@ void Synth::playSysexWithoutHeader(Bit8u device, Bit8u command, const Bit8u *sys
 			break;
 		}
 		*/
-		// fall through
+		// Fall-through
 	case SYSEX_CMD_DT1:
 		writeSysex(device, sysex, len);
 		break;
@@ -1067,7 +1181,7 @@ void Synth::playSysexWithoutHeader(Bit8u device, Bit8u command, const Bit8u *sys
 			// FIXME: We should send SYSEX_CMD_RJC in this case
 			break;
 		}
-		// fall through
+		// Fall-through
 	case SYSEX_CMD_RQ1:
 		readSysex(device, sysex, len);
 		break;
@@ -1097,45 +1211,59 @@ void Synth::writeSysex(Bit8u device, const Bit8u *sysex, Bit32u len) {
 		printDebug("WRITE-CHANNEL: Channel %d temp area 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr));
 #endif
 		if (/*addr >= MT32EMU_MEMADDR(0x000000) && */addr < MT32EMU_MEMADDR(0x010000)) {
-			int offset;
-			if (chantable[device] > 8) {
+			addr += MT32EMU_MEMADDR(0x030000);
+			Bit8u *chanParts = extensions.chantable[device];
+			if (*chanParts > 8) {
 #if MT32EMU_MONITOR_SYSEX > 0
 				printDebug(" (Channel not mapped to a part... 0 offset)");
 #endif
-				offset = 0;
-			} else if (chantable[device] == 8) {
+			} else {
+				for (Bit32u partIx = 0; partIx <= 8; partIx++) {
+					if (chanParts[partIx] > 8) break;
+					int offset;
+					if (chanParts[partIx] == 8) {
 #if MT32EMU_MONITOR_SYSEX > 0
-				printDebug(" (Channel mapped to rhythm... 0 offset)");
+						printDebug(" (Channel mapped to rhythm... 0 offset)");
 #endif
-				offset = 0;
-			} else {
-				offset = chantable[device] * sizeof(MemParams::PatchTemp);
+						offset = 0;
+					} else {
+						offset = chanParts[partIx] * sizeof(MemParams::PatchTemp);
 #if MT32EMU_MONITOR_SYSEX > 0
-				printDebug(" (Setting extra offset to %d)", offset);
+						printDebug(" (Setting extra offset to %d)", offset);
 #endif
+					}
+					writeSysexGlobal(addr + offset, sysex, len);
+				}
+				return;
 			}
-			addr += MT32EMU_MEMADDR(0x030000) + offset;
 		} else if (/*addr >= MT32EMU_MEMADDR(0x010000) && */ addr < MT32EMU_MEMADDR(0x020000)) {
 			addr += MT32EMU_MEMADDR(0x030110) - MT32EMU_MEMADDR(0x010000);
 		} else if (/*addr >= MT32EMU_MEMADDR(0x020000) && */ addr < MT32EMU_MEMADDR(0x030000)) {
-			int offset;
-			if (chantable[device] > 8) {
+			addr += MT32EMU_MEMADDR(0x040000) - MT32EMU_MEMADDR(0x020000);
+			Bit8u *chanParts = extensions.chantable[device];
+			if (*chanParts > 8) {
 #if MT32EMU_MONITOR_SYSEX > 0
 				printDebug(" (Channel not mapped to a part... 0 offset)");
 #endif
-				offset = 0;
-			} else if (chantable[device] == 8) {
+			} else {
+				for (Bit32u partIx = 0; partIx <= 8; partIx++) {
+					if (chanParts[partIx] > 8) break;
+					int offset;
+					if (chanParts[partIx] == 8) {
 #if MT32EMU_MONITOR_SYSEX > 0
-				printDebug(" (Channel mapped to rhythm... 0 offset)");
+						printDebug(" (Channel mapped to rhythm... 0 offset)");
 #endif
-				offset = 0;
-			} else {
-				offset = chantable[device] * sizeof(TimbreParam);
+						offset = 0;
+					} else {
+						offset = chanParts[partIx] * sizeof(TimbreParam);
 #if MT32EMU_MONITOR_SYSEX > 0
-				printDebug(" (Setting extra offset to %d)", offset);
+						printDebug(" (Setting extra offset to %d)", offset);
 #endif
+					}
+					writeSysexGlobal(addr + offset, sysex, len);
+				}
+				return;
 			}
-			addr += MT32EMU_MEMADDR(0x040000) - MT32EMU_MEMADDR(0x020000) + offset;
 		} else {
 #if MT32EMU_MONITOR_SYSEX > 0
 			printDebug(" Invalid channel");
@@ -1143,8 +1271,11 @@ void Synth::writeSysex(Bit8u device, const Bit8u *sysex, Bit32u len) {
 			return;
 		}
 	}
+	writeSysexGlobal(addr, sysex, len);
+}
 
-	// Process device-global sysex (possibly converted from channel-specific sysex above)
+// Process device-global sysex (possibly converted from channel-specific sysex above)
+void Synth::writeSysexGlobal(Bit32u addr, const Bit8u *sysex, Bit32u len) {
 	for (;;) {
 		// Find the appropriate memory region
 		const MemoryRegion *region = findMemoryRegion(addr);
@@ -1484,6 +1615,8 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le
 }
 
 void Synth::refreshSystemMasterTune() {
+	// 171 is ~half a semitone.
+	extensions.masterTunePitchDelta = ((mt32ram.system.masterTune - 64) * 171) >> 6; // PORTABILITY NOTE: Assumes arithmetic shift.
 #if MT32EMU_MONITOR_SYSEX > 0
 	//FIXME:KG: This is just an educated guess.
 	// The LAPC-I documentation claims a range of 427.5Hz-452.6Hz (similar to what we have here)
@@ -1543,9 +1676,10 @@ void Synth::refreshSystemReserveSettings() {
 }
 
 void Synth::refreshSystemChanAssign(Bit8u firstPart, Bit8u lastPart) {
-	memset(chantable, 0xFF, sizeof(chantable));
+	memset(extensions.chantable, 0xFF, sizeof(extensions.chantable));
 
-	// CONFIRMED: In the case of assigning a channel to multiple parts, the lower part wins.
+	// CONFIRMED: In the case of assigning a MIDI channel to multiple parts,
+	//            the messages received on that MIDI channel are handled by all the parts.
 	for (Bit32u i = 0; i <= 8; i++) {
 		if (parts[i] != NULL && i >= firstPart && i <= lastPart) {
 			// CONFIRMED: Decay is started for all polys, and all controllers are reset, for every part whose assignment was touched by the sysex write.
@@ -1553,8 +1687,13 @@ void Synth::refreshSystemChanAssign(Bit8u firstPart, Bit8u lastPart) {
 			parts[i]->resetAllControllers();
 		}
 		Bit8u chan = mt32ram.system.chanAssign[i];
-		if (chan < 16 && chantable[chan] > 8) {
-			chantable[chan] = Bit8u(i);
+		if (chan > 15) continue;
+		Bit8u *chanParts = extensions.chantable[chan];
+		for (Bit32u j = 0; j <= 8; j++) {
+			if (chanParts[j] > 8) {
+				chanParts[j] = Bit8u(i);
+				break;
+			}
 		}
 	}
 
@@ -1595,9 +1734,25 @@ void Synth::reset() {
 		}
 	}
 	refreshSystem();
+	resetMasterTunePitchDelta();
 	isActive();
 }
 
+void Synth::resetMasterTunePitchDelta() {
+	// This effectively resets master tune to 440.0Hz.
+	// Despite that the manual claims 442.0Hz is the default setting for master tune,
+	// it doesn't actually take effect upon a reset due to a bug in the reset routine.
+	// CONFIRMED: This bug is present in all supported Control ROMs.
+	extensions.masterTunePitchDelta = 0;
+#if MT32EMU_MONITOR_SYSEX > 0
+	printDebug(" Actual Master Tune reset to 440.0");
+#endif
+}
+
+Bit32s Synth::getMasterTunePitchDelta() const {
+	return extensions.masterTunePitchDelta;
+}
+
 MidiEvent::~MidiEvent() {
 	if (sysexData != NULL) {
 		delete[] sysexData;
@@ -1677,73 +1832,140 @@ bool MidiEventQueue::isEmpty() const {
 	return startPosition == endPosition;
 }
 
+void Synth::selectRendererType(RendererType newRendererType) {
+	extensions.selectedRendererType = newRendererType;
+}
+
+RendererType Synth::getSelectedRendererType() const {
+	return extensions.selectedRendererType;
+}
+
 Bit32u Synth::getStereoOutputSampleRate() const {
 	return (analog == NULL) ? SAMPLE_RATE : analog->getOutputSampleRate();
 }
 
-void Renderer::render(SampleFormatConverter &converter, Bit32u len) {
-	if (!synth.opened) {
-		converter.addSilence(len << 1);
+template <class Sample>
+void RendererImpl<Sample>::doRender(Sample *stereoStream, Bit32u len) {
+	if (!isActivated()) {
+		incRenderedSampleCount(getAnalog().getDACStreamsLength(len));
+		if (!getAnalog().process(NULL, NULL, NULL, NULL, NULL, NULL, stereoStream, len)) {
+			printDebug("RendererImpl: Invalid call to Analog::process()!\n");
+		}
+		Synth::muteSampleBuffer(stereoStream, len << 1);
 		return;
 	}
 
-	if (!synth.activated) {
-		synth.renderedSampleCount += synth.analog->getDACStreamsLength(len);
-		synth.analog->process(NULL, NULL, NULL, NULL, NULL, NULL, NULL, len);
-		converter.addSilence(len << 1);
-		return;
+	while (len > 0) {
+		// As in AnalogOutputMode_ACCURATE mode output is upsampled, MAX_SAMPLES_PER_RUN is more than enough for the temp buffers.
+		Bit32u thisPassLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
+		doRenderStreams(tmpBuffers, getAnalog().getDACStreamsLength(thisPassLen));
+		if (!getAnalog().process(stereoStream, tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisPassLen)) {
+			printDebug("RendererImpl: Invalid call to Analog::process()!\n");
+			Synth::muteSampleBuffer(stereoStream, len << 1);
+			return;
+		}
+		stereoStream += thisPassLen << 1;
+		len -= thisPassLen;
 	}
+}
 
+template <class Sample>
+template <class O>
+void RendererImpl<Sample>::doRenderAndConvert(O *stereoStream, Bit32u len) {
+	Sample renderingBuffer[MAX_SAMPLES_PER_RUN << 1];
 	while (len > 0) {
-		// As in AnalogOutputMode_ACCURATE mode output is upsampled, MAX_SAMPLES_PER_RUN is more than enough for the temp buffers.
 		Bit32u thisPassLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
-		synth.renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, synth.analog->getDACStreamsLength(thisPassLen));
-		synth.analog->process(converter.sampleBuffer, tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisPassLen);
-		converter.convert(thisPassLen << 1);
+		doRender(renderingBuffer, thisPassLen);
+		convertSampleFormat(renderingBuffer, stereoStream, thisPassLen << 1);
+		stereoStream += thisPassLen << 1;
 		len -= thisPassLen;
 	}
 }
 
+template<>
+void RendererImpl<IntSample>::render(IntSample *stereoStream, Bit32u len) {
+	doRender(stereoStream, len);
+}
+
+template<>
+void RendererImpl<IntSample>::render(FloatSample *stereoStream, Bit32u len) {
+	doRenderAndConvert(stereoStream, len);
+}
+
+template<>
+void RendererImpl<FloatSample>::render(IntSample *stereoStream, Bit32u len) {
+	doRenderAndConvert(stereoStream, len);
+}
+
+template<>
+void RendererImpl<FloatSample>::render(FloatSample *stereoStream, Bit32u len) {
+	doRender(stereoStream, len);
+}
+
+template <class S>
+static inline void renderStereo(bool opened, Renderer *renderer, S *stream, Bit32u len) {
+	if (opened) {
+		renderer->render(stream, len);
+	} else {
+		Synth::muteSampleBuffer(stream, len << 1);
+	}
+}
+
 void Synth::render(Bit16s *stream, Bit32u len) {
-#if MT32EMU_USE_FLOAT_SAMPLES
-	BufferedSampleFormatConverter<2> converter(stream);
-#else
-	SampleFormatConverter converter(stream);
-#endif
-	renderer.render(converter, len);
+	renderStereo(opened, renderer, stream, len);
 }
 
 void Synth::render(float *stream, Bit32u len) {
-#if MT32EMU_USE_FLOAT_SAMPLES
-	SampleFormatConverter converter(stream);
-#else
-	BufferedSampleFormatConverter<2> converter(stream);
-#endif
-	renderer.render(converter, len);
+	renderStereo(opened, renderer, stream, len);
 }
 
-void Renderer::renderStreams(
-	SampleFormatConverter &nonReverbLeft, SampleFormatConverter &nonReverbRight,
-	SampleFormatConverter &reverbDryLeft, SampleFormatConverter &reverbDryRight,
-	SampleFormatConverter &reverbWetLeft, SampleFormatConverter &reverbWetRight,
-	Bit32u len)
-{
-	if (!synth.opened) {
-		nonReverbLeft.addSilence(len);
-		nonReverbRight.addSilence(len);
-		reverbDryLeft.addSilence(len);
-		reverbDryRight.addSilence(len);
-		reverbWetLeft.addSilence(len);
-		reverbWetRight.addSilence(len);
-		return;
+template <class Sample>
+static inline void advanceStream(Sample *&stream, Bit32u len) {
+	if (stream != NULL) {
+		stream += len;
 	}
+}
+
+template <class Sample>
+static inline void advanceStreams(DACOutputStreams<Sample> &streams, Bit32u len) {
+	advanceStream(streams.nonReverbLeft, len);
+	advanceStream(streams.nonReverbRight, len);
+	advanceStream(streams.reverbDryLeft, len);
+	advanceStream(streams.reverbDryRight, len);
+	advanceStream(streams.reverbWetLeft, len);
+	advanceStream(streams.reverbWetRight, len);
+}
+
+template <class Sample>
+static inline void muteStreams(const DACOutputStreams<Sample> &streams, Bit32u len) {
+	Synth::muteSampleBuffer(streams.nonReverbLeft, len);
+	Synth::muteSampleBuffer(streams.nonReverbRight, len);
+	Synth::muteSampleBuffer(streams.reverbDryLeft, len);
+	Synth::muteSampleBuffer(streams.reverbDryRight, len);
+	Synth::muteSampleBuffer(streams.reverbWetLeft, len);
+	Synth::muteSampleBuffer(streams.reverbWetRight, len);
+}
+
+template <class I, class O>
+static inline void convertStreamsFormat(const DACOutputStreams<I> &inStreams, const DACOutputStreams<O> &outStreams, Bit32u len) {
+	convertSampleFormat(inStreams.nonReverbLeft, outStreams.nonReverbLeft, len);
+	convertSampleFormat(inStreams.nonReverbRight, outStreams.nonReverbRight, len);
+	convertSampleFormat(inStreams.reverbDryLeft, outStreams.reverbDryLeft, len);
+	convertSampleFormat(inStreams.reverbDryRight, outStreams.reverbDryRight, len);
+	convertSampleFormat(inStreams.reverbWetLeft, outStreams.reverbWetLeft, len);
+	convertSampleFormat(inStreams.reverbWetRight, outStreams.reverbWetRight, len);
+}
 
+template <class Sample>
+void RendererImpl<Sample>::doRenderStreams(const DACOutputStreams<Sample> &streams, Bit32u len)
+{
+	DACOutputStreams<Sample> tmpStreams = streams;
 	while (len > 0) {
 		// We need to ensure zero-duration notes will play so add minimum 1-sample delay.
 		Bit32u thisLen = 1;
-		if (!synth.isAbortingPoly()) {
-			const MidiEvent *nextEvent = synth.midiQueue->peekMidiEvent();
-			Bit32s samplesToNextEvent = (nextEvent != NULL) ? Bit32s(nextEvent->timestamp - synth.renderedSampleCount) : MAX_SAMPLES_PER_RUN;
+		if (!isAbortingPoly()) {
+			const MidiEvent *nextEvent = getMidiQueue().peekMidiEvent();
+			Bit32s samplesToNextEvent = (nextEvent != NULL) ? Bit32s(nextEvent->timestamp - getRenderedSampleCount()) : MAX_SAMPLES_PER_RUN;
 			if (samplesToNextEvent > 0) {
 				thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
 				if (thisLen > Bit32u(samplesToNextEvent)) {
@@ -1754,51 +1976,94 @@ void Renderer::renderStreams(
 					synth.playMsgNow(nextEvent->shortMessageData);
 					// If a poly is aborting we don't drop the event from the queue.
 					// Instead, we'll return to it again when the abortion is done.
-					if (!synth.isAbortingPoly()) {
-						synth.midiQueue->dropMidiEvent();
+					if (!isAbortingPoly()) {
+						getMidiQueue().dropMidiEvent();
 					}
 				} else {
 					synth.playSysexNow(nextEvent->sysexData, nextEvent->sysexLength);
-					synth.midiQueue->dropMidiEvent();
+					getMidiQueue().dropMidiEvent();
 				}
 			}
 		}
-		DACOutputStreams<Sample> streams = {
-			nonReverbLeft.sampleBuffer, nonReverbRight.sampleBuffer,
-			reverbDryLeft.sampleBuffer, reverbDryRight.sampleBuffer,
-			reverbWetLeft.sampleBuffer, reverbWetRight.sampleBuffer
-		};
-		doRenderStreams(streams, thisLen);
-		nonReverbLeft.convert(thisLen);
-		nonReverbRight.convert(thisLen);
-		reverbDryLeft.convert(thisLen);
-		reverbDryRight.convert(thisLen);
-		reverbWetLeft.convert(thisLen);
-		reverbWetRight.convert(thisLen);
+		produceStreams(tmpStreams, thisLen);
+		advanceStreams(tmpStreams, thisLen);
 		len -= thisLen;
 	}
 }
 
+template <class Sample>
+template <class O>
+void RendererImpl<Sample>::doRenderAndConvertStreams(const DACOutputStreams<O> &streams, Bit32u len) {
+	Sample cnvNonReverbLeft[MAX_SAMPLES_PER_RUN], cnvNonReverbRight[MAX_SAMPLES_PER_RUN];
+	Sample cnvReverbDryLeft[MAX_SAMPLES_PER_RUN], cnvReverbDryRight[MAX_SAMPLES_PER_RUN];
+	Sample cnvReverbWetLeft[MAX_SAMPLES_PER_RUN], cnvReverbWetRight[MAX_SAMPLES_PER_RUN];
+
+	const DACOutputStreams<Sample> cnvStreams = {
+		cnvNonReverbLeft, cnvNonReverbRight,
+		cnvReverbDryLeft, cnvReverbDryRight,
+		cnvReverbWetLeft, cnvReverbWetRight
+	};
+
+	DACOutputStreams<O> tmpStreams = streams;
+
+	while (len > 0) {
+		Bit32u thisPassLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
+		doRenderStreams(cnvStreams, thisPassLen);
+		convertStreamsFormat(cnvStreams, tmpStreams, thisPassLen);
+		advanceStreams(tmpStreams, thisPassLen);
+		len -= thisPassLen;
+	}
+}
+
+template<>
+void RendererImpl<IntSample>::renderStreams(const DACOutputStreams<IntSample> &streams, Bit32u len) {
+	doRenderStreams(streams, len);
+}
+
+template<>
+void RendererImpl<IntSample>::renderStreams(const DACOutputStreams<FloatSample> &streams, Bit32u len) {
+	doRenderAndConvertStreams(streams, len);
+}
+
+template<>
+void RendererImpl<FloatSample>::renderStreams(const DACOutputStreams<IntSample> &streams, Bit32u len) {
+	doRenderAndConvertStreams(streams, len);
+}
+
+template<>
+void RendererImpl<FloatSample>::renderStreams(const DACOutputStreams<FloatSample> &streams, Bit32u len) {
+	doRenderStreams(streams, len);
+}
+
+template <class S>
+static inline void renderStreams(bool opened, Renderer *renderer, const DACOutputStreams<S> &streams, Bit32u len) {
+	if (opened) {
+		renderer->renderStreams(streams, len);
+	} else {
+		muteStreams(streams, len);
+	}
+}
+
+void Synth::renderStreams(const DACOutputStreams<Bit16s> &streams, Bit32u len) {
+	MT32Emu::renderStreams(opened, renderer, streams, len);
+}
+
+void Synth::renderStreams(const DACOutputStreams<float> &streams, Bit32u len) {
+	MT32Emu::renderStreams(opened, renderer, streams, len);
+}
+
 void Synth::renderStreams(
 	Bit16s *nonReverbLeft, Bit16s *nonReverbRight,
 	Bit16s *reverbDryLeft, Bit16s *reverbDryRight,
 	Bit16s *reverbWetLeft, Bit16s *reverbWetRight,
 	Bit32u len)
 {
-#if MT32EMU_USE_FLOAT_SAMPLES
-	BufferedSampleFormatConverter<> convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight);
-	BufferedSampleFormatConverter<> convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight);
-	BufferedSampleFormatConverter<> convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight);
-#else
-	SampleFormatConverter convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight);
-	SampleFormatConverter convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight);
-	SampleFormatConverter convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight);
-#endif
-	renderer.renderStreams(
-		convNonReverbLeft, convNonReverbRight,
-		convReverbDryLeft, convReverbDryRight,
-		convReverbWetLeft, convReverbWetRight,
-		len);
+	DACOutputStreams<IntSample> streams = {
+		nonReverbLeft, nonReverbRight,
+		reverbDryLeft, reverbDryRight,
+		reverbWetLeft, reverbWetRight
+	};
+	renderStreams(streams, len);
 }
 
 void Synth::renderStreams(
@@ -1807,30 +2072,19 @@ void Synth::renderStreams(
 	float *reverbWetLeft, float *reverbWetRight,
 	Bit32u len)
 {
-#if MT32EMU_USE_FLOAT_SAMPLES
-	SampleFormatConverter convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight);
-	SampleFormatConverter convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight);
-	SampleFormatConverter convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight);
-#else
-	BufferedSampleFormatConverter<> convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight);
-	BufferedSampleFormatConverter<> convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight);
-	BufferedSampleFormatConverter<> convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight);
-#endif
-	renderer.renderStreams(
-		convNonReverbLeft, convNonReverbRight,
-		convReverbDryLeft, convReverbDryRight,
-		convReverbWetLeft, convReverbWetRight,
-		len);
+	DACOutputStreams<FloatSample> streams = {
+		nonReverbLeft, nonReverbRight,
+		reverbDryLeft, reverbDryRight,
+		reverbWetLeft, reverbWetRight
+	};
+	renderStreams(streams, len);
 }
 
 // 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 Renderer::produceLA32Output(Sample *buffer, Bit32u len) {
-#if MT32EMU_USE_FLOAT_SAMPLES
-	(void)buffer;
-	(void)len;
-#else
-	switch (synth.dacInputMode) {
+template <>
+void RendererImpl<IntSample>::produceLA32Output(IntSample *buffer, Bit32u len) {
+	switch (synth.getDACInputMode()) {
 		case DACInputMode_GENERATION2:
 			while (len--) {
 				*buffer = (*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE) | ((*buffer >> 14) & 0x0001);
@@ -1839,32 +2093,71 @@ void Renderer::produceLA32Output(Sample *buffer, Bit32u len) {
 			break;
 		case DACInputMode_NICE:
 			while (len--) {
-				*buffer = Synth::clipSampleEx(SampleEx(*buffer) << 1);
+				*buffer = Synth::clipSampleEx(IntSampleEx(*buffer) << 1);
 				++buffer;
 			}
 			break;
 		default:
 			break;
 	}
-#endif
 }
 
-void Renderer::convertSamplesToOutput(Sample *buffer, Bit32u len) {
-#if MT32EMU_USE_FLOAT_SAMPLES
-	(void)buffer;
-	(void)len;
-#else
-	if (synth.dacInputMode == DACInputMode_GENERATION1) {
+template <>
+void RendererImpl<IntSample>::convertSamplesToOutput(IntSample *buffer, Bit32u len) {
+	if (synth.getDACInputMode() == DACInputMode_GENERATION1) {
 		while (len--) {
-			*buffer = Sample((*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE));
+			*buffer = IntSample((*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE));
 			++buffer;
 		}
 	}
-#endif
 }
 
-void Renderer::doRenderStreams(DACOutputStreams<Sample> &streams, Bit32u len) {
-	if (synth.activated) {
+static inline float produceDistortedSample(float sample) {
+	// Here we roughly simulate the distortion caused by the DAC bit shift.
+	if (sample < -1.0f) {
+		return sample + 2.0f;
+	} else if (1.0f < sample) {
+		return sample - 2.0f;
+	}
+	return sample;
+}
+
+template <>
+void RendererImpl<FloatSample>::produceLA32Output(FloatSample *buffer, Bit32u len) {
+	switch (synth.getDACInputMode()) {
+	case DACInputMode_NICE:
+		// Note, we do not do any clamping for floats here to avoid introducing distortions.
+		// This means that the output signal may actually overshoot the unity when the volume is set too high.
+		// We leave it up to the consumer whether the output is to be clamped or properly normalised further on.
+		while (len--) {
+			*buffer *= 2.0f;
+			buffer++;
+		}
+		break;
+	case DACInputMode_GENERATION2:
+		while (len--) {
+			*buffer = produceDistortedSample(2.0f * *buffer);
+			buffer++;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+template <>
+void RendererImpl<FloatSample>::convertSamplesToOutput(FloatSample *buffer, Bit32u len) {
+	if (synth.getDACInputMode() == DACInputMode_GENERATION1) {
+		while (len--) {
+			*buffer = produceDistortedSample(2.0f * *buffer);
+			buffer++;
+		}
+	}
+}
+
+template <class Sample>
+void RendererImpl<Sample>::produceStreams(const DACOutputStreams<Sample> &streams, Bit32u len) {
+	if (isActivated()) {
 		// Even if LA32 output isn't desired, we proceed anyway with temp buffers
 		Sample *nonReverbLeft = streams.nonReverbLeft == NULL ? tmpNonReverbLeft : streams.nonReverbLeft;
 		Sample *nonReverbRight = streams.nonReverbRight == NULL ? tmpNonReverbRight : streams.nonReverbRight;
@@ -1877,10 +2170,10 @@ void Renderer::doRenderStreams(DACOutputStreams<Sample> &streams, Bit32u len) {
 		Synth::muteSampleBuffer(reverbDryRight, len);
 
 		for (unsigned int i = 0; i < synth.getPartialCount(); i++) {
-			if (synth.partialManager->shouldReverb(i)) {
-				synth.partialManager->produceOutput(i, reverbDryLeft, reverbDryRight, len);
+			if (getPartialManager().shouldReverb(i)) {
+				getPartialManager().produceOutput(i, reverbDryLeft, reverbDryRight, len);
 			} else {
-				synth.partialManager->produceOutput(i, nonReverbLeft, nonReverbRight, len);
+				getPartialManager().produceOutput(i, nonReverbLeft, nonReverbRight, len);
 			}
 		}
 
@@ -1888,7 +2181,9 @@ void Renderer::doRenderStreams(DACOutputStreams<Sample> &streams, Bit32u len) {
 		produceLA32Output(reverbDryRight, len);
 
 		if (synth.isReverbEnabled()) {
-			synth.reverbModel->process(reverbDryLeft, reverbDryRight, streams.reverbWetLeft, streams.reverbWetRight, len);
+			if (!getReverbModel().process(reverbDryLeft, reverbDryRight, streams.reverbWetLeft, streams.reverbWetRight, len)) {
+				printDebug("RendererImpl: Invalid call to BReverbModel::process()!\n");
+			}
 			if (streams.reverbWetLeft != NULL) convertSamplesToOutput(streams.reverbWetLeft, len);
 			if (streams.reverbWetRight != NULL) convertSamplesToOutput(streams.reverbWetRight, len);
 		} else {
@@ -1908,16 +2203,11 @@ void Renderer::doRenderStreams(DACOutputStreams<Sample> &streams, Bit32u len) {
 		if (streams.reverbDryLeft != NULL) convertSamplesToOutput(reverbDryLeft, len);
 		if (streams.reverbDryRight != NULL) convertSamplesToOutput(reverbDryRight, len);
 	} else {
-		Synth::muteSampleBuffer(streams.nonReverbLeft, len);
-		Synth::muteSampleBuffer(streams.nonReverbRight, len);
-		Synth::muteSampleBuffer(streams.reverbDryLeft, len);
-		Synth::muteSampleBuffer(streams.reverbDryRight, len);
-		Synth::muteSampleBuffer(streams.reverbWetLeft, len);
-		Synth::muteSampleBuffer(streams.reverbWetRight, len);
+		muteStreams(streams, len);
 	}
 
-	synth.partialManager->clearAlreadyOutputed();
-	synth.renderedSampleCount += len;
+	getPartialManager().clearAlreadyOutputed();
+	incRenderedSampleCount(len);
 }
 
 void Synth::printPartialUsage(Bit32u sampleOffset) {
diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h
index fe31d99..54417d5 100644
--- a/audio/softsynth/mt32/Synth.h
+++ b/audio/softsynth/mt32/Synth.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -30,6 +30,7 @@ namespace MT32Emu {
 
 class Analog;
 class BReverbModel;
+class Extensions;
 class MemoryRegion;
 class MidiEventQueue;
 class Part;
@@ -123,6 +124,7 @@ friend class RhythmPart;
 friend class SamplerateAdapter;
 friend class SoxrAdapter;
 friend class TVA;
+friend class TVF;
 friend class TVP;
 
 private:
@@ -151,7 +153,7 @@ private:
 	const char (*soundGroupNames)[9]; // Array
 
 	Bit32u partialCount;
-	Bit8u chantable[16]; // NOTE: value above 8 means that the channel is not assigned
+	Bit8u nukeme[16]; // FIXME: Nuke it. For binary compatibility only.
 
 	MidiEventQueue *midiQueue;
 	volatile Bit32u lastReceivedMIDIEventTimestamp;
@@ -186,16 +188,17 @@ private:
 	Poly *abortingPoly;
 
 	Analog *analog;
-	Renderer &renderer;
+	Renderer *renderer;
 
 	// Binary compatibility helper.
-	void *reserved;
+	Extensions &extensions;
 
 	// **************************** Implementation methods **************************
 
 	Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp);
 	bool isAbortingPoly() const { return abortingPoly != NULL; }
 
+	void writeSysexGlobal(Bit32u addr, const Bit8u *sysex, Bit32u len);
 	void readSysex(Bit8u channel, const Bit8u *sysex, Bit32u len) const;
 	void initMemoryRegions();
 	void deleteMemoryRegions();
@@ -229,6 +232,9 @@ private:
 	// partNum should be 0..7 for Part 1..8, or 8 for Rhythm
 	const Part *getPart(Bit8u partNum) const;
 
+	void resetMasterTunePitchDelta();
+	Bit32s getMasterTunePitchDelta() const;
+
 public:
 	static inline Bit16s clipSampleEx(Bit32s sampleEx) {
 		// Clamp values above 32767 to 32767, and values below -32768 to -32768
@@ -257,11 +263,11 @@ public:
 	}
 
 	static inline Bit16s convertSample(float sample) {
-		return Synth::clipSampleEx(Bit32s(sample * 16384.0f)); // This multiplier takes into account the DAC bit shift
+		return Synth::clipSampleEx(Bit32s(sample * 32768.0f)); // This multiplier corresponds to normalised floats
 	}
 
 	static inline float convertSample(Bit16s sample) {
-		return float(sample) / 16384.0f; // This multiplier takes into account the DAC bit shift
+		return float(sample) / 32768.0f; // This multiplier corresponds to normalised floats
 	}
 
 	// Returns library version as an integer in format: 0x00MMmmpp, where:
@@ -307,6 +313,10 @@ public:
 	// Returns the actual queue size being used.
 	MT32EMU_EXPORT Bit32u setMIDIEventQueueSize(Bit32u);
 
+	// Returns current value of the global counter of samples rendered since the synth was created (at the native sample rate 32000 Hz).
+	// This method helps to compute accurate timestamp of a MIDI message to use with the methods below.
+	MT32EMU_EXPORT Bit32u getInternalRenderedSampleCount() const;
+
 	// Enqueues a MIDI event for subsequent playback.
 	// 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).
@@ -381,7 +391,6 @@ public:
 	// 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
 	MT32EMU_EXPORT void setOutputGain(float gain);
 	// Returns current output gain factor for synth output channels.
 	MT32EMU_EXPORT float getOutputGain() const;
@@ -394,7 +403,6 @@ public:
 	// 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
 	// of that for LA32 analogue output. This factor is applied to the reverb output gain.
-	// Ignored in DACInputMode_PURE
 	MT32EMU_EXPORT void setReverbOutputGain(float gain);
 	// Returns current output gain factor for reverb wet output channels.
 	MT32EMU_EXPORT float getReverbOutputGain() const;
@@ -404,6 +412,24 @@ public:
 	// Returns whether left and right output channels are swapped.
 	MT32EMU_EXPORT bool isReversedStereoEnabled() const;
 
+	// Allows to toggle the NiceAmpRamp mode.
+	// In this mode, we want to ensure that amp ramp never jumps to the target
+	// value and always gradually increases or decreases. It seems that real units
+	// do not bother to always check if a newly started ramp leads to a jump.
+	// We also prefer the quality improvement over the emulation accuracy,
+	// so this mode is enabled by default.
+	MT32EMU_EXPORT void setNiceAmpRampEnabled(bool enabled);
+	// Returns whether NiceAmpRamp mode is enabled.
+	MT32EMU_EXPORT bool isNiceAmpRampEnabled() const;
+
+	// Selects new type of the wave generator and renderer to be used during subsequent calls to open().
+	// By default, RendererType_BIT16S is selected.
+	// See RendererType for details.
+	MT32EMU_EXPORT void selectRendererType(RendererType);
+	// Returns previously selected type of the wave generator and renderer.
+	// See RendererType for details.
+	MT32EMU_EXPORT RendererType getSelectedRendererType() const;
+
 	// Returns actual sample rate used in emulation of stereo analog circuitry of hardware units.
 	// See comment for render() below.
 	MT32EMU_EXPORT Bit32u getStereoOutputSampleRate() const;
@@ -422,14 +448,10 @@ public:
 	// NULL may be specified in place of any or all of the stream buffers to skip it.
 	// The length is in samples, not bytes. Uses NATIVE byte ordering.
 	MT32EMU_EXPORT void renderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len);
-	void renderStreams(const DACOutputStreams<Bit16s> &streams, Bit32u len) {
-		renderStreams(streams.nonReverbLeft, streams.nonReverbRight, streams.reverbDryLeft, streams.reverbDryRight, streams.reverbWetLeft, streams.reverbWetRight, len);
-	}
+	MT32EMU_EXPORT void renderStreams(const DACOutputStreams<Bit16s> &streams, Bit32u len);
 	// Same as above but outputs to float streams.
 	MT32EMU_EXPORT void renderStreams(float *nonReverbLeft, float *nonReverbRight, float *reverbDryLeft, float *reverbDryRight, float *reverbWetLeft, float *reverbWetRight, Bit32u len);
-	void renderStreams(const DACOutputStreams<float> &streams, Bit32u len) {
-		renderStreams(streams.nonReverbLeft, streams.nonReverbRight, streams.reverbDryLeft, streams.reverbDryRight, streams.reverbWetLeft, streams.reverbWetRight, len);
-	}
+	MT32EMU_EXPORT void renderStreams(const DACOutputStreams<float> &streams, Bit32u len);
 
 	// Returns true when there is at least one active partial, otherwise false.
 	MT32EMU_EXPORT bool hasActivePartials() const;
diff --git a/audio/softsynth/mt32/TVA.cpp b/audio/softsynth/mt32/TVA.cpp
index c20b8b6..3f7064f 100644
--- a/audio/softsynth/mt32/TVA.cpp
+++ b/audio/softsynth/mt32/TVA.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -43,7 +43,7 @@ void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) {
 	phase = newPhase;
 	ampRamp->startRamp(newTarget, newIncrement);
 #if MT32EMU_MONITOR_TVA >= 1
-	partial->getSynth()->printDebug("[+%lu] [Partial %d] TVA,ramp,%d,%d,%d,%d", partial->debugGetSampleNum(), partial->debugGetPartialNum(), (newIncrement & 0x80) ? -1 : 1, (newIncrement & 0x7F), newPhase);
+	partial->getSynth()->printDebug("[+%lu] [Partial %d] TVA,ramp,%x,%s%x,%d", partial->debugGetSampleNum(), partial->debugGetPartialNum(), newTarget, (newIncrement & 0x80) ? "-" : "+", (newIncrement & 0x7F), newPhase);
 #endif
 }
 
@@ -99,10 +99,10 @@ static int calcVeloAmpSubtraction(Bit8u veloSensitivity, unsigned int velocity)
 	return absVelocityMult - (velocityMult >> 8); // PORTABILITY NOTE: Assumes arithmetic shift
 }
 
-static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemParams::System *system, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, const MemParams::RhythmTemp *rhythmTemp, int biasAmpSubtraction, int veloAmpSubtraction, Bit8u expression) {
+static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemParams::System *system, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, const MemParams::RhythmTemp *rhythmTemp, int biasAmpSubtraction, int veloAmpSubtraction, Bit8u expression, bool hasRingModQuirk) {
 	int amp = 155;
 
-	if (!partial->isRingModulatingSlave()) {
+	if (!(hasRingModQuirk ? partial->isRingModulatingNoMix() : partial->isRingModulatingSlave())) {
 		amp -= tables->masterVolToAmpSubtraction[system->masterVol];
 		if (amp < 0) {
 			return 0;
@@ -169,7 +169,7 @@ void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartial
 	biasAmpSubtraction = calcBiasAmpSubtractions(partialParam, key);
 	veloAmpSubtraction = calcVeloAmpSubtraction(partialParam->tva.veloSensitivity, velocity);
 
-	int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, newRhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
+	int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, newRhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression(), partial->getSynth()->controlROMFeatures->quirkRingModulationNoMix);
 	int newPhase;
 	if (partialParam->tva.envTime[0] == 0) {
 		// Initially go to the TVA_PHASE_ATTACK target amp, and spend the next phase going from there to the TVA_PHASE_2 target amp
@@ -221,18 +221,29 @@ void TVA::recalcSustain() {
 	}
 	// We're sustaining. Recalculate all the values
 	const Tables *tables = &Tables::getInstance();
-	int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
+	int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression(), partial->getSynth()->controlROMFeatures->quirkRingModulationNoMix);
 	newTarget += partialParam->tva.envLevel[3];
-	// Since we're in TVA_PHASE_SUSTAIN at this point, we know that target has been reached and an interrupt fired, so we can rely on it being the current amp.
+
+	// Although we're in TVA_PHASE_SUSTAIN at this point, we cannot be sure that there is no active ramp at the moment.
+	// In case the channel volume or the expression changes frequently, the previously started ramp may still be in progress.
+	// Real hardware units ignore this possibility and rely on the assumption that the target is the current amp.
+	// This is OK in most situations but when the ramp that is currently in progress needs to change direction
+	// due to a volume/expression update, this leads to a jump in the amp that is audible as an unpleasant click.
+	// To avoid that, we compare the newTarget with the the actual current ramp value and correct the direction if necessary.
 	int targetDelta = newTarget - target;
 
 	// Calculate an increment to get to the new amp value in a short, more or less consistent amount of time
 	Bit8u newIncrement;
-	if (targetDelta >= 0) {
+	bool descending = targetDelta < 0;
+	if (!descending) {
 		newIncrement = tables->envLogarithmicTime[Bit8u(targetDelta)] - 2;
 	} else {
 		newIncrement = (tables->envLogarithmicTime[Bit8u(-targetDelta)] - 2) | 0x80;
 	}
+	if (part->getSynth()->isNiceAmpRampEnabled() && (descending != ampRamp->isBelowCurrent(newTarget))) {
+		newIncrement ^= 0x80;
+	}
+
 	// Configure so that once the transition's complete and nextPhase() is called, we'll just re-enter sustain phase (or decay phase, depending on parameters at the time).
 	startRamp(newTarget, newIncrement, TVA_PHASE_SUSTAIN - 1);
 }
@@ -260,7 +271,7 @@ void TVA::nextPhase() {
 	}
 
 	bool allLevelsZeroFromNowOn = false;
-	if (partialParam->tva.envLevel[3] == 0) {
+	if (!partial->getSynth()->controlROMFeatures->quirkTVAZeroEnvLevels && partialParam->tva.envLevel[3] == 0) {
 		if (newPhase == TVA_PHASE_4) {
 			allLevelsZeroFromNowOn = true;
 		} else if (partialParam->tva.envLevel[2] == 0) {
@@ -283,7 +294,7 @@ void TVA::nextPhase() {
 	int envPointIndex = phase;
 
 	if (!allLevelsZeroFromNowOn) {
-		newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
+		newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression(), partial->getSynth()->controlROMFeatures->quirkRingModulationNoMix);
 
 		if (newPhase == TVA_PHASE_SUSTAIN || newPhase == TVA_PHASE_RELEASE) {
 			if (partialParam->tva.envLevel[3] == 0) {
diff --git a/audio/softsynth/mt32/TVA.h b/audio/softsynth/mt32/TVA.h
index f593b4e..cf9296d 100644
--- a/audio/softsynth/mt32/TVA.h
+++ b/audio/softsynth/mt32/TVA.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/TVF.cpp b/audio/softsynth/mt32/TVF.cpp
index b296c34..7ba9c7f 100644
--- a/audio/softsynth/mt32/TVF.cpp
+++ b/audio/softsynth/mt32/TVF.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -21,6 +21,7 @@
 #include "LA32Ramp.h"
 #include "Partial.h"
 #include "Poly.h"
+#include "Synth.h"
 #include "Tables.h"
 
 namespace MT32Emu {
@@ -52,7 +53,7 @@ enum {
 	PHASE_DONE = 7
 };
 
-static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u basePitch, unsigned int key) {
+static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u basePitch, unsigned int key, bool quirkTVFBaseCutoffLimit) {
 	// This table matches the values used by a real LAPC-I.
 	static const Bit8s biasLevelToBiasMult[] = {85, 42, 21, 16, 10, 5, 2, 0, -2, -5, -10, -16, -21, -74, -85};
 	// These values represent unique options with no consistent pattern, so we have to use something like a table in any case.
@@ -90,8 +91,14 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u
 		if (pitchDeltaThing > 0) {
 			baseCutoff -= pitchDeltaThing;
 		}
-	} else if (baseCutoff < -2048) {
-		baseCutoff = -2048;
+	} else if (quirkTVFBaseCutoffLimit) {
+		if (baseCutoff <= -0x400) {
+			baseCutoff = -400;
+		}
+	} else {
+		if (baseCutoff < -2048) {
+			baseCutoff = -2048;
+		}
 	}
 	baseCutoff += 2056;
 	baseCutoff >>= 4; // PORTABILITY NOTE: Hmm... Depends whether it could've been below -2056, but maybe arithmetic shift assumed?
@@ -110,7 +117,7 @@ void TVF::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) {
 	phase = newPhase;
 	cutoffModifierRamp->startRamp(newTarget, newIncrement);
 #if MT32EMU_MONITOR_TVF >= 1
-	partial->getSynth()->printDebug("[+%lu] [Partial %d] TVF,ramp,%d,%d,%d,%d", partial->debugGetSampleNum(), partial->debugGetPartialNum(), newTarget, (newIncrement & 0x80) ? -1 : 1, (newIncrement & 0x7F), newPhase);
+	partial->getSynth()->printDebug("[+%lu] [Partial %d] TVF,ramp,%x,%s%x,%d", partial->debugGetSampleNum(), partial->debugGetPartialNum(), newTarget, (newIncrement & 0x80) ? "-" : "+", (newIncrement & 0x7F), newPhase);
 #endif
 }
 
@@ -122,7 +129,7 @@ void TVF::reset(const TimbreParam::PartialParam *newPartialParam, unsigned int b
 
 	const Tables *tables = &Tables::getInstance();
 
-	baseCutoff = calcBaseCutoff(newPartialParam, basePitch, key);
+	baseCutoff = calcBaseCutoff(newPartialParam, basePitch, key, partial->getSynth()->controlROMFeatures->quirkTVFBaseCutoffLimit);
 #if MT32EMU_MONITOR_TVF >= 1
 	partial->getSynth()->printDebug("[+%lu] [Partial %d] TVF,base,%d", partial->debugGetSampleNum(), partial->debugGetPartialNum(), baseCutoff);
 #endif
diff --git a/audio/softsynth/mt32/TVF.h b/audio/softsynth/mt32/TVF.h
index 38dcef7..e637aa5 100644
--- a/audio/softsynth/mt32/TVF.h
+++ b/audio/softsynth/mt32/TVF.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/TVP.cpp b/audio/softsynth/mt32/TVP.cpp
index dca0003..a3b3640 100644
--- a/audio/softsynth/mt32/TVP.cpp
+++ b/audio/softsynth/mt32/TVP.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -51,13 +51,15 @@ static Bit16u keyToPitchTable[] = {
 	21845, 22187, 22528, 22869
 };
 
+// We want to do processing 4000 times per second. FIXME: This is pretty arbitrary.
+static const int NOMINAL_PROCESS_TIMER_PERIOD_SAMPLES = SAMPLE_RATE / 4000;
+
+// The timer runs at 500kHz. This is how much to increment it after 8 samples passes.
+// We multiply by 8 to get rid of the fraction and deal with just integers.
+static const int PROCESS_TIMER_INCREMENT_x8 = 8 * 500000 / SAMPLE_RATE;
+
 TVP::TVP(const Partial *usePartial) :
 	partial(usePartial), system(&usePartial->getSynth()->mt32ram.system) {
-	// We want to do processing 4000 times per second. FIXME: This is pretty arbitrary.
-	maxCounter = SAMPLE_RATE / 4000;
-	// The timer runs at 500kHz. We only need to bother updating it every maxCounter samples, before we do processing.
-	// This is how much to increment it by every maxCounter samples.
-	processTimerIncrement = 500000 * maxCounter / SAMPLE_RATE;
 }
 
 static Bit16s keyToPitch(unsigned int key) {
@@ -76,13 +78,15 @@ static inline Bit32s fineToPitch(Bit8u fine) {
 	return (fine - 50) * 4096 / 1200; // One cent per fine offset
 }
 
-static Bit32u calcBasePitch(const Partial *partial, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, unsigned int key) {
+static Bit32u calcBasePitch(const Partial *partial, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, unsigned int key, const ControlROMFeatureSet *controlROMFeatures) {
 	Bit32s basePitch = keyToPitch(key);
 	basePitch = (basePitch * pitchKeyfollowMult[partialParam->wg.pitchKeyfollow]) >> 13; // PORTABILITY NOTE: Assumes arithmetic shift
 	basePitch += coarseToPitch(partialParam->wg.pitchCoarse);
 	basePitch += fineToPitch(partialParam->wg.pitchFine);
-	// NOTE:Mok: This is done on MT-32, but not LAPC-I:
-	//pitch += coarseToPitch(patchTemp->patch.keyShift + 12);
+	if (controlROMFeatures->quirkKeyShift) {
+		// NOTE:Mok: This is done on MT-32, but not LAPC-I:
+		basePitch += coarseToPitch(patchTemp->patch.keyShift + 12);
+	}
 	basePitch += fineToPitch(patchTemp->patch.fineTune);
 
 	const ControlROMPCMStruct *controlROMPCMStruct = partial->getControlROMPCMStruct();
@@ -97,7 +101,12 @@ static Bit32u calcBasePitch(const Partial *partial, const TimbreParam::PartialPa
 			basePitch += 33037;
 		}
 	}
-	if (basePitch < 0) {
+
+	// MT-32 GEN0 does 16-bit calculations here, allowing an integer overflow.
+	// This quirk is observable playing the patch defined for timbre "HIT BOTTOM" in Larry 3.
+	if (controlROMFeatures->quirkBasePitchOverflow) {
+		basePitch = basePitch & 0xffff;
+	} else if (basePitch < 0) {
 		basePitch = 0;
 	}
 	if (basePitch > 59392) {
@@ -107,18 +116,22 @@ static Bit32u calcBasePitch(const Partial *partial, const TimbreParam::PartialPa
 }
 
 static Bit32u calcVeloMult(Bit8u veloSensitivity, unsigned int velocity) {
-	if (veloSensitivity == 0 || veloSensitivity > 3) {
-		// Note that on CM-32L/LAPC-I veloSensitivity is never > 3, since it's clipped to 3 by the max tables.
+	if (veloSensitivity == 0) {
 		return 21845; // aka floor(4096 / 12 * 64), aka ~64 semitones
 	}
+	unsigned int reversedVelocity = 127 - velocity;
+	unsigned int scaledReversedVelocity;
+	if (veloSensitivity > 3) {
+		// Note that on CM-32L/LAPC-I veloSensitivity is never > 3, since it's clipped to 3 by the max tables.
+		// MT-32 GEN0 has a bug here that leads to unspecified behaviour. We assume it is as follows.
+		scaledReversedVelocity = (reversedVelocity << 8) >> ((3 - veloSensitivity) & 0x1f);
+	} else {
+		scaledReversedVelocity = reversedVelocity << (5 + veloSensitivity);
+	}
 	// When velocity is 127, the multiplier is 21845, aka ~64 semitones (regardless of veloSensitivity).
 	// The lower the velocity, the lower the multiplier. The veloSensitivity determines the amount decreased per velocity value.
-	// The minimum multiplier (with velocity 0, veloSensitivity 3) is 170 (~half a semitone).
-	Bit32u veloMult = 32768;
-	veloMult -= (127 - velocity) << (5 + veloSensitivity);
-	veloMult *= 21845;
-	veloMult >>= 15;
-	return veloMult;
+	// The minimum multiplier on CM-32L/LAPC-I (with velocity 0, veloSensitivity 3) is 170 (~half a semitone).
+	return ((32768 - scaledReversedVelocity) * 21845) >> 15;
 }
 
 static Bit32s calcTargetPitchOffsetWithoutLFO(const TimbreParam::PartialParam *partialParam, int levelIndex, unsigned int velocity) {
@@ -139,7 +152,7 @@ void TVP::reset(const Part *usePart, const TimbreParam::PartialParam *usePartial
 	// FIXME: We're using a per-TVP timer instead of a system-wide one for convenience.
 	timeElapsed = 0;
 
-	basePitch = calcBasePitch(partial, partialParam, patchTemp, key);
+	basePitch = calcBasePitch(partial, partialParam, patchTemp, key, partial->getSynth()->controlROMFeatures);
 	currentPitchOffset = calcTargetPitchOffsetWithoutLFO(partialParam, 0, velocity);
 	targetPitchOffsetWithoutLFO = currentPitchOffset;
 	phase = 0;
@@ -166,22 +179,23 @@ Bit32u TVP::getBasePitch() const {
 void TVP::updatePitch() {
 	Bit32s newPitch = basePitch + currentPitchOffset;
 	if (!partial->isPCM() || (partial->getControlROMPCMStruct()->len & 0x01) == 0) { // FIXME: Use !partial->pcmWaveEntry->unaffectedByMasterTune instead
-		// FIXME: masterTune recalculation doesn't really happen here, and there are various bugs not yet emulated
+		// FIXME: There are various bugs not yet emulated
 		// 171 is ~half a semitone.
-		newPitch += ((system->masterTune - 64) * 171) >> 6; // PORTABILITY NOTE: Assumes arithmetic shift.
+		newPitch += partial->getSynth()->getMasterTunePitchDelta();
 	}
 	if ((partialParam->wg.pitchBenderEnabled & 1) != 0) {
 		newPitch += part->getPitchBend();
 	}
-	if (newPitch < 0) {
+
+	// MT-32 GEN0 does 16-bit calculations here, allowing an integer overflow.
+	// This quirk is exploited e.g. in Colonel's Bequest timbres "Lightning" and "SwmpBackgr".
+	if (partial->getSynth()->controlROMFeatures->quirkPitchEnvelopeOverflow) {
+		newPitch = newPitch & 0xffff;
+	} else if (newPitch < 0) {
 		newPitch = 0;
 	}
-
-	// Skipping this check seems about right emulation of MT-32 GEN0 quirk exploited in Colonel's Bequest timbre "Lightning"
-	if (partial->getSynth()->controlROMFeatures->quirkPitchEnvelopeOverflow == 0) {
-		if (newPitch > 59392) {
-			newPitch = 59392;
-		}
+	if (newPitch > 59392) {
+		newPitch = 59392;
 	}
 	pitch = Bit16u(newPitch);
 
@@ -284,13 +298,19 @@ void TVP::startDecay() {
 }
 
 Bit16u TVP::nextPitch() {
-	// FIXME: Write explanation of counter and time increment
+	// We emulate MCU software timer using these counter and processTimerIncrement variables.
+	// The value of nominalProcessTimerPeriod approximates the period in samples
+	// between subsequent firings of the timer that normally occur.
+	// However, accurate emulation is quite complicated because the timer is not guaranteed to fire in time.
+	// This makes pitch variations on real unit non-deterministic and dependent on various factors.
 	if (counter == 0) {
-		timeElapsed += processTimerIncrement;
-		timeElapsed = timeElapsed & 0x00FFFFFF;
+		timeElapsed = (timeElapsed + processTimerIncrement) & 0x00FFFFFF;
+		// This roughly emulates pitch deviations observed on real units when playing a single partial that uses TVP/LFO.
+		counter = NOMINAL_PROCESS_TIMER_PERIOD_SAMPLES + (rand() & 3);
+		processTimerIncrement = (PROCESS_TIMER_INCREMENT_x8 * counter) >> 3;
 		process();
 	}
-	counter = (counter + 1) % maxCounter;
+	counter--;
 	return pitch;
 }
 
diff --git a/audio/softsynth/mt32/TVP.h b/audio/softsynth/mt32/TVP.h
index be90f0f..896e8c1 100644
--- a/audio/softsynth/mt32/TVP.h
+++ b/audio/softsynth/mt32/TVP.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -36,7 +36,6 @@ private:
 	const TimbreParam::PartialParam *partialParam;
 	const MemParams::PatchTemp *patchTemp;
 
-	int maxCounter;
 	int processTimerIncrement;
 	int counter;
 	Bit32u timeElapsed;
diff --git a/audio/softsynth/mt32/Tables.cpp b/audio/softsynth/mt32/Tables.cpp
index cb34932..f12caa6 100644
--- a/audio/softsynth/mt32/Tables.cpp
+++ b/audio/softsynth/mt32/Tables.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h
index 249e329..4746509 100644
--- a/audio/softsynth/mt32/Tables.h
+++ b/audio/softsynth/mt32/Tables.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/Types.h b/audio/softsynth/mt32/Types.h
index f90dce1..f70e479 100644
--- a/audio/softsynth/mt32/Types.h
+++ b/audio/softsynth/mt32/Types.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/c_interface/c_interface.cpp b/audio/softsynth/mt32/c_interface/c_interface.cpp
index 6ae252b..adb1cb6 100644
--- a/audio/softsynth/mt32/c_interface/c_interface.cpp
+++ b/audio/softsynth/mt32/c_interface/c_interface.cpp
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -22,6 +22,7 @@
 #include "../ROMInfo.h"
 #include "../Synth.h"
 #include "../MidiStreamParser.h"
+#include "../SampleRateConverter.h"
 
 #include "c_types.h"
 #include "c_interface.h"
@@ -30,11 +31,17 @@ using namespace MT32Emu;
 
 namespace MT32Emu {
 
+struct SamplerateConversionState {
+	double outputSampleRate;
+	SamplerateConversionQuality srcQuality;
+	SampleRateConverter *src;
+};
+
 static mt32emu_service_version getSynthVersionID(mt32emu_service_i) {
 	return MT32EMU_SERVICE_VERSION_CURRENT;
 }
 
-static const mt32emu_service_i_v0 SERVICE_VTABLE = {
+static const mt32emu_service_i_v2 SERVICE_VTABLE = {
 	getSynthVersionID,
 	mt32emu_get_supported_report_handler_version,
 	mt32emu_get_supported_midi_receiver_version,
@@ -95,7 +102,17 @@ static const mt32emu_service_i_v0 SERVICE_VTABLE = {
 	mt32emu_get_partial_states,
 	mt32emu_get_playing_notes,
 	mt32emu_get_patch_name,
-	mt32emu_read_memory
+	mt32emu_read_memory,
+	mt32emu_get_best_analog_output_mode,
+	mt32emu_set_stereo_output_samplerate,
+	mt32emu_set_samplerate_conversion_quality,
+	mt32emu_select_renderer_type,
+	mt32emu_get_selected_renderer_type,
+	mt32emu_convert_output_to_synth_timestamp,
+	mt32emu_convert_synth_to_output_timestamp,
+	mt32emu_get_internal_rendered_sample_count,
+	mt32emu_set_nice_amp_ramp_enabled,
+	mt32emu_is_nice_amp_ramp_enabled
 };
 
 } // namespace MT32Emu
@@ -108,6 +125,7 @@ struct mt32emu_data {
 	DefaultMidiStreamParser *midiParser;
 	Bit32u partialCount;
 	AnalogOutputMode analogOutputMode;
+	SamplerateConversionState *srcState;
 };
 
 // Internal C++ utility stuff
@@ -303,8 +321,9 @@ static mt32emu_return_code addROMFile(mt32emu_data *data, File *file) {
 
 extern "C" {
 
-const mt32emu_service_i mt32emu_get_service_i() {
-	mt32emu_service_i i = { &SERVICE_VTABLE };
+mt32emu_service_i mt32emu_get_service_i() {
+	mt32emu_service_i i;
+	i.v2 = &SERVICE_VTABLE;
 	return i;
 }
 
@@ -328,6 +347,10 @@ mt32emu_bit32u mt32emu_get_stereo_output_samplerate(const mt32emu_analog_output_
 	return Synth::getStereoOutputSampleRate(static_cast<AnalogOutputMode>(analog_output_mode));
 }
 
+mt32emu_analog_output_mode mt32emu_get_best_analog_output_mode(const double target_samplerate) {
+	return mt32emu_analog_output_mode(SampleRateConverter::getBestAnalogOutputMode(target_samplerate));
+}
+
 mt32emu_context mt32emu_create_context(mt32emu_report_handler_i report_handler, void *instance_data) {
 	mt32emu_data *data = new mt32emu_data;
 	data->reportHandler = (report_handler.v0 != NULL) ? new DelegatingReportHandlerAdapter(report_handler, instance_data) : new ReportHandler;
@@ -337,11 +360,23 @@ mt32emu_context mt32emu_create_context(mt32emu_report_handler_i report_handler,
 	data->pcmROMImage = NULL;
 	data->partialCount = DEFAULT_MAX_PARTIALS;
 	data->analogOutputMode = AnalogOutputMode_COARSE;
+
+	data->srcState = new SamplerateConversionState;
+	data->srcState->outputSampleRate = 0.0;
+	data->srcState->srcQuality = SamplerateConversionQuality_GOOD;
+	data->srcState->src = NULL;
+
 	return data;
 }
 
 void mt32emu_free_context(mt32emu_context data) {
 	if (data == NULL) return;
+
+	delete data->srcState->src;
+	data->srcState->src = NULL;
+	delete data->srcState;
+	data->srcState = NULL;
+
 	if (data->controlROMImage != NULL) {
 		delete data->controlROMImage->getFile();
 		ROMImage::freeROMImage(data->controlROMImage);
@@ -414,18 +449,39 @@ void mt32emu_set_analog_output_mode(mt32emu_context context, const mt32emu_analo
 	context->analogOutputMode = static_cast<AnalogOutputMode>(analog_output_mode);
 }
 
+void mt32emu_set_stereo_output_samplerate(mt32emu_context context, const double samplerate) {
+	context->srcState->outputSampleRate = SampleRateConverter::getSupportedOutputSampleRate(samplerate);
+}
+
+void mt32emu_set_samplerate_conversion_quality(mt32emu_context context, const mt32emu_samplerate_conversion_quality quality) {
+	context->srcState->srcQuality = SamplerateConversionQuality(quality);
+}
+
+void mt32emu_select_renderer_type(mt32emu_context context, const mt32emu_renderer_type renderer_type) {
+	context->synth->selectRendererType(static_cast<RendererType>(renderer_type));
+}
+
+mt32emu_renderer_type mt32emu_get_selected_renderer_type(mt32emu_context context) {
+	return static_cast<mt32emu_renderer_type>(context->synth->getSelectedRendererType());
+}
+
 mt32emu_return_code mt32emu_open_synth(mt32emu_const_context context) {
 	if ((context->controlROMImage == NULL) || (context->pcmROMImage == NULL)) {
 		return MT32EMU_RC_MISSING_ROMS;
 	}
-	if (context->synth->open(*context->controlROMImage, *context->pcmROMImage, context->partialCount, context->analogOutputMode)) {
-		return MT32EMU_RC_OK;
+	if (!context->synth->open(*context->controlROMImage, *context->pcmROMImage, context->partialCount, context->analogOutputMode)) {
+		return MT32EMU_RC_FAILED;
 	}
-	return MT32EMU_RC_FAILED;
+	SamplerateConversionState &srcState = *context->srcState;
+	const double outputSampleRate = (0.0 < srcState.outputSampleRate) ? srcState.outputSampleRate : context->synth->getStereoOutputSampleRate();
+	srcState.src = new SampleRateConverter(*context->synth, outputSampleRate, srcState.srcQuality);
+	return MT32EMU_RC_OK;
 }
 
 void mt32emu_close_synth(mt32emu_const_context context) {
 	context->synth->close();
+	delete context->srcState->src;
+	context->srcState->src = NULL;
 }
 
 mt32emu_boolean mt32emu_is_open(mt32emu_const_context context) {
@@ -433,7 +489,24 @@ mt32emu_boolean mt32emu_is_open(mt32emu_const_context context) {
 }
 
 mt32emu_bit32u mt32emu_get_actual_stereo_output_samplerate(mt32emu_const_context context) {
-	return context->synth->getStereoOutputSampleRate();
+	if (context->srcState->src == NULL) {
+		return context->synth->getStereoOutputSampleRate();
+	}
+	return mt32emu_bit32u(0.5 + context->srcState->src->convertSynthToOutputTimestamp(SAMPLE_RATE));
+}
+
+mt32emu_bit32u mt32emu_convert_output_to_synth_timestamp(mt32emu_const_context context, mt32emu_bit32u output_timestamp) {
+	if (context->srcState->src == NULL) {
+		return output_timestamp;
+	}
+	return mt32emu_bit32u(0.5 + context->srcState->src->convertOutputToSynthTimestamp(output_timestamp));
+}
+
+mt32emu_bit32u mt32emu_convert_synth_to_output_timestamp(mt32emu_const_context context, mt32emu_bit32u synth_timestamp) {
+	if (context->srcState->src == NULL) {
+		return synth_timestamp;
+	}
+	return mt32emu_bit32u(0.5 + context->srcState->src->convertSynthToOutputTimestamp(synth_timestamp));
 }
 
 void mt32emu_flush_midi_queue(mt32emu_const_context context) {
@@ -449,6 +522,10 @@ void mt32emu_set_midi_receiver(mt32emu_context context, mt32emu_midi_receiver_i
 	context->midiParser = (midi_receiver.v0 != NULL) ? new DelegatingMidiStreamParser(context, midi_receiver, instance_data) : new DefaultMidiStreamParser(*context->synth);
 }
 
+mt32emu_bit32u mt32emu_get_internal_rendered_sample_count(mt32emu_const_context context) {
+	return context->synth->getInternalRenderedSampleCount();
+}
+
 void mt32emu_parse_stream(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length) {
 	context->midiParser->resetTimestamp();
 	context->midiParser->parseStream(stream, length);
@@ -573,12 +650,28 @@ mt32emu_boolean mt32emu_is_reversed_stereo_enabled(mt32emu_const_context context
 	return context->synth->isReversedStereoEnabled() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE;
 }
 
+void mt32emu_set_nice_amp_ramp_enabled(mt32emu_const_context context, const mt32emu_boolean enabled) {
+	context->synth->setNiceAmpRampEnabled(enabled != MT32EMU_BOOL_FALSE);
+}
+
+mt32emu_boolean mt32emu_is_nice_amp_ramp_enabled(mt32emu_const_context context) {
+	return context->synth->isNiceAmpRampEnabled() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE;
+}
+
 void mt32emu_render_bit16s(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len) {
-	context->synth->render(stream, len);
+	if (context->srcState->src != NULL) {
+		context->srcState->src->getOutputSamples(stream, len);
+	} else {
+		context->synth->render(stream, len);
+	}
 }
 
 void mt32emu_render_float(mt32emu_const_context context, float *stream, mt32emu_bit32u len) {
-	context->synth->render(stream, len);
+	if (context->srcState->src != NULL) {
+		context->srcState->src->getOutputSamples(stream, len);
+	} else {
+		context->synth->render(stream, len);
+	}
 }
 
 void mt32emu_render_bit16s_streams(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len) {
diff --git a/audio/softsynth/mt32/c_interface/c_interface.h b/audio/softsynth/mt32/c_interface/c_interface.h
index a2bdcb1..f736400 100644
--- a/audio/softsynth/mt32/c_interface/c_interface.h
+++ b/audio/softsynth/mt32/c_interface/c_interface.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -35,7 +35,7 @@ extern "C" {
 /* === Interface handling === */
 
 /** Returns mt32emu_service_i interface. */
-MT32EMU_EXPORT const mt32emu_service_i mt32emu_get_service_i();
+MT32EMU_EXPORT mt32emu_service_i mt32emu_get_service_i(void);
 
 #if MT32EMU_EXPORTS_TYPE == 2
 #undef MT32EMU_EXPORT
@@ -46,13 +46,13 @@ MT32EMU_EXPORT const mt32emu_service_i mt32emu_get_service_i();
  * Returns the version ID of mt32emu_report_handler_i interface the library has been compiled with.
  * This allows a client to fall-back gracefully instead of silently not receiving expected event reports.
  */
-MT32EMU_EXPORT mt32emu_report_handler_version mt32emu_get_supported_report_handler_version();
+MT32EMU_EXPORT mt32emu_report_handler_version mt32emu_get_supported_report_handler_version(void);
 
 /**
  * Returns the version ID of mt32emu_midi_receiver_version_i interface the library has been compiled with.
  * This allows a client to fall-back gracefully instead of silently not receiving expected MIDI messages.
  */
-MT32EMU_EXPORT mt32emu_midi_receiver_version mt32emu_get_supported_midi_receiver_version();
+MT32EMU_EXPORT mt32emu_midi_receiver_version mt32emu_get_supported_midi_receiver_version(void);
 
 /**
  * Returns library version as an integer in format: 0x00MMmmpp, where:
@@ -60,12 +60,12 @@ MT32EMU_EXPORT mt32emu_midi_receiver_version mt32emu_get_supported_midi_receiver
  * mm - minor version number
  * pp - patch number
  */
-MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_library_version_int();
+MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_library_version_int(void);
 
 /**
  * Returns library version as a C-string in format: "MAJOR.MINOR.PATCH".
  */
-MT32EMU_EXPORT const char *mt32emu_get_library_version_string();
+MT32EMU_EXPORT const char *mt32emu_get_library_version_string(void);
 
 /**
  * Returns output sample rate used in emulation of stereo analog circuitry of hardware units for particular analog_output_mode.
@@ -73,6 +73,13 @@ MT32EMU_EXPORT const char *mt32emu_get_library_version_string();
  */
 MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_stereo_output_samplerate(const mt32emu_analog_output_mode analog_output_mode);
 
+/**
+ * Returns the value of analog_output_mode for which the output signal may retain its full frequency spectrum
+ * at the sample rate specified by the target_samplerate argument.
+ * See comment for mt32emu_analog_output_mode.
+ */
+MT32EMU_EXPORT mt32emu_analog_output_mode mt32emu_get_best_analog_output_mode(const double target_samplerate);
+
 /* == Context-dependent functions == */
 
 /** Initialises a new emulation context and installs custom report handler if non-NULL. */
@@ -105,17 +112,50 @@ MT32EMU_EXPORT void mt32emu_get_rom_info(mt32emu_const_context context, mt32emu_
 
 /**
  * Allows to override the default maximum number of partials playing simultaneously within the emulation session.
- * This function doesn't immediately change the state of already opened synth. Newly set vale will take effect upon next call of mt32emu_open_synth().
+ * This function doesn't immediately change the state of already opened synth. Newly set value will take effect upon next call of mt32emu_open_synth().
  */
 MT32EMU_EXPORT void mt32emu_set_partial_count(mt32emu_context context, const mt32emu_bit32u partial_count);
 
 /**
  * Allows to override the default mode for emulation of analogue circuitry of the hardware units within the emulation session.
- * This function doesn't immediately change the state of already opened synth. Newly set vale will take effect upon next call of mt32emu_open_synth().
+ * This function doesn't immediately change the state of already opened synth. Newly set value will take effect upon next call of mt32emu_open_synth().
  */
 MT32EMU_EXPORT void mt32emu_set_analog_output_mode(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode);
 
 /**
+ * Allows to convert the synthesiser output to any desired sample rate. The samplerate conversion
+ * processes the completely mixed stereo output signal as it passes the analogue circuit emulation,
+ * so emulating the synthesiser output signal passing further through an ADC. When the samplerate
+ * argument is set to 0, the default output sample rate is used which depends on the current
+ * mode of analog circuitry emulation. See mt32emu_analog_output_mode.
+ * This function doesn't immediately change the state of already opened synth.
+ * Newly set value will take effect upon next call of mt32emu_open_synth().
+ */
+MT32EMU_EXPORT void mt32emu_set_stereo_output_samplerate(mt32emu_context context, const double samplerate);
+
+/**
+ * Several samplerate conversion quality options are provided which allow to trade-off the conversion speed vs.
+ * the retained passband width. All the options except FASTEST guarantee full suppression of the aliasing noise
+ * in terms of the 16-bit integer samples.
+ * This function doesn't immediately change the state of already opened synth.
+ * Newly set value will take effect upon next call of mt32emu_open_synth().
+ */
+MT32EMU_EXPORT void mt32emu_set_samplerate_conversion_quality(mt32emu_context context, const mt32emu_samplerate_conversion_quality quality);
+
+/**
+ * Selects new type of the wave generator and renderer to be used during subsequent calls to mt32emu_open_synth().
+ * By default, MT32EMU_RT_BIT16S is selected.
+ * See mt32emu_renderer_type for details.
+ */
+MT32EMU_EXPORT void mt32emu_select_renderer_type(mt32emu_context context, const mt32emu_renderer_type renderer_type);
+
+/**
+ * Returns previously selected type of the wave generator and renderer.
+ * See mt32emu_renderer_type for details.
+ */
+MT32EMU_EXPORT mt32emu_renderer_type mt32emu_get_selected_renderer_type(mt32emu_context context);
+
+/**
  * Prepares the emulation context to receive MIDI messages and produce output audio data using aforehand added set of ROMs,
  * and optionally set the maximum partial count and the analog output mode.
  * Returns MT32EMU_RC_OK upon success.
@@ -129,11 +169,28 @@ MT32EMU_EXPORT void mt32emu_close_synth(mt32emu_const_context context);
 MT32EMU_EXPORT mt32emu_boolean mt32emu_is_open(mt32emu_const_context context);
 
 /**
- * Returns actual output sample rate used in emulation of stereo analog circuitry of hardware units.
- * See comment for mt32emu_analog_output_mode.
+ * Returns actual sample rate of the fully processed output stereo signal.
+ * If samplerate conversion is used (i.e. when mt32emu_set_stereo_output_samplerate() has been invoked with a non-zero value),
+ * the returned value is the desired output samplerate rounded down to the closest integer.
+ * Otherwise, the output samplerate is choosen depending on the emulation mode of stereo analog circuitry of hardware units.
+ * See comment for mt32emu_analog_output_mode for more info.
  */
 MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_actual_stereo_output_samplerate(mt32emu_const_context context);
 
+/**
+ * Returns the number of samples produced at the internal synth sample rate (32000 Hz)
+ * that correspond to the given number of samples at the output sample rate.
+ * Intended to facilitate audio time synchronisation.
+ */
+MT32EMU_EXPORT mt32emu_bit32u mt32emu_convert_output_to_synth_timestamp(mt32emu_const_context context, mt32emu_bit32u output_timestamp);
+
+/**
+ * Returns the number of samples produced at the output sample rate
+ * that correspond to the given number of samples at the internal synth sample rate (32000 Hz).
+ * Intended to facilitate audio time synchronisation.
+ */
+MT32EMU_EXPORT mt32emu_bit32u mt32emu_convert_synth_to_output_timestamp(mt32emu_const_context context, mt32emu_bit32u synth_timestamp);
+
 /** All the enqueued events are processed by the synth immediately. */
 MT32EMU_EXPORT void mt32emu_flush_midi_queue(mt32emu_const_context context);
 
@@ -152,6 +209,12 @@ MT32EMU_EXPORT mt32emu_bit32u mt32emu_set_midi_event_queue_size(mt32emu_const_co
  */
 MT32EMU_EXPORT void mt32emu_set_midi_receiver(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data);
 
+/**
+ * Returns current value of the global counter of samples rendered since the synth was created (at the native sample rate 32000 Hz).
+ * This method helps to compute accurate timestamp of a MIDI message to use with the methods below.
+ */
+MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_internal_rendered_sample_count(mt32emu_const_context context);
+
 /* Enqueues a MIDI event for subsequent playback.
  * 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).
@@ -267,7 +330,6 @@ MT32EMU_EXPORT mt32emu_midi_delay_mode mt32emu_get_midi_delay_mode(mt32emu_const
  * 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 mt32emu_set_reverb_output_gain()
  * it offers to the user a capability to control the gain of reverb and non-reverb output channels independently.
- * Ignored in MT32EMU_DAC_PURE mode.
  */
 MT32EMU_EXPORT void mt32emu_set_output_gain(mt32emu_const_context context, float gain);
 /** Returns current output gain factor for synth output channels. */
@@ -282,7 +344,6 @@ MT32EMU_EXPORT float mt32emu_get_output_gain(mt32emu_const_context context);
  * 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
  * of that for LA32 analogue output. This factor is applied to the reverb output gain.
- * Ignored in MT32EMU_DAC_PURE mode.
  */
 MT32EMU_EXPORT void mt32emu_set_reverb_output_gain(mt32emu_const_context context, float gain);
 /** Returns current output gain factor for reverb wet output channels. */
@@ -294,10 +355,21 @@ MT32EMU_EXPORT void mt32emu_set_reversed_stereo_enabled(mt32emu_const_context co
 MT32EMU_EXPORT mt32emu_boolean mt32emu_is_reversed_stereo_enabled(mt32emu_const_context context);
 
 /**
- * Renders samples to the specified output stream as if they were sampled at the analog stereo output.
- * When mt32emu_analog_output_mode is set to ACCURATE (OVERSAMPLED), the output signal is upsampled to 48 (96) kHz in order
- * to retain emulation accuracy in whole audible frequency spectra. Otherwise, native digital signal sample rate is retained.
- * mt32emu_get_actual_stereo_output_samplerate() can be used to query actual sample rate of the output signal.
+ * Allows to toggle the NiceAmpRamp mode.
+ * In this mode, we want to ensure that amp ramp never jumps to the target
+ * value and always gradually increases or decreases. It seems that real units
+ * do not bother to always check if a newly started ramp leads to a jump.
+ * We also prefer the quality improvement over the emulation accuracy,
+ * so this mode is enabled by default.
+ */
+MT32EMU_EXPORT void mt32emu_set_nice_amp_ramp_enabled(mt32emu_const_context context, const mt32emu_boolean enabled);
+/** Returns whether NiceAmpRamp mode is enabled. */
+MT32EMU_EXPORT mt32emu_boolean mt32emu_is_nice_amp_ramp_enabled(mt32emu_const_context context);
+
+/**
+ * Renders samples to the specified output stream as if they were sampled at the analog stereo output at the desired sample rate.
+ * If the output sample rate is not specified explicitly, the default output sample rate is used which depends on the current
+ * mode of analog circuitry emulation. See mt32emu_analog_output_mode.
  * The length is in frames, not bytes (in 16-bit stereo, one frame is 4 bytes). Uses NATIVE byte ordering.
  */
 MT32EMU_EXPORT void mt32emu_render_bit16s(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len);
diff --git a/audio/softsynth/mt32/c_interface/c_types.h b/audio/softsynth/mt32/c_interface/c_types.h
index 3cd8744..dada610 100644
--- a/audio/softsynth/mt32/c_interface/c_types.h
+++ b/audio/softsynth/mt32/c_interface/c_types.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -68,6 +68,8 @@ typedef enum mt32emu_analog_output_mode mt32emu_analog_output_mode;
 typedef enum mt32emu_dac_input_mode mt32emu_dac_input_mode;
 typedef enum mt32emu_midi_delay_mode mt32emu_midi_delay_mode;
 typedef enum mt32emu_partial_state mt32emu_partial_state;
+typedef enum mt32emu_samplerate_conversion_quality mt32emu_samplerate_conversion_quality;
+typedef enum mt32emu_renderer_type mt32emu_renderer_type;
 #endif
 
 /** Contains identifiers and descriptions of ROM files being used. */
@@ -117,7 +119,9 @@ typedef enum {
 /** Synth interface versions */
 typedef enum {
 	MT32EMU_SERVICE_VERSION_0 = 0,
-	MT32EMU_SERVICE_VERSION_CURRENT = MT32EMU_SERVICE_VERSION_0
+	MT32EMU_SERVICE_VERSION_1 = 1,
+	MT32EMU_SERVICE_VERSION_2 = 2,
+	MT32EMU_SERVICE_VERSION_CURRENT = MT32EMU_SERVICE_VERSION_2
 } mt32emu_service_version;
 
 /* === Report Handler Interface === */
@@ -164,7 +168,7 @@ typedef struct {
 /**
  * Extensible interface for handling reported events.
  * Union intended to view an interface of any subsequent version as any parent interface not requiring a cast.
- * Elements are to be addressed using the tag of the interface version when they were introduced.
+ * It is caller's responsibility to check the actual interface version in runtime using the getVersionID() method.
  */
 union mt32emu_report_handler_i {
 	const mt32emu_report_handler_i_v0 *v0;
@@ -192,7 +196,7 @@ typedef struct {
 /**
  * Extensible interface for receiving MIDI messages.
  * Union intended to view an interface of any subsequent version as any parent interface not requiring a cast.
- * Elements are to be addressed using the tag of the interface version when they were introduced.
+ * It is caller's responsibility to check the actual interface version in runtime using the getVersionID() method.
  */
 union mt32emu_midi_receiver_i {
 	const mt32emu_midi_receiver_i_v0 *v0;
@@ -209,90 +213,124 @@ typedef union mt32emu_service_i mt32emu_service_i;
  * to bind to mt32emu_get_service_i() function instead of binding to each function it needs to use.
  * See c_interface.h for parameter description.
  */
-typedef struct {
-	/** Returns the actual interface version ID */
-	mt32emu_service_version (*getVersionID)(mt32emu_service_i i);
-	mt32emu_report_handler_version (*getSupportedReportHandlerVersionID)();
-	mt32emu_midi_receiver_version (*getSupportedMIDIReceiverVersionID)();
-
-	mt32emu_bit32u (*getLibraryVersionInt)();
-	const char *(*getLibraryVersionString)();
-
-	mt32emu_bit32u (*getStereoOutputSamplerate)(const mt32emu_analog_output_mode analog_output_mode);
-
-	mt32emu_context (*createContext)(mt32emu_report_handler_i report_handler, void *instance_data);
-	void (*freeContext)(mt32emu_context context);
-	mt32emu_return_code (*addROMData)(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest);
-	mt32emu_return_code (*addROMFile)(mt32emu_context context, const char *filename);
-	void (*getROMInfo)(mt32emu_const_context context, mt32emu_rom_info *rom_info);
-	void (*setPartialCount)(mt32emu_context context, const mt32emu_bit32u partial_count);
-	void (*setAnalogOutputMode)(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode);
-	mt32emu_return_code (*openSynth)(mt32emu_const_context context);
-	void (*closeSynth)(mt32emu_const_context context);
-	mt32emu_boolean (*isOpen)(mt32emu_const_context context);
-	mt32emu_bit32u (*getActualStereoOutputSamplerate)(mt32emu_const_context context);
-	void (*flushMIDIQueue)(mt32emu_const_context context);
-	mt32emu_bit32u (*setMIDIEventQueueSize)(mt32emu_const_context context, const mt32emu_bit32u queue_size);
-	void (*setMIDIReceiver)(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data);
-
-	void (*parseStream)(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length);
-	void (*parseStream_At)(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp);
-	void (*playShortMessage)(mt32emu_const_context context, mt32emu_bit32u message);
-	void (*playShortMessageAt)(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp);
-	mt32emu_return_code (*playMsg)(mt32emu_const_context context, mt32emu_bit32u msg);
-	mt32emu_return_code (*playSysex)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len);
-	mt32emu_return_code (*playMsgAt)(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp);
-	mt32emu_return_code (*playSysexAt)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp);
-
-	void (*playMsgNow)(mt32emu_const_context context, mt32emu_bit32u msg);
-	void (*playMsgOnPart)(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity);
-	void (*playSysexNow)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len);
-	void (*writeSysex)(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len);
-
-	void (*setReverbEnabled)(mt32emu_const_context context, const mt32emu_boolean reverb_enabled);
-	mt32emu_boolean (*isReverbEnabled)(mt32emu_const_context context);
-	void (*setReverbOverridden)(mt32emu_const_context context, const mt32emu_boolean reverb_overridden);
-	mt32emu_boolean (*isReverbOverridden)(mt32emu_const_context context);
-	void (*setReverbCompatibilityMode)(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode);
-	mt32emu_boolean (*isMT32ReverbCompatibilityMode)(mt32emu_const_context context);
-	mt32emu_boolean (*isDefaultReverbMT32Compatible)(mt32emu_const_context context);
-
-	void (*setDACInputMode)(mt32emu_const_context context, const mt32emu_dac_input_mode mode);
-	mt32emu_dac_input_mode (*getDACInputMode)(mt32emu_const_context context);
-
-	void (*setMIDIDelayMode)(mt32emu_const_context context, const mt32emu_midi_delay_mode mode);
-	mt32emu_midi_delay_mode (*getMIDIDelayMode)(mt32emu_const_context context);
-
-	void (*setOutputGain)(mt32emu_const_context context, float gain);
-	float (*getOutputGain)(mt32emu_const_context context);
-	void (*setReverbOutputGain)(mt32emu_const_context context, float gain);
-	float (*getReverbOutputGain)(mt32emu_const_context context);
-
-	void (*setReversedStereoEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled);
-	mt32emu_boolean (*isReversedStereoEnabled)(mt32emu_const_context context);
-
-	void (*renderBit16s)(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len);
-	void (*renderFloat)(mt32emu_const_context context, float *stream, mt32emu_bit32u len);
-	void (*renderBit16sStreams)(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len);
-	void (*renderFloatStreams)(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len);
-
-	mt32emu_boolean (*hasActivePartials)(mt32emu_const_context context);
-	mt32emu_boolean (*isActive)(mt32emu_const_context context);
-	mt32emu_bit32u (*getPartialCount)(mt32emu_const_context context);
-	mt32emu_bit32u (*getPartStates)(mt32emu_const_context context);
-	void (*getPartialStates)(mt32emu_const_context context, mt32emu_bit8u *partial_states);
-	mt32emu_bit32u (*getPlayingNotes)(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities);
-	const char *(*getPatchName)(mt32emu_const_context context, mt32emu_bit8u part_number);
+#define MT32EMU_SERVICE_I_V0 \
+	/** Returns the actual interface version ID */ \
+	mt32emu_service_version (*getVersionID)(mt32emu_service_i i); \
+	mt32emu_report_handler_version (*getSupportedReportHandlerVersionID)(void); \
+	mt32emu_midi_receiver_version (*getSupportedMIDIReceiverVersionID)(void); \
+\
+	mt32emu_bit32u (*getLibraryVersionInt)(void); \
+	const char *(*getLibraryVersionString)(void); \
+\
+	mt32emu_bit32u (*getStereoOutputSamplerate)(const mt32emu_analog_output_mode analog_output_mode); \
+\
+	mt32emu_context (*createContext)(mt32emu_report_handler_i report_handler, void *instance_data); \
+	void (*freeContext)(mt32emu_context context); \
+	mt32emu_return_code (*addROMData)(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest); \
+	mt32emu_return_code (*addROMFile)(mt32emu_context context, const char *filename); \
+	void (*getROMInfo)(mt32emu_const_context context, mt32emu_rom_info *rom_info); \
+	void (*setPartialCount)(mt32emu_context context, const mt32emu_bit32u partial_count); \
+	void (*setAnalogOutputMode)(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode); \
+	mt32emu_return_code (*openSynth)(mt32emu_const_context context); \
+	void (*closeSynth)(mt32emu_const_context context); \
+	mt32emu_boolean (*isOpen)(mt32emu_const_context context); \
+	mt32emu_bit32u (*getActualStereoOutputSamplerate)(mt32emu_const_context context); \
+	void (*flushMIDIQueue)(mt32emu_const_context context); \
+	mt32emu_bit32u (*setMIDIEventQueueSize)(mt32emu_const_context context, const mt32emu_bit32u queue_size); \
+	void (*setMIDIReceiver)(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data); \
+\
+	void (*parseStream)(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length); \
+	void (*parseStream_At)(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp); \
+	void (*playShortMessage)(mt32emu_const_context context, mt32emu_bit32u message); \
+	void (*playShortMessageAt)(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp); \
+	mt32emu_return_code (*playMsg)(mt32emu_const_context context, mt32emu_bit32u msg); \
+	mt32emu_return_code (*playSysex)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \
+	mt32emu_return_code (*playMsgAt)(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp); \
+	mt32emu_return_code (*playSysexAt)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp); \
+\
+	void (*playMsgNow)(mt32emu_const_context context, mt32emu_bit32u msg); \
+	void (*playMsgOnPart)(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity); \
+	void (*playSysexNow)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \
+	void (*writeSysex)(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \
+\
+	void (*setReverbEnabled)(mt32emu_const_context context, const mt32emu_boolean reverb_enabled); \
+	mt32emu_boolean (*isReverbEnabled)(mt32emu_const_context context); \
+	void (*setReverbOverridden)(mt32emu_const_context context, const mt32emu_boolean reverb_overridden); \
+	mt32emu_boolean (*isReverbOverridden)(mt32emu_const_context context); \
+	void (*setReverbCompatibilityMode)(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode); \
+	mt32emu_boolean (*isMT32ReverbCompatibilityMode)(mt32emu_const_context context); \
+	mt32emu_boolean (*isDefaultReverbMT32Compatible)(mt32emu_const_context context); \
+\
+	void (*setDACInputMode)(mt32emu_const_context context, const mt32emu_dac_input_mode mode); \
+	mt32emu_dac_input_mode (*getDACInputMode)(mt32emu_const_context context); \
+\
+	void (*setMIDIDelayMode)(mt32emu_const_context context, const mt32emu_midi_delay_mode mode); \
+	mt32emu_midi_delay_mode (*getMIDIDelayMode)(mt32emu_const_context context); \
+\
+	void (*setOutputGain)(mt32emu_const_context context, float gain); \
+	float (*getOutputGain)(mt32emu_const_context context); \
+	void (*setReverbOutputGain)(mt32emu_const_context context, float gain); \
+	float (*getReverbOutputGain)(mt32emu_const_context context); \
+\
+	void (*setReversedStereoEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \
+	mt32emu_boolean (*isReversedStereoEnabled)(mt32emu_const_context context); \
+\
+	void (*renderBit16s)(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len); \
+	void (*renderFloat)(mt32emu_const_context context, float *stream, mt32emu_bit32u len); \
+	void (*renderBit16sStreams)(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len); \
+	void (*renderFloatStreams)(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len); \
+\
+	mt32emu_boolean (*hasActivePartials)(mt32emu_const_context context); \
+	mt32emu_boolean (*isActive)(mt32emu_const_context context); \
+	mt32emu_bit32u (*getPartialCount)(mt32emu_const_context context); \
+	mt32emu_bit32u (*getPartStates)(mt32emu_const_context context); \
+	void (*getPartialStates)(mt32emu_const_context context, mt32emu_bit8u *partial_states); \
+	mt32emu_bit32u (*getPlayingNotes)(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities); \
+	const char *(*getPatchName)(mt32emu_const_context context, mt32emu_bit8u part_number); \
 	void (*readMemory)(mt32emu_const_context context, mt32emu_bit32u addr, mt32emu_bit32u len, mt32emu_bit8u *data);
+
+#define MT32EMU_SERVICE_I_V1 \
+	mt32emu_analog_output_mode (*getBestAnalogOutputMode)(const double target_samplerate); \
+	void (*setStereoOutputSampleRate)(mt32emu_context context, const double samplerate); \
+	void (*setSamplerateConversionQuality)(mt32emu_context context, const mt32emu_samplerate_conversion_quality quality); \
+	void (*selectRendererType)(mt32emu_context context, mt32emu_renderer_type renderer_type); \
+	mt32emu_renderer_type (*getSelectedRendererType)(mt32emu_context context); \
+	mt32emu_bit32u (*convertOutputToSynthTimestamp)(mt32emu_const_context context, mt32emu_bit32u output_timestamp); \
+	mt32emu_bit32u (*convertSynthToOutputTimestamp)(mt32emu_const_context context, mt32emu_bit32u synth_timestamp);
+
+#define MT32EMU_SERVICE_I_V2 \
+	mt32emu_bit32u (*getInternalRenderedSampleCount)(mt32emu_const_context context); \
+	void (*setNiceAmpRampEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \
+	mt32emu_boolean (*isNiceAmpRampEnabled)(mt32emu_const_context context);
+
+typedef struct {
+	MT32EMU_SERVICE_I_V0
 } mt32emu_service_i_v0;
 
+typedef struct {
+	MT32EMU_SERVICE_I_V0
+	MT32EMU_SERVICE_I_V1
+} mt32emu_service_i_v1;
+
+typedef struct {
+	MT32EMU_SERVICE_I_V0
+	MT32EMU_SERVICE_I_V1
+	MT32EMU_SERVICE_I_V2
+} mt32emu_service_i_v2;
+
 /**
  * Extensible interface for all the library services.
  * Union intended to view an interface of any subsequent version as any parent interface not requiring a cast.
- * Elements are to be addressed using the tag of the interface version when they were introduced.
+ * It is caller's responsibility to check the actual interface version in runtime using the getVersionID() method.
  */
 union mt32emu_service_i {
 	const mt32emu_service_i_v0 *v0;
+	const mt32emu_service_i_v1 *v1;
+	const mt32emu_service_i_v2 *v2;
 };
 
+#undef MT32EMU_SERVICE_I_V0
+#undef MT32EMU_SERVICE_I_V1
+#undef MT32EMU_SERVICE_I_V2
+
 #endif /* #ifndef MT32EMU_C_TYPES_H */
diff --git a/audio/softsynth/mt32/c_interface/cpp_interface.h b/audio/softsynth/mt32/c_interface/cpp_interface.h
index 3e86322..3b02c03 100644
--- a/audio/softsynth/mt32/c_interface/cpp_interface.h
+++ b/audio/softsynth/mt32/c_interface/cpp_interface.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -28,11 +28,19 @@
 
 #if MT32EMU_API_TYPE == 2
 
+extern "C" {
+
+/** Returns mt32emu_service_i interface. */
+mt32emu_service_i mt32emu_get_service_i();
+
+}
+
 #define mt32emu_get_supported_report_handler_version i.v0->getSupportedReportHandlerVersionID
 #define mt32emu_get_supported_midi_receiver_version i.v0->getSupportedMIDIReceiverVersionID
 #define mt32emu_get_library_version_int i.v0->getLibraryVersionInt
 #define mt32emu_get_library_version_string i.v0->getLibraryVersionString
 #define mt32emu_get_stereo_output_samplerate i.v0->getStereoOutputSamplerate
+#define mt32emu_get_best_analog_output_mode iV1()->getBestAnalogOutputMode
 #define mt32emu_create_context i.v0->createContext
 #define mt32emu_free_context i.v0->freeContext
 #define mt32emu_add_rom_data i.v0->addROMData
@@ -40,13 +48,20 @@
 #define mt32emu_get_rom_info i.v0->getROMInfo
 #define mt32emu_set_partial_count i.v0->setPartialCount
 #define mt32emu_set_analog_output_mode i.v0->setAnalogOutputMode
+#define mt32emu_set_stereo_output_samplerate iV1()->setStereoOutputSampleRate
+#define mt32emu_set_samplerate_conversion_quality iV1()->setSamplerateConversionQuality
+#define mt32emu_select_renderer_type iV1()->selectRendererType
+#define mt32emu_get_selected_renderer_type iV1()->getSelectedRendererType
 #define mt32emu_open_synth i.v0->openSynth
 #define mt32emu_close_synth i.v0->closeSynth
 #define mt32emu_is_open i.v0->isOpen
 #define mt32emu_get_actual_stereo_output_samplerate i.v0->getActualStereoOutputSamplerate
+#define mt32emu_convert_output_to_synth_timestamp iV1()->convertOutputToSynthTimestamp
+#define mt32emu_convert_synth_to_output_timestamp iV1()->convertSynthToOutputTimestamp
 #define mt32emu_flush_midi_queue i.v0->flushMIDIQueue
 #define mt32emu_set_midi_event_queue_size i.v0->setMIDIEventQueueSize
 #define mt32emu_set_midi_receiver i.v0->setMIDIReceiver
+#define mt32emu_get_internal_rendered_sample_count iV2()->getInternalRenderedSampleCount
 #define mt32emu_parse_stream i.v0->parseStream
 #define mt32emu_parse_stream_at i.v0->parseStream_At
 #define mt32emu_play_short_message i.v0->playShortMessage
@@ -76,6 +91,8 @@
 #define mt32emu_get_reverb_output_gain i.v0->getReverbOutputGain
 #define mt32emu_set_reversed_stereo_enabled i.v0->setReversedStereoEnabled
 #define mt32emu_is_reversed_stereo_enabled i.v0->isReversedStereoEnabled
+#define mt32emu_set_nice_amp_ramp_enabled iV2()->setNiceAmpRampEnabled
+#define mt32emu_is_nice_amp_ramp_enabled iV2()->isNiceAmpRampEnabled
 #define mt32emu_render_bit16s i.v0->renderBit16s
 #define mt32emu_render_float i.v0->renderFloat
 #define mt32emu_render_bit16s_streams i.v0->renderBit16sStreams
@@ -171,6 +188,7 @@ public:
 	const char *getLibraryVersionString() { return mt32emu_get_library_version_string(); }
 
 	Bit32u getStereoOutputSamplerate(const AnalogOutputMode analog_output_mode) { return mt32emu_get_stereo_output_samplerate(static_cast<mt32emu_analog_output_mode>(analog_output_mode)); }
+	AnalogOutputMode getBestAnalogOutputMode(const double target_samplerate) { return static_cast<AnalogOutputMode>(mt32emu_get_best_analog_output_mode(target_samplerate)); }
 
 	// Context-dependent methods
 
@@ -183,15 +201,22 @@ public:
 	void getROMInfo(mt32emu_rom_info *rom_info) { mt32emu_get_rom_info(c, rom_info); }
 	void setPartialCount(const Bit32u partial_count) { mt32emu_set_partial_count(c, partial_count); }
 	void setAnalogOutputMode(const AnalogOutputMode analog_output_mode) { mt32emu_set_analog_output_mode(c, static_cast<mt32emu_analog_output_mode>(analog_output_mode)); }
+	void setStereoOutputSampleRate(const double samplerate) { mt32emu_set_stereo_output_samplerate(c, samplerate); }
+	void setSamplerateConversionQuality(const SamplerateConversionQuality quality) { mt32emu_set_samplerate_conversion_quality(c, static_cast<mt32emu_samplerate_conversion_quality>(quality)); }
+	void selectRendererType(const RendererType newRendererType) { mt32emu_select_renderer_type(c, static_cast<mt32emu_renderer_type>(newRendererType)); }
+	RendererType getSelectedRendererType() { return static_cast<RendererType>(mt32emu_get_selected_renderer_type(c)); }
 	mt32emu_return_code openSynth() { return mt32emu_open_synth(c); }
 	void closeSynth() { mt32emu_close_synth(c); }
 	bool isOpen() { return mt32emu_is_open(c) != MT32EMU_BOOL_FALSE; }
 	Bit32u getActualStereoOutputSamplerate() { return mt32emu_get_actual_stereo_output_samplerate(c); }
+	Bit32u convertOutputToSynthTimestamp(Bit32u output_timestamp) { return mt32emu_convert_output_to_synth_timestamp(c, output_timestamp); }
+	Bit32u convertSynthToOutputTimestamp(Bit32u synth_timestamp) { return mt32emu_convert_synth_to_output_timestamp(c, synth_timestamp); }
 	void flushMIDIQueue() { mt32emu_flush_midi_queue(c); }
 	Bit32u setMIDIEventQueueSize(const Bit32u queue_size) { return mt32emu_set_midi_event_queue_size(c, queue_size); }
 	void setMIDIReceiver(mt32emu_midi_receiver_i midi_receiver, void *instance_data) { mt32emu_set_midi_receiver(c, midi_receiver, instance_data); }
 	void setMIDIReceiver(IMidiReceiver &midi_receiver) { setMIDIReceiver(CppInterfaceImpl::getMidiReceiverThunk(), &midi_receiver); }
 
+	Bit32u getInternalRenderedSampleCount() { return mt32emu_get_internal_rendered_sample_count(c); }
 	void parseStream(const Bit8u *stream, Bit32u length) { mt32emu_parse_stream(c, stream, length); }
 	void parseStream_At(const Bit8u *stream, Bit32u length, Bit32u timestamp) { mt32emu_parse_stream_at(c, stream, length, timestamp); }
 	void playShortMessage(Bit32u message) { mt32emu_play_short_message(c, message); }
@@ -228,6 +253,9 @@ public:
 	void setReversedStereoEnabled(const bool enabled) { mt32emu_set_reversed_stereo_enabled(c, enabled ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); }
 	bool isReversedStereoEnabled() { return mt32emu_is_reversed_stereo_enabled(c) != MT32EMU_BOOL_FALSE; }
 
+	void setNiceAmpRampEnabled(const bool enabled) { mt32emu_set_nice_amp_ramp_enabled(c, enabled ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); }
+	bool isNiceAmpRampEnabled() { return mt32emu_is_nice_amp_ramp_enabled(c) != MT32EMU_BOOL_FALSE; }
+
 	void renderBit16s(Bit16s *stream, Bit32u len) { mt32emu_render_bit16s(c, stream, len); }
 	void renderFloat(float *stream, Bit32u len) { mt32emu_render_float(c, stream, len); }
 	void renderBit16sStreams(const mt32emu_dac_output_bit16s_streams *streams, Bit32u len) { mt32emu_render_bit16s_streams(c, streams, len); }
@@ -247,6 +275,11 @@ private:
 	const mt32emu_service_i i;
 #endif
 	mt32emu_context c;
+
+#if MT32EMU_API_TYPE == 2
+	const mt32emu_service_i_v1 *iV1() { return (getVersionID() < MT32EMU_SERVICE_VERSION_1) ? NULL : i.v1; }
+	const mt32emu_service_i_v2 *iV2() { return (getVersionID() < MT32EMU_SERVICE_VERSION_2) ? NULL : i.v2; }
+#endif
 };
 
 namespace CppInterfaceImpl {
@@ -256,59 +289,59 @@ static mt32emu_report_handler_version getReportHandlerVersionID(mt32emu_report_h
 }
 
 static void printDebug(void *instance_data, const char *fmt, va_list list) {
-	((IReportHandler *)instance_data)->printDebug(fmt, list);
+	static_cast<IReportHandler *>(instance_data)->printDebug(fmt, list);
 }
 
 static void onErrorControlROM(void *instance_data) {
-	((IReportHandler *)instance_data)->onErrorControlROM();
+	static_cast<IReportHandler *>(instance_data)->onErrorControlROM();
 }
 
 static void onErrorPCMROM(void *instance_data) {
-	((IReportHandler *)instance_data)->onErrorPCMROM();
+	static_cast<IReportHandler *>(instance_data)->onErrorPCMROM();
 }
 
 static void showLCDMessage(void *instance_data, const char *message) {
-	((IReportHandler *)instance_data)->showLCDMessage(message);
+	static_cast<IReportHandler *>(instance_data)->showLCDMessage(message);
 }
 
 static void onMIDIMessagePlayed(void *instance_data) {
-	((IReportHandler *)instance_data)->onMIDIMessagePlayed();
+	static_cast<IReportHandler *>(instance_data)->onMIDIMessagePlayed();
 }
 
 static mt32emu_boolean onMIDIQueueOverflow(void *instance_data) {
-	return ((IReportHandler *)instance_data)->onMIDIQueueOverflow() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE;
+	return static_cast<IReportHandler *>(instance_data)->onMIDIQueueOverflow() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE;
 }
 
 static void onMIDISystemRealtime(void *instance_data, mt32emu_bit8u system_realtime) {
-	((IReportHandler *)instance_data)->onMIDISystemRealtime(system_realtime);
+	static_cast<IReportHandler *>(instance_data)->onMIDISystemRealtime(system_realtime);
 }
 
 static void onDeviceReset(void *instance_data) {
-	((IReportHandler *)instance_data)->onDeviceReset();
+	static_cast<IReportHandler *>(instance_data)->onDeviceReset();
 }
 
 static void onDeviceReconfig(void *instance_data) {
-	((IReportHandler *)instance_data)->onDeviceReconfig();
+	static_cast<IReportHandler *>(instance_data)->onDeviceReconfig();
 }
 
 static void onNewReverbMode(void *instance_data, mt32emu_bit8u mode) {
-	((IReportHandler *)instance_data)->onNewReverbMode(mode);
+	static_cast<IReportHandler *>(instance_data)->onNewReverbMode(mode);
 }
 
 static void onNewReverbTime(void *instance_data, mt32emu_bit8u time) {
-	((IReportHandler *)instance_data)->onNewReverbTime(time);
+	static_cast<IReportHandler *>(instance_data)->onNewReverbTime(time);
 }
 
 static void onNewReverbLevel(void *instance_data, mt32emu_bit8u level) {
-	((IReportHandler *)instance_data)->onNewReverbLevel(level);
+	static_cast<IReportHandler *>(instance_data)->onNewReverbLevel(level);
 }
 
 static void onPolyStateChanged(void *instance_data, mt32emu_bit8u part_num) {
-	((IReportHandler *)instance_data)->onPolyStateChanged(part_num);
+	static_cast<IReportHandler *>(instance_data)->onPolyStateChanged(part_num);
 }
 
 static void onProgramChanged(void *instance_data, mt32emu_bit8u part_num, const char *sound_group_name, const char *patch_name) {
-	((IReportHandler *)instance_data)->onProgramChanged(part_num, sound_group_name, patch_name);
+	static_cast<IReportHandler *>(instance_data)->onProgramChanged(part_num, sound_group_name, patch_name);
 }
 
 static mt32emu_report_handler_i getReportHandlerThunk() {
@@ -340,15 +373,15 @@ static mt32emu_midi_receiver_version getMidiReceiverVersionID(mt32emu_midi_recei
 }
 
 static void handleShortMessage(void *instance_data, const mt32emu_bit32u message) {
-	((IMidiReceiver *)instance_data)->handleShortMessage(message);
+	static_cast<IMidiReceiver *>(instance_data)->handleShortMessage(message);
 }
 
 static void handleSysex(void *instance_data, const mt32emu_bit8u stream[], const mt32emu_bit32u length) {
-	((IMidiReceiver *)instance_data)->handleSysex(stream, length);
+	static_cast<IMidiReceiver *>(instance_data)->handleSysex(stream, length);
 }
 
 static void handleSystemRealtimeMessage(void *instance_data, const mt32emu_bit8u realtime) {
-	((IMidiReceiver *)instance_data)->handleSystemRealtimeMessage(realtime);
+	static_cast<IMidiReceiver *>(instance_data)->handleSystemRealtimeMessage(realtime);
 }
 
 static mt32emu_midi_receiver_i getMidiReceiverThunk() {
@@ -375,6 +408,7 @@ static mt32emu_midi_receiver_i getMidiReceiverThunk() {
 #undef mt32emu_get_library_version_int
 #undef mt32emu_get_library_version_string
 #undef mt32emu_get_stereo_output_samplerate
+#undef mt32emu_get_best_analog_output_mode
 #undef mt32emu_create_context
 #undef mt32emu_free_context
 #undef mt32emu_add_rom_data
@@ -382,13 +416,20 @@ static mt32emu_midi_receiver_i getMidiReceiverThunk() {
 #undef mt32emu_get_rom_info
 #undef mt32emu_set_partial_count
 #undef mt32emu_set_analog_output_mode
+#undef mt32emu_set_stereo_output_samplerate
+#undef mt32emu_set_samplerate_conversion_quality
+#undef mt32emu_select_renderer_type
+#undef mt32emu_get_selected_renderer_type
 #undef mt32emu_open_synth
 #undef mt32emu_close_synth
 #undef mt32emu_is_open
 #undef mt32emu_get_actual_stereo_output_samplerate
+#undef mt32emu_convert_output_to_synth_timestamp
+#undef mt32emu_convert_synth_to_output_timestamp
 #undef mt32emu_flush_midi_queue
 #undef mt32emu_set_midi_event_queue_size
 #undef mt32emu_set_midi_receiver
+#undef mt32emu_get_internal_rendered_sample_count
 #undef mt32emu_parse_stream
 #undef mt32emu_parse_stream_at
 #undef mt32emu_play_short_message
@@ -418,6 +459,8 @@ static mt32emu_midi_receiver_i getMidiReceiverThunk() {
 #undef mt32emu_get_reverb_output_gain
 #undef mt32emu_set_reversed_stereo_enabled
 #undef mt32emu_is_reversed_stereo_enabled
+#undef mt32emu_set_nice_amp_ramp_enabled
+#undef mt32emu_is_nice_amp_ramp_enabled
 #undef mt32emu_render_bit16s
 #undef mt32emu_render_float
 #undef mt32emu_render_bit16s_streams
diff --git a/audio/softsynth/mt32/config.h b/audio/softsynth/mt32/config.h
index af59f05..5ad650c 100644
--- a/audio/softsynth/mt32/config.h
+++ b/audio/softsynth/mt32/config.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -18,11 +18,21 @@
 #ifndef MT32EMU_CONFIG_H
 #define MT32EMU_CONFIG_H
 
-#define MT32EMU_VERSION      "2.0.3"
+#define MT32EMU_VERSION      "2.3.0"
 #define MT32EMU_VERSION_MAJOR 2
-#define MT32EMU_VERSION_MINOR 0
-#define MT32EMU_VERSION_PATCH 3
+#define MT32EMU_VERSION_MINOR 3
+#define MT32EMU_VERSION_PATCH 0
 
+/* Library Exports Configuration
+ *
+ * This reflects the API types actually provided by the library build.
+ * 0: The full-featured C++ API is only available in this build. The client application may ONLY use MT32EMU_API_TYPE 0.
+ * 1: The C-compatible API is only available. The library is built as a shared object, only C functions are exported,
+ *    and thus the client application may NOT use MT32EMU_API_TYPE 0.
+ * 2: The C-compatible API is only available. The library is built as a shared object, only the factory function
+ *    is exported, and thus the client application may ONLY use MT32EMU_API_TYPE 2.
+ * 3: All the available API types are provided by the library build.
+ */
 #define MT32EMU_EXPORTS_TYPE  3
 
 #endif
diff --git a/audio/softsynth/mt32/globals.h b/audio/softsynth/mt32/globals.h
index 49a5ecc..2d984c8 100644
--- a/audio/softsynth/mt32/globals.h
+++ b/audio/softsynth/mt32/globals.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -87,7 +87,7 @@
 #define MT32EMU_MAX_STREAM_BUFFER_SIZE 32768
 
 /* This should correspond to the MIDI buffer size used in real h/w devices.
- * CM-32L control ROM seems using 1000 bytes, old MT-32 isn't confirmed by now.
+ * CM-32L control ROM is using 1000 bytes, and MT-32 GEN0 is using only 240 bytes (semi-confirmed by now).
  */
 #define MT32EMU_SYSEX_BUFFER_SIZE 1000
 
diff --git a/audio/softsynth/mt32/internals.h b/audio/softsynth/mt32/internals.h
index c64ba39..0bae8d9 100644
--- a/audio/softsynth/mt32/internals.h
+++ b/audio/softsynth/mt32/internals.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -81,12 +81,6 @@
 
 // Configuration
 
-// 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.
-#ifndef MT32EMU_USE_FLOAT_SAMPLES
-#define MT32EMU_USE_FLOAT_SAMPLES 0
-#endif
-
 // 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.
 #ifndef MT32EMU_REDUCE_REVERB_MEMORY
@@ -101,6 +95,10 @@
 
 namespace MT32Emu {
 
+typedef Bit16s IntSample;
+typedef Bit32s IntSampleEx;
+typedef float FloatSample;
+
 enum PolyState {
 	POLY_Playing,
 	POLY_Held, // This marks keys that have been released on the keyboard, but are being held by the pedal
@@ -115,14 +113,6 @@ enum ReverbMode {
 	REVERB_MODE_TAP_DELAY
 };
 
-#if MT32EMU_USE_FLOAT_SAMPLES
-typedef float Sample;
-typedef float SampleEx;
-#else
-typedef Bit16s Sample;
-typedef Bit32s SampleEx;
-#endif
-
-}
+} // namespace MT32Emu
 
 #endif // #ifndef MT32EMU_INTERNALS_H
diff --git a/audio/softsynth/mt32/mmath.h b/audio/softsynth/mt32/mmath.h
index f233bed..9a9e642 100644
--- a/audio/softsynth/mt32/mmath.h
+++ b/audio/softsynth/mt32/mmath.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
diff --git a/audio/softsynth/mt32/module.mk b/audio/softsynth/mt32/module.mk
index 7657f5b..7c50f3e 100644
--- a/audio/softsynth/mt32/module.mk
+++ b/audio/softsynth/mt32/module.mk
@@ -5,6 +5,7 @@ MODULE_OBJS := \
 	BReverbModel.o \
 	File.o \
 	FileStream.o \
+	LA32FloatWaveGenerator.o \
 	LA32Ramp.o \
 	LA32WaveGenerator.o \
 	MidiStreamParser.o \
@@ -19,22 +20,8 @@ MODULE_OBJS := \
 	TVF.o \
 	TVP.o \
 	sha1/sha1.o \
-	c_interface/c_interface.o
-
-#	SampleRateConverter.o \
-#	srchelper/InternalResampler.o \
-#	srchelper/SamplerateAdapter.o \
-#	srchelper/SoxrAdapter.o \
-#	srchelper/srctools/src/FIRResampler.o \
-#	srchelper/srctools/src/IIR2xResampler.o \
-#	srchelper/srctools/src/LinearResampler.o \
-#	srchelper/srctools/src/ResamplerModel.o \
-#	srchelper/srctools/src/SincResampler.o
-# TODO: The Munt SampleRateConverter requires these additional -I options.
-# This is not a very nice way of doing that, though, as it adds them globally.
-# INCLUDES += -I $(srcdir)/$(MODULE)/srchelper/srctools/include
-# INCLUDES += -I $(srcdir)/$(MODULE)/
-
+	c_interface/c_interface.o \
+	SampleRateConverter.o
 
 # Include common rules
 include $(srcdir)/rules.mk
diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h
index 3f3b6af..6b93121 100644
--- a/audio/softsynth/mt32/mt32emu.h
+++ b/audio/softsynth/mt32/mt32emu.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2017 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
@@ -43,7 +43,7 @@
 #error Incompatible setting MT32EMU_API_TYPE=1
 #elif MT32EMU_API_TYPE == 2 && (MT32EMU_EXPORTS_TYPE == 0)
 #error Incompatible setting MT32EMU_API_TYPE=2
-#elif MT32EMU_API_TYPE == 3 && (MT32EMU_EXPORTS_TYPE == 0)
+#elif MT32EMU_API_TYPE == 3 && (MT32EMU_EXPORTS_TYPE == 0 || MT32EMU_EXPORTS_TYPE == 2)
 #error Incompatible setting MT32EMU_API_TYPE=3
 #endif
 #else /* #ifdef MT32EMU_API_TYPE */
diff --git a/audio/softsynth/mt32/srchelper/InternalResampler.cpp b/audio/softsynth/mt32/srchelper/InternalResampler.cpp
index f76c7d1..3204084 100644
--- a/audio/softsynth/mt32/srchelper/InternalResampler.cpp
+++ b/audio/softsynth/mt32/srchelper/InternalResampler.cpp
@@ -16,10 +16,10 @@
 
 #include "InternalResampler.h"
 
-#include <SincResampler.h>
-#include <ResamplerModel.h>
+#include "srctools/include/SincResampler.h"
+#include "srctools/include/ResamplerModel.h"
 
-#include "Synth.h"
+#include "../Synth.h"
 
 using namespace SRCTools;
 
@@ -37,11 +37,11 @@ public:
 	}
 };
 
-static FloatSampleProvider &createModel(Synth &synth, SRCTools::FloatSampleProvider &synthSource, double targetSampleRate, SampleRateConverter::Quality quality) {
+static FloatSampleProvider &createModel(Synth &synth, SRCTools::FloatSampleProvider &synthSource, double targetSampleRate, SamplerateConversionQuality quality) {
 	static const double MAX_AUDIBLE_FREQUENCY = 20000.0;
 
 	const double sourceSampleRate = synth.getStereoOutputSampleRate();
-	if (quality != SampleRateConverter::FASTEST) {
+	if (quality != SamplerateConversionQuality_FASTEST) {
 		const bool oversampledMode = synth.getStereoOutputSampleRate() == Synth::getStereoOutputSampleRate(AnalogOutputMode_OVERSAMPLED);
 		// Oversampled input allows to bypass IIR interpolation stage and, in some cases, IIR decimation stage
 		if (oversampledMode && (0.5 * sourceSampleRate) <= targetSampleRate) {
@@ -59,7 +59,7 @@ static FloatSampleProvider &createModel(Synth &synth, SRCTools::FloatSampleProvi
 
 using namespace MT32Emu;
 
-InternalResampler::InternalResampler(Synth &synth, double targetSampleRate, SampleRateConverter::Quality quality) :
+InternalResampler::InternalResampler(Synth &synth, double targetSampleRate, SamplerateConversionQuality quality) :
 	synthSource(*new SynthWrapper(synth)),
 	model(createModel(synth, synthSource, targetSampleRate, quality))
 {}
diff --git a/audio/softsynth/mt32/srchelper/InternalResampler.h b/audio/softsynth/mt32/srchelper/InternalResampler.h
index 0a5c323..be54077 100644
--- a/audio/softsynth/mt32/srchelper/InternalResampler.h
+++ b/audio/softsynth/mt32/srchelper/InternalResampler.h
@@ -14,12 +14,12 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef INTERNAL_RESAMPLER_H
-#define INTERNAL_RESAMPLER_H
+#ifndef MT32EMU_INTERNAL_RESAMPLER_H
+#define MT32EMU_INTERNAL_RESAMPLER_H
 
-#include "../SampleRateConverter.h"
+#include "../Enumerations.h"
 
-#include "FloatSampleProvider.h"
+#include "srctools/include/FloatSampleProvider.h"
 
 namespace MT32Emu {
 
@@ -27,7 +27,7 @@ class Synth;
 
 class InternalResampler {
 public:
-	InternalResampler(Synth &synth, double targetSampleRate, SampleRateConverter::Quality quality);
+	InternalResampler(Synth &synth, double targetSampleRate, SamplerateConversionQuality quality);
 	~InternalResampler();
 
 	void getOutputSamples(float *buffer, unsigned int length);
@@ -39,4 +39,4 @@ private:
 
 } // namespace MT32Emu
 
-#endif // INTERNAL_RESAMPLER_H
+#endif // MT32EMU_INTERNAL_RESAMPLER_H
diff --git a/audio/softsynth/mt32/srchelper/SamplerateAdapter.cpp b/audio/softsynth/mt32/srchelper/SamplerateAdapter.cpp
index 33fcfa8..715d298 100644
--- a/audio/softsynth/mt32/srchelper/SamplerateAdapter.cpp
+++ b/audio/softsynth/mt32/srchelper/SamplerateAdapter.cpp
@@ -16,7 +16,7 @@
 
 #include "SamplerateAdapter.h"
 
-#include "Synth.h"
+#include "../Synth.h"
 
 using namespace MT32Emu;
 
@@ -31,7 +31,7 @@ long SamplerateAdapter::getInputSamples(void *cb_data, float **data) {
 	return length;
 }
 
-SamplerateAdapter::SamplerateAdapter(Synth &useSynth, double targetSampleRate, SampleRateConverter::Quality quality) :
+SamplerateAdapter::SamplerateAdapter(Synth &useSynth, double targetSampleRate, SamplerateConversionQuality quality) :
 	synth(useSynth),
 	inBuffer(new float[CHANNEL_COUNT * MAX_SAMPLES_PER_RUN]),
 	inBufferSize(MAX_SAMPLES_PER_RUN),
@@ -41,16 +41,16 @@ SamplerateAdapter::SamplerateAdapter(Synth &useSynth, double targetSampleRate, S
 	int error;
 	int conversionType;
 	switch (quality) {
-	case SampleRateConverter::FASTEST:
+	case SamplerateConversionQuality_FASTEST:
 		conversionType = SRC_LINEAR;
 		break;
-	case SampleRateConverter::FAST:
+	case SamplerateConversionQuality_FAST:
 		conversionType = SRC_SINC_FASTEST;
 		break;
-	case SampleRateConverter::BEST:
+	case SamplerateConversionQuality_BEST:
 		conversionType = SRC_SINC_BEST_QUALITY;
 		break;
-	case SampleRateConverter::GOOD:
+	case SamplerateConversionQuality_GOOD:
 	default:
 		conversionType = SRC_SINC_MEDIUM_QUALITY;
 		break;
diff --git a/audio/softsynth/mt32/srchelper/SamplerateAdapter.h b/audio/softsynth/mt32/srchelper/SamplerateAdapter.h
index aac259b..0991fd7 100644
--- a/audio/softsynth/mt32/srchelper/SamplerateAdapter.h
+++ b/audio/softsynth/mt32/srchelper/SamplerateAdapter.h
@@ -14,18 +14,20 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef SAMPLERATE_ADAPTER_H
-#define SAMPLERATE_ADAPTER_H
+#ifndef MT32EMU_SAMPLERATE_ADAPTER_H
+#define MT32EMU_SAMPLERATE_ADAPTER_H
 
 #include <samplerate.h>
 
-#include "../SampleRateConverter.h"
+#include "../Enumerations.h"
 
 namespace MT32Emu {
 
+class Synth;
+
 class SamplerateAdapter {
 public:
-	SamplerateAdapter(Synth &synth, double targetSampleRate, SampleRateConverter::Quality quality);
+	SamplerateAdapter(Synth &synth, double targetSampleRate, SamplerateConversionQuality quality);
 	~SamplerateAdapter();
 
 	void getOutputSamples(float *outBuffer, unsigned int length);
@@ -43,4 +45,4 @@ private:
 
 } // namespace MT32Emu
 
-#endif // SAMPLERATE_ADAPTER_H
+#endif // MT32EMU_SAMPLERATE_ADAPTER_H
diff --git a/audio/softsynth/mt32/srchelper/SoxrAdapter.cpp b/audio/softsynth/mt32/srchelper/SoxrAdapter.cpp
index b13192b..5e8dca9 100644
--- a/audio/softsynth/mt32/srchelper/SoxrAdapter.cpp
+++ b/audio/softsynth/mt32/srchelper/SoxrAdapter.cpp
@@ -16,37 +16,37 @@
 
 #include "SoxrAdapter.h"
 
-#include "Synth.h"
+#include "../Synth.h"
 
 using namespace MT32Emu;
 
 static const unsigned int CHANNEL_COUNT = 2;
 
 size_t SoxrAdapter::getInputSamples(void *input_fn_state, soxr_in_t *data, size_t requested_len) {
-	unsigned int length = requested_len < 1 ? 1 : (MAX_SAMPLES_PER_RUN < requested_len ? MAX_SAMPLES_PER_RUN : requested_len);
+	unsigned int length = requested_len < 1 ? 1 : (MAX_SAMPLES_PER_RUN < requested_len ? MAX_SAMPLES_PER_RUN : static_cast<unsigned int>(requested_len));
 	SoxrAdapter *instance = static_cast<SoxrAdapter *>(input_fn_state);
 	instance->synth.render(instance->inBuffer, length);
 	*data = instance->inBuffer;
 	return length;
 }
 
-SoxrAdapter::SoxrAdapter(Synth &useSynth, double targetSampleRate, SampleRateConverter::Quality quality) :
+SoxrAdapter::SoxrAdapter(Synth &useSynth, double targetSampleRate, SamplerateConversionQuality quality) :
 	synth(useSynth),
 	inBuffer(new float[CHANNEL_COUNT * MAX_SAMPLES_PER_RUN])
 {
 	soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_FLOAT32_I, SOXR_FLOAT32_I);
 	unsigned long qualityRecipe;
 	switch (quality) {
-	case SampleRateConverter::FASTEST:
+	case SamplerateConversionQuality_FASTEST:
 		qualityRecipe = SOXR_QQ;
 		break;
-	case SampleRateConverter::FAST:
+	case SamplerateConversionQuality_FAST:
 		qualityRecipe = SOXR_LQ;
 		break;
-	case SampleRateConverter::GOOD:
+	case SamplerateConversionQuality_GOOD:
 		qualityRecipe = SOXR_MQ;
 		break;
-	case SampleRateConverter::BEST:
+	case SamplerateConversionQuality_BEST:
 	default:
 		qualityRecipe = SOXR_16_BITQ;
 		break;
diff --git a/audio/softsynth/mt32/srchelper/SoxrAdapter.h b/audio/softsynth/mt32/srchelper/SoxrAdapter.h
index c764d9a..b97ca4d 100644
--- a/audio/softsynth/mt32/srchelper/SoxrAdapter.h
+++ b/audio/softsynth/mt32/srchelper/SoxrAdapter.h
@@ -14,18 +14,20 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef SOXR_ADAPTER_H
-#define SOXR_ADAPTER_H
+#ifndef MT32EMU_SOXR_ADAPTER_H
+#define MT32EMU_SOXR_ADAPTER_H
 
 #include <soxr.h>
 
-#include "../SampleRateConverter.h"
+#include "../Enumerations.h"
 
 namespace MT32Emu {
 
+class Synth;
+
 class SoxrAdapter {
 public:
-	SoxrAdapter(Synth &synth, double targetSampleRate, SampleRateConverter::Quality quality);
+	SoxrAdapter(Synth &synth, double targetSampleRate, SamplerateConversionQuality quality);
 	~SoxrAdapter();
 
 	void getOutputSamples(float *buffer, unsigned int length);
@@ -40,4 +42,4 @@ private:
 
 } // namespace MT32Emu
 
-#endif // SOXR_ADAPTER_H
+#endif // MT32EMU_SOXR_ADAPTER_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/include/FIRResampler.h b/audio/softsynth/mt32/srchelper/srctools/include/FIRResampler.h
index 2c5d690..7c09bf8 100644
--- a/audio/softsynth/mt32/srchelper/srctools/include/FIRResampler.h
+++ b/audio/softsynth/mt32/srchelper/srctools/include/FIRResampler.h
@@ -14,8 +14,8 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef FIR_RESAMPLER_H
-#define FIR_RESAMPLER_H
+#ifndef SRCTOOLS_FIR_RESAMPLER_H
+#define SRCTOOLS_FIR_RESAMPLER_H
 
 #include "ResamplerStage.h"
 
@@ -64,4 +64,4 @@ private:
 
 } // namespace SRCTools
 
-#endif // FIR_RESAMPLER_H
+#endif // SRCTOOLS_FIR_RESAMPLER_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/include/FloatSampleProvider.h b/audio/softsynth/mt32/srchelper/srctools/include/FloatSampleProvider.h
index 03038d0..9820769 100644
--- a/audio/softsynth/mt32/srchelper/srctools/include/FloatSampleProvider.h
+++ b/audio/softsynth/mt32/srchelper/srctools/include/FloatSampleProvider.h
@@ -14,8 +14,8 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef FLOAT_SAMPLE_PROVIDER_H
-#define FLOAT_SAMPLE_PROVIDER_H
+#ifndef SRCTOOLS_FLOAT_SAMPLE_PROVIDER_H
+#define SRCTOOLS_FLOAT_SAMPLE_PROVIDER_H
 
 namespace SRCTools {
 
@@ -24,11 +24,11 @@ typedef float FloatSample;
 /** Interface defines an abstract source of samples. It can either define a single channel stream or a stream with interleaved channels. */
 class FloatSampleProvider {
 public:
-	virtual ~FloatSampleProvider() {};
+	virtual ~FloatSampleProvider() {}
 
 	virtual void getOutputSamples(FloatSample *outBuffer, unsigned int size) = 0;
 };
 
 } // namespace SRCTools
 
-#endif // FLOAT_SAMPLE_PROVIDER_H
+#endif // SRCTOOLS_FLOAT_SAMPLE_PROVIDER_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/include/IIR2xResampler.h b/audio/softsynth/mt32/srchelper/srctools/include/IIR2xResampler.h
index 23733e4..0bfe1c4 100644
--- a/audio/softsynth/mt32/srchelper/srctools/include/IIR2xResampler.h
+++ b/audio/softsynth/mt32/srchelper/srctools/include/IIR2xResampler.h
@@ -14,8 +14,8 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef IIR_2X_RESAMPLER_H
-#define IIR_2X_RESAMPLER_H
+#ifndef SRCTOOLS_IIR_2X_RESAMPLER_H
+#define SRCTOOLS_IIR_2X_RESAMPLER_H
 
 #include "ResamplerStage.h"
 
@@ -97,4 +97,4 @@ public:
 
 } // namespace SRCTools
 
-#endif // IIR_2X_RESAMPLER_H
+#endif // SRCTOOLS_IIR_2X_RESAMPLER_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/include/LinearResampler.h b/audio/softsynth/mt32/srchelper/srctools/include/LinearResampler.h
index 1f4dd2f..c81ff2a 100644
--- a/audio/softsynth/mt32/srchelper/srctools/include/LinearResampler.h
+++ b/audio/softsynth/mt32/srchelper/srctools/include/LinearResampler.h
@@ -14,8 +14,8 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef LINEAR_RESAMPLER_H
-#define LINEAR_RESAMPLER_H
+#ifndef SRCTOOLS_LINEAR_RESAMPLER_H
+#define SRCTOOLS_LINEAR_RESAMPLER_H
 
 #include "ResamplerStage.h"
 
@@ -39,4 +39,4 @@ private:
 
 } // namespace SRCTools
 
-#endif // LINEAR_RESAMPLER_H
+#endif // SRCTOOLS_LINEAR_RESAMPLER_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/include/ResamplerModel.h b/audio/softsynth/mt32/srchelper/srctools/include/ResamplerModel.h
index 0372605..f0ac237 100644
--- a/audio/softsynth/mt32/srchelper/srctools/include/ResamplerModel.h
+++ b/audio/softsynth/mt32/srchelper/srctools/include/ResamplerModel.h
@@ -14,8 +14,8 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef RESAMPLER_MODEL_H
-#define RESAMPLER_MODEL_H
+#ifndef SRCTOOLS_RESAMPLER_MODEL_H
+#define SRCTOOLS_RESAMPLER_MODEL_H
 
 #include "FloatSampleProvider.h"
 
@@ -60,4 +60,4 @@ void freeResamplerModel(FloatSampleProvider &model, FloatSampleProvider &source)
 
 } // namespace SRCTools
 
-#endif // RESAMPLER_MODEL_H
+#endif // SRCTOOLS_RESAMPLER_MODEL_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/include/ResamplerStage.h b/audio/softsynth/mt32/srchelper/srctools/include/ResamplerStage.h
index c0f0a0a..e335c0c 100644
--- a/audio/softsynth/mt32/srchelper/srctools/include/ResamplerStage.h
+++ b/audio/softsynth/mt32/srchelper/srctools/include/ResamplerStage.h
@@ -14,8 +14,8 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef RESAMPLER_STAGE_H
-#define RESAMPLER_STAGE_H
+#ifndef SRCTOOLS_RESAMPLER_STAGE_H
+#define SRCTOOLS_RESAMPLER_STAGE_H
 
 #include "FloatSampleProvider.h"
 
@@ -24,7 +24,7 @@ namespace SRCTools {
 /** Interface defines an abstract source of samples. It can either define a single channel stream or a stream with interleaved channels. */
 class ResamplerStage {
 public:
-	virtual ~ResamplerStage() {};
+	virtual ~ResamplerStage() {}
 
 	/** Returns a lower estimation of required number of input samples to produce the specified number of output samples. */
 	virtual unsigned int estimateInLength(const unsigned int outLength) const = 0;
@@ -35,4 +35,4 @@ public:
 
 } // namespace SRCTools
 
-#endif // RESAMPLER_STAGE_H
+#endif // SRCTOOLS_RESAMPLER_STAGE_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/include/SincResampler.h b/audio/softsynth/mt32/srchelper/srctools/include/SincResampler.h
index ea3f03b..1551a1e 100644
--- a/audio/softsynth/mt32/srchelper/srctools/include/SincResampler.h
+++ b/audio/softsynth/mt32/srchelper/srctools/include/SincResampler.h
@@ -14,8 +14,8 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef SINC_RESAMPLER_H
-#define SINC_RESAMPLER_H
+#ifndef SRCTOOLS_SINC_RESAMPLER_H
+#define SRCTOOLS_SINC_RESAMPLER_H
 
 #include "FIRResampler.h"
 
@@ -43,4 +43,4 @@ namespace SincResampler {
 
 } // namespace SRCTools
 
-#endif // SINC_RESAMPLER_H
+#endif // SRCTOOLS_SINC_RESAMPLER_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/src/FIRResampler.cpp b/audio/softsynth/mt32/srchelper/srctools/src/FIRResampler.cpp
index 2cded0c..15d95c5 100644
--- a/audio/softsynth/mt32/srchelper/srctools/src/FIRResampler.cpp
+++ b/audio/softsynth/mt32/srchelper/srctools/src/FIRResampler.cpp
@@ -17,7 +17,7 @@
 #include <cmath>
 #include <cstring>
 
-#include "FIRResampler.h"
+#include "../include/FIRResampler.h"
 
 using namespace SRCTools;
 
diff --git a/audio/softsynth/mt32/srchelper/srctools/src/IIR2xResampler.cpp b/audio/softsynth/mt32/srchelper/srctools/src/IIR2xResampler.cpp
index 061006a..1adc593 100644
--- a/audio/softsynth/mt32/srchelper/srctools/src/IIR2xResampler.cpp
+++ b/audio/softsynth/mt32/srchelper/srctools/src/IIR2xResampler.cpp
@@ -16,12 +16,12 @@
 
 #include <cstddef>
 
-#include "IIR2xResampler.h"
+#include "../include/IIR2xResampler.h"
 
 namespace SRCTools {
 
 	// Avoid denormals degrading performance, using biased input
-	static const BufferedSample BIAS = 1e-35f;
+	static const BufferedSample BIAS = 1e-20f;
 
 	// Sharp elliptic filter with symmetric ripple: N=18, Ap=As=-106 dB, fp=0.238, fs = 0.25 (in terms of sample rate)
 	static const IIRCoefficient FIR_BEST = 0.0014313792470984f;
@@ -132,7 +132,7 @@ IIRResampler::~IIRResampler() {
 
 IIR2xInterpolator::IIR2xInterpolator(const Quality quality) :
 	IIRResampler(quality),
-	phase()
+	phase(1)
 {
 	for (unsigned int chIx = 0; chIx < IIR_RESAMPER_CHANNEL_COUNT; ++chIx) {
 		lastInputSamples[chIx] = 0;
@@ -141,7 +141,7 @@ IIR2xInterpolator::IIR2xInterpolator(const Quality quality) :
 
 IIR2xInterpolator::IIR2xInterpolator(const unsigned int useSectionsCount, const IIRCoefficient useFIR, const IIRSection useSections[]) :
 	IIRResampler(useSectionsCount, useFIR, useSections),
-	phase()
+	phase(1)
 {
 	for (unsigned int chIx = 0; chIx < IIR_RESAMPER_CHANNEL_COUNT; ++chIx) {
 		lastInputSamples[chIx] = 0;
diff --git a/audio/softsynth/mt32/srchelper/srctools/src/LinearResampler.cpp b/audio/softsynth/mt32/srchelper/srctools/src/LinearResampler.cpp
index 98b9c77..e7b60c6 100644
--- a/audio/softsynth/mt32/srchelper/srctools/src/LinearResampler.cpp
+++ b/audio/softsynth/mt32/srchelper/srctools/src/LinearResampler.cpp
@@ -14,7 +14,7 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "LinearResampler.h"
+#include "../include/LinearResampler.h"
 
 using namespace SRCTools;
 
diff --git a/audio/softsynth/mt32/srchelper/srctools/src/ResamplerModel.cpp b/audio/softsynth/mt32/srchelper/srctools/src/ResamplerModel.cpp
index 4d2d930..44b969c 100644
--- a/audio/softsynth/mt32/srchelper/srctools/src/ResamplerModel.cpp
+++ b/audio/softsynth/mt32/srchelper/srctools/src/ResamplerModel.cpp
@@ -17,12 +17,12 @@
 #include <cmath>
 #include <cstddef>
 
-#include "ResamplerModel.h"
+#include "../include/ResamplerModel.h"
 
-#include "ResamplerStage.h"
-#include "SincResampler.h"
-#include "IIR2xResampler.h"
-#include "LinearResampler.h"
+#include "../include/ResamplerStage.h"
+#include "../include/SincResampler.h"
+#include "../include/IIR2xResampler.h"
+#include "../include/LinearResampler.h"
 
 namespace SRCTools {
 
diff --git a/audio/softsynth/mt32/srchelper/srctools/src/SincResampler.cpp b/audio/softsynth/mt32/srchelper/srctools/src/SincResampler.cpp
index 3ed028d..fff2703 100644
--- a/audio/softsynth/mt32/srchelper/srctools/src/SincResampler.cpp
+++ b/audio/softsynth/mt32/srchelper/srctools/src/SincResampler.cpp
@@ -16,11 +16,11 @@
 
 #include <cmath>
 
-#ifdef SINC_RESAMPLER_DEBUG_LOG
+#ifdef SRCTOOLS_SINC_RESAMPLER_DEBUG_LOG
 #include <iostream>
 #endif
 
-#include "SincResampler.h"
+#include "../include/SincResampler.h"
 
 #ifndef M_PI
 static const double M_PI = 3.1415926535897932;
@@ -124,7 +124,7 @@ ResamplerStage *SincResampler::createSincResampler(const double inputFrequency,
 	unsigned int order = KaizerWindow::estimateOrder(dbSNR, fp, fs);
 	const unsigned int kernelLength = order + 1;
 
-#ifdef SINC_RESAMPLER_DEBUG_LOG
+#ifdef SRCTOOLS_SINC_RESAMPLER_DEBUG_LOG
 	std::clog << "FIR: " << upsampleFactor << "/" << downsampleFactor << ", N=" << kernelLength << ", NPh=" << kernelLength / double(upsampleFactor) << ", C=" << 0.5 / fc << ", fp=" << fp << ", fs=" << fs << ", M=" << maxUpsampleFactor << std::endl;
 #endif
 





More information about the Scummvm-git-logs mailing list