[Scummvm-cvs-logs] scummvm master -> 00992c1e68444a8123ffc89a971751cecf7287ed

bluegr bluegr at gmail.com
Wed Aug 21 02:43:27 CEST 2013


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:
00992c1e68 MT-32: Sync with the latest changes in munt


Commit: 00992c1e68444a8123ffc89a971751cecf7287ed
    https://github.com/scummvm/scummvm/commit/00992c1e68444a8123ffc89a971751cecf7287ed
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2013-08-20T17:41:30-07:00

Commit Message:
MT-32: Sync with the latest changes in munt

Changed paths:
  A audio/softsynth/mt32/LA32FloatWaveGenerator.cpp
  A audio/softsynth/mt32/LA32FloatWaveGenerator.h
  R audio/softsynth/mt32/AReverbModel.cpp
  R audio/softsynth/mt32/AReverbModel.h
  R audio/softsynth/mt32/DelayReverb.cpp
  R audio/softsynth/mt32/DelayReverb.h
  R audio/softsynth/mt32/FreeverbModel.cpp
  R audio/softsynth/mt32/FreeverbModel.h
  R audio/softsynth/mt32/LegacyWaveGenerator.cpp
  R audio/softsynth/mt32/LegacyWaveGenerator.h
  R audio/softsynth/mt32/freeverb.cpp
  R audio/softsynth/mt32/freeverb.h
    audio/softsynth/mt32/BReverbModel.cpp
    audio/softsynth/mt32/BReverbModel.h
    audio/softsynth/mt32/LA32WaveGenerator.cpp
    audio/softsynth/mt32/LA32WaveGenerator.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/Structures.h
    audio/softsynth/mt32/Synth.cpp
    audio/softsynth/mt32/Synth.h
    audio/softsynth/mt32/TVP.cpp
    audio/softsynth/mt32/Tables.h
    audio/softsynth/mt32/module.mk
    audio/softsynth/mt32/mt32emu.h



diff --git a/audio/softsynth/mt32/AReverbModel.cpp b/audio/softsynth/mt32/AReverbModel.cpp
deleted file mode 100644
index 1d63832..0000000
--- a/audio/softsynth/mt32/AReverbModel.cpp
+++ /dev/null
@@ -1,277 +0,0 @@
-/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU Lesser General Public License as published by
- *  the Free Software Foundation, either version 2.1 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "mt32emu.h"
-
-#if MT32EMU_USE_REVERBMODEL == 1
-
-#include "AReverbModel.h"
-
-// Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that
-// the reverb model implemented in the real devices consists of three series allpass filters preceded by a non-feedback comb (or a delay with a LPF)
-// and followed by three parallel comb filters
-
-namespace MT32Emu {
-
-// Because LA-32 chip makes it's output available to process by the Boss chip with a significant delay,
-// the Boss chip puts to the buffer the LA32 dry output when it is ready and performs processing of the _previously_ latched data.
-// Of course, the right way would be to use a dedicated variable for this, but our reverb model is way higher level,
-// so we can simply increase the input buffer size.
-static const Bit32u PROCESS_DELAY = 1;
-
-// Default reverb settings for modes 0-2. These correspond to CM-32L / LAPC-I "new" reverb settings. MT-32 reverb is a bit different.
-// Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog).
-
-static const Bit32u NUM_ALLPASSES = 3;
-static const Bit32u NUM_COMBS = 4; // Well, actually there are 3 comb filters, but the entrance LPF + delay can be perfectly processed via a comb here.
-
-static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78};
-static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632};
-static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960};
-static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145};
-static const Bit32u MODE_0_COMB_FACTOR[] = {0x3C, 0x60, 0x60, 0x60};
-static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                                              0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
-                                              0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
-                                              0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
-static const Bit32u MODE_0_LEVELS[] = {10*1, 10*3, 10*5, 10*7, 11*9, 11*12, 11*15, 13*15};
-static const Bit32u MODE_0_LPF_AMP = 6;
-
-static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176};
-static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519};
-static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518};
-static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274};
-static const Bit32u MODE_1_COMB_FACTOR[] = {0x30, 0x60, 0x60, 0x60};
-static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-											  0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
-											  0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
-											  0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
-static const Bit32u MODE_1_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 11*15, 14*15};
-static const Bit32u MODE_1_LPF_AMP = 6;
-
-static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157};
-static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539};
-static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769};
-static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1};
-static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20};
-static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                                              0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0,
-                                              0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0,
-                                              0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0};
-static const Bit32u MODE_2_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 12*15, 14*15};
-static const Bit32u MODE_2_LPF_AMP = 8;
-
-static const AReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_ALLPASSES, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_LEVELS, MODE_0_LPF_AMP};
-static const AReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_ALLPASSES, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_LEVELS, MODE_1_LPF_AMP};
-static const AReverbSettings REVERB_MODE_2_SETTINGS = {MODE_2_ALLPASSES, MODE_2_COMBS, MODE_2_OUTL, MODE_2_OUTR, MODE_2_COMB_FACTOR, MODE_2_COMB_FEEDBACK, MODE_2_LEVELS, MODE_2_LPF_AMP};
-
-static const AReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_0_SETTINGS};
-
-RingBuffer::RingBuffer(const Bit32u newsize) : size(newsize), index(0) {
-	buffer = new float[size];
-}
-
-RingBuffer::~RingBuffer() {
-	delete[] buffer;
-	buffer = NULL;
-}
-
-float RingBuffer::next() {
-	if (++index >= size) {
-		index = 0;
-	}
-	return buffer[index];
-}
-
-bool RingBuffer::isEmpty() const {
-	if (buffer == NULL) return true;
-
-	float *buf = buffer;
-	float max = 0.001f;
-	for (Bit32u i = 0; i < size; i++) {
-		if ((*buf < -max) || (*buf > max)) return false;
-		buf++;
-	}
-	return true;
-}
-
-void RingBuffer::mute() {
-	float *buf = buffer;
-	for (Bit32u i = 0; i < size; i++) {
-		*buf++ = 0;
-	}
-}
-
-AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {}
-
-float AllpassFilter::process(const float in) {
-	// This model corresponds to the allpass filter implementation of the real CM-32L device
-	// found from sample analysis
-
-	const float bufferOut = next();
-
-	// store input - feedback / 2
-	buffer[index] = in - 0.5f * bufferOut;
-
-	// return buffer output + feedforward / 2
-	return bufferOut + 0.5f * buffer[index];
-}
-
-CombFilter::CombFilter(const Bit32u useSize) : RingBuffer(useSize) {}
-
-void CombFilter::process(const float in) {
-	// This model corresponds to the comb filter implementation of the real CM-32L device
-	// found from sample analysis
-
-	// the previously stored value
-	float last = buffer[index];
-
-	// prepare input + feedback
-	float filterIn = in + next() * feedbackFactor;
-
-	// store input + feedback processed by a low-pass filter
-	buffer[index] = filterFactor * last - filterIn;
-}
-
-float CombFilter::getOutputAt(const Bit32u outIndex) const {
-	return buffer[(size + index - outIndex) % size];
-}
-
-void CombFilter::setFeedbackFactor(const float useFeedbackFactor) {
-	feedbackFactor = useFeedbackFactor;
-}
-
-void CombFilter::setFilterFactor(const float useFilterFactor) {
-	filterFactor = useFilterFactor;
-}
-
-AReverbModel::AReverbModel(const ReverbMode mode) : allpasses(NULL), combs(NULL), currentSettings(*REVERB_SETTINGS[mode]) {}
-
-AReverbModel::~AReverbModel() {
-	close();
-}
-
-void AReverbModel::open() {
-	allpasses = new AllpassFilter*[NUM_ALLPASSES];
-	for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
-		allpasses[i] = new AllpassFilter(currentSettings.allpassSizes[i]);
-	}
-	combs = new CombFilter*[NUM_COMBS];
-	for (Bit32u i = 0; i < NUM_COMBS; i++) {
-		combs[i] = new CombFilter(currentSettings.combSizes[i]);
-		combs[i]->setFilterFactor(currentSettings.filterFactor[i] / 256.0f);
-	}
-	lpfAmp = currentSettings.lpfAmp / 16.0f;
-	mute();
-}
-
-void AReverbModel::close() {
-	if (allpasses != NULL) {
-		for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
-			if (allpasses[i] != NULL) {
-				delete allpasses[i];
-				allpasses[i] = NULL;
-			}
-		}
-		delete[] allpasses;
-		allpasses = NULL;
-	}
-	if (combs != NULL) {
-		for (Bit32u i = 0; i < NUM_COMBS; i++) {
-			if (combs[i] != NULL) {
-				delete combs[i];
-				combs[i] = NULL;
-			}
-		}
-		delete[] combs;
-		combs = NULL;
-	}
-}
-
-void AReverbModel::mute() {
-	if (allpasses == NULL || combs == NULL) return;
-	for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
-		allpasses[i]->mute();
-	}
-	for (Bit32u i = 0; i < NUM_COMBS; i++) {
-		combs[i]->mute();
-	}
-}
-
-void AReverbModel::setParameters(Bit8u time, Bit8u level) {
-// FIXME: wetLevel definitely needs ramping when changed
-// Although, most games don't set reverb level during MIDI playback
-	if (combs == NULL) return;
-	level &= 7;
-	time &= 7;
-	for (Bit32u i = 0; i < NUM_COMBS; i++) {
-		combs[i]->setFeedbackFactor(currentSettings.decayTimes[(i << 3) + time] / 256.0f);
-	}
-	wetLevel = (level == 0 && time == 0) ? 0.0f : 0.5f * lpfAmp * currentSettings.wetLevels[level] / 256.0f;
-}
-
-bool AReverbModel::isActive() const {
-	for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
-		if (!allpasses[i]->isEmpty()) return true;
-	}
-	for (Bit32u i = 0; i < NUM_COMBS; i++) {
-		if (!combs[i]->isEmpty()) return true;
-	}
-	return false;
-}
-
-void AReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) {
-	float dry, link, outL1;
-
-	for (unsigned long i = 0; i < numSamples; i++) {
-		dry = wetLevel * (*inLeft + *inRight);
-
-		// Get the last stored sample before processing in order not to loose it
-		link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1);
-
-		combs[0]->process(-dry);
-
-		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
-		outL1 = 1.5f * combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1);
-
-		combs[1]->process(link);
-		combs[2]->process(link);
-		combs[3]->process(link);
-
-		link = outL1 + 1.5f * combs[2]->getOutputAt(currentSettings.outLPositions[1]);
-		link += combs[3]->getOutputAt(currentSettings.outLPositions[2]);
-		*outLeft = link;
-
-		link = 1.5f * combs[1]->getOutputAt(currentSettings.outRPositions[0]);
-		link += 1.5f * combs[2]->getOutputAt(currentSettings.outRPositions[1]);
-		link += combs[3]->getOutputAt(currentSettings.outRPositions[2]);
-		*outRight = link;
-
-		inLeft++;
-		inRight++;
-		outLeft++;
-		outRight++;
-	}
-}
-
-}
-
-#endif
diff --git a/audio/softsynth/mt32/AReverbModel.h b/audio/softsynth/mt32/AReverbModel.h
deleted file mode 100644
index c992478..0000000
--- a/audio/softsynth/mt32/AReverbModel.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU Lesser General Public License as published by
- *  the Free Software Foundation, either version 2.1 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef MT32EMU_A_REVERB_MODEL_H
-#define MT32EMU_A_REVERB_MODEL_H
-
-namespace MT32Emu {
-
-struct AReverbSettings {
-	const Bit32u * const allpassSizes;
-	const Bit32u * const combSizes;
-	const Bit32u * const outLPositions;
-	const Bit32u * const outRPositions;
-	const Bit32u * const filterFactor;
-	const Bit32u * const decayTimes;
-	const Bit32u * const wetLevels;
-	const Bit32u lpfAmp;
-};
-
-class RingBuffer {
-protected:
-	float *buffer;
-	const Bit32u size;
-	Bit32u index;
-
-public:
-	RingBuffer(const Bit32u size);
-	virtual ~RingBuffer();
-	float next();
-	bool isEmpty() const;
-	void mute();
-};
-
-class AllpassFilter : public RingBuffer {
-public:
-	AllpassFilter(const Bit32u size);
-	float process(const float in);
-};
-
-class CombFilter : public RingBuffer {
-	float feedbackFactor;
-	float filterFactor;
-
-public:
-	CombFilter(const Bit32u size);
-	void process(const float in);
-	float getOutputAt(const Bit32u outIndex) const;
-	void setFeedbackFactor(const float useFeedbackFactor);
-	void setFilterFactor(const float useFilterFactor);
-};
-
-class AReverbModel : public ReverbModel {
-	AllpassFilter **allpasses;
-	CombFilter **combs;
-
-	const AReverbSettings ¤tSettings;
-	float lpfAmp;
-	float wetLevel;
-	void mute();
-
-public:
-	AReverbModel(const ReverbMode mode);
-	~AReverbModel();
-	void open();
-	void close();
-	void setParameters(Bit8u time, Bit8u level);
-	void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples);
-	bool isActive() const;
-};
-
-}
-
-#endif
diff --git a/audio/softsynth/mt32/BReverbModel.cpp b/audio/softsynth/mt32/BReverbModel.cpp
index cc0219b..c16f7f1 100644
--- a/audio/softsynth/mt32/BReverbModel.cpp
+++ b/audio/softsynth/mt32/BReverbModel.cpp
@@ -15,10 +15,8 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+//#include <memory.h>
 #include "mt32emu.h"
-
-#if MT32EMU_USE_REVERBMODEL == 2
-
 #include "BReverbModel.h"
 
 // Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that
@@ -62,9 +60,9 @@ static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518};
 static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274};
 static const Bit32u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60};
 static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-											  0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
-											  0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
-											  0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
+                                              0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
+                                              0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
+                                              0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
 static const Bit32u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0};
 static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0};
 static const Bit32u MODE_1_LPF_AMP = 0x60;
@@ -103,7 +101,11 @@ static const BReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTING
 
 // 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 Bit32s weirdMul(Bit32s a, Bit8u addMask, Bit8u carryMask) {
+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
 	Bit8u mask = 0x80;
 	Bit32s res = 0;
 	for (int i = 0; i < 8; i++) {
@@ -113,10 +115,13 @@ static Bit32s weirdMul(Bit32s a, Bit8u addMask, Bit8u carryMask) {
 		mask >>= 1;
 	}
 	return res;
+#else
+	return Sample(((Bit32s)a * addMask) >> 8);
+#endif
 }
 
 RingBuffer::RingBuffer(Bit32u newsize) : size(newsize), index(0) {
-	buffer = new Bit16s[size];
+	buffer = new Sample[size];
 }
 
 RingBuffer::~RingBuffer() {
@@ -124,7 +129,7 @@ RingBuffer::~RingBuffer() {
 	buffer = NULL;
 }
 
-Bit32s RingBuffer::next() {
+Sample RingBuffer::next() {
 	if (++index >= size) {
 		index = 0;
 	}
@@ -134,52 +139,69 @@ Bit32s RingBuffer::next() {
 bool RingBuffer::isEmpty() const {
 	if (buffer == NULL) return true;
 
-	Bit16s *buf = buffer;
+#if MT32EMU_USE_FLOAT_SAMPLES
+	Sample max = 0.001f;
+#else
+	Sample max = 8;
+#endif
+	Sample *buf = buffer;
 	for (Bit32u i = 0; i < size; i++) {
-		if (*buf < -8 || *buf > 8) return false;
+		if (*buf < -max || *buf > max) return false;
 		buf++;
 	}
 	return true;
 }
 
 void RingBuffer::mute() {
-	Bit16s *buf = buffer;
+#if MT32EMU_USE_FLOAT_SAMPLES
+	Sample *buf = buffer;
 	for (Bit32u i = 0; i < size; i++) {
 		*buf++ = 0;
 	}
+#else
+	memset(buffer, 0, size * sizeof(Sample));
+#endif
 }
 
 AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {}
 
-Bit32s AllpassFilter::process(const Bit32s in) {
+Sample AllpassFilter::process(const Sample in) {
 	// This model corresponds to the allpass filter implementation of the real CM-32L device
 	// found from sample analysis
 
-	Bit16s bufferOut = next();
+	const Sample bufferOut = next();
 
+#if MT32EMU_USE_FLOAT_SAMPLES
+	// store input - feedback / 2
+	buffer[index] = in - 0.5f * bufferOut;
+
+	// return buffer output + feedforward / 2
+	return bufferOut + 0.5f * buffer[index];
+#else
 	// store input - feedback / 2
 	buffer[index] = in - (bufferOut >> 1);
 
 	// return buffer output + feedforward / 2
 	return bufferOut + (buffer[index] >> 1);
+#endif
 }
 
 CombFilter::CombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : RingBuffer(useSize), filterFactor(useFilterFactor) {}
 
-void CombFilter::process(const Bit32s in) {
+void CombFilter::process(const Sample in) {
 	// This model corresponds to the comb filter implementation of the real CM-32L device
 
 	// the previously stored value
-	Bit32s last = buffer[index];
+	const Sample last = buffer[index];
 
 	// prepare input + feedback
-	Bit32s filterIn = in + weirdMul(next(), feedbackFactor, 0xF0 /* Maybe 0x80 ? */);
+	const Sample filterIn = in + weirdMul(next(), feedbackFactor, 0xF0 /* Maybe 0x80 ? */);
 
 	// store input + feedback processed by a low-pass filter
 	buffer[index] = weirdMul(last, filterFactor, 0x40) - filterIn;
 }
 
-Bit32s CombFilter::getOutputAt(const Bit32u outIndex) const {
+Sample CombFilter::getOutputAt(const Bit32u outIndex) const {
 	return buffer[(size + index - outIndex) % size];
 }
 
@@ -190,15 +212,15 @@ void CombFilter::setFeedbackFactor(const Bit32u useFeedbackFactor) {
 DelayWithLowPassFilter::DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp)
 	: CombFilter(useSize, useFilterFactor), amp(useAmp) {}
 
-void DelayWithLowPassFilter::process(const Bit32s in) {
+void DelayWithLowPassFilter::process(const Sample in) {
 	// the previously stored value
-	Bit32s last = buffer[index];
+	const Sample last = buffer[index];
 
 	// move to the next index
 	next();
 
 	// low-pass filter process
-	Bit32s lpfOut = weirdMul(last, filterFactor, 0xFF) + in;
+	Sample lpfOut = weirdMul(last, filterFactor, 0xFF) + in;
 
 	// store lpfOut multiplied by LPF amp factor
 	buffer[index] = weirdMul(lpfOut, amp, 0xFF);
@@ -206,26 +228,26 @@ void DelayWithLowPassFilter::process(const Bit32s in) {
 
 TapDelayCombFilter::TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : CombFilter(useSize, useFilterFactor) {}
 
-void TapDelayCombFilter::process(const Bit32s in) {
+void TapDelayCombFilter::process(const Sample in) {
 	// the previously stored value
-	Bit32s last = buffer[index];
+	const Sample last = buffer[index];
 
 	// move to the next index
 	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
-	Bit32s filterIn = in + weirdMul(getOutputAt(outR + MODE_3_FEEDBACK_DELAY), feedbackFactor, 0xF0);
+	const Sample filterIn = in + weirdMul(getOutputAt(outR + MODE_3_FEEDBACK_DELAY), feedbackFactor, 0xF0);
 
 	// store input + feedback processed by a low-pass filter
 	buffer[index] = weirdMul(last, filterFactor, 0xF0) - filterIn;
 }
 
-Bit32s TapDelayCombFilter::getLeftOutput() const {
+Sample TapDelayCombFilter::getLeftOutput() const {
 	return getOutputAt(outL + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY);
 }
 
-Bit32s TapDelayCombFilter::getRightOutput() const {
+Sample TapDelayCombFilter::getRightOutput() const {
 	return getOutputAt(outR + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY);
 }
 
@@ -327,14 +349,14 @@ bool BReverbModel::isActive() const {
 	return false;
 }
 
-void BReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) {
-	Bit32s dry, link, outL1, outR1;
+void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples) {
+	Sample dry;
 
-	for (unsigned long i = 0; i < numSamples; i++) {
+	while (numSamples > 0) {
 		if (tapDelayMode) {
-			dry = Bit32s(*inLeft * 8192.0f) + Bit32s(*inRight * 8192.0f);
+			dry = *inLeft + *inRight;
 		} else {
-			dry = Bit32s(*inLeft * 8192.0f) / 2 + Bit32s(*inRight * 8192.0f) / 2;
+			dry = *inLeft / 2 + *inRight / 2;
 		}
 
 		// Looks like dryAmp doesn't change in MT-32 but it does in CM-32L / LAPC-I
@@ -343,44 +365,53 @@ void BReverbModel::process(const float *inLeft, const float *inRight, float *out
 		if (tapDelayMode) {
 			TapDelayCombFilter *comb = static_cast<TapDelayCombFilter *> (*combs);
 			comb->process(dry);
-			*outLeft = weirdMul(comb->getLeftOutput(), wetLevel, 0xFF) / 8192.0f;
-			*outRight = weirdMul(comb->getRightOutput(), wetLevel, 0xFF) / 8192.0f;
+			*outLeft = weirdMul(comb->getLeftOutput(), wetLevel, 0xFF);
+			*outRight = weirdMul(comb->getRightOutput(), wetLevel, 0xFF);
 		} else {
-			// Get the last stored sample before processing in order not to loose it
-			link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1);
+			// 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
-			outL1 = combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1);
-			outL1 += outL1 >> 1;
+			Sample outL1 = combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1);
 
 			combs[1]->process(link);
 			combs[2]->process(link);
 			combs[3]->process(link);
 
-			link = combs[2]->getOutputAt(currentSettings.outLPositions[1]);
-			link += link >> 1;
-			link += outL1;
-			link += combs[3]->getOutputAt(currentSettings.outLPositions[2]);
-			*outLeft = weirdMul(link, wetLevel, 0xFF) / 8192.0f;
+			Sample outL2 = combs[2]->getOutputAt(currentSettings.outLPositions[1]);
+			Sample outL3 = combs[3]->getOutputAt(currentSettings.outLPositions[2]);
+			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
+			*outLeft = 1.5f * (outL1 + outL2) + outL3;
+			*outRight = 1.5f * (outR1 + outR2) + outR3;
+#else
+			outL1 += outL1 >> 1;
+			outL2 += outL2 >> 1;
+			*outLeft = outL1 + outL2 + outL3;
 
-			outR1 = combs[1]->getOutputAt(currentSettings.outRPositions[0]);
 			outR1 += outR1 >> 1;
-			link = combs[2]->getOutputAt(currentSettings.outRPositions[1]);
-			link += link >> 1;
-			link += outR1;
-			link += combs[3]->getOutputAt(currentSettings.outRPositions[2]);
-			*outRight = weirdMul(link, wetLevel, 0xFF) / 8192.0f;
+			outR2 += outR2 >> 1;
+			*outRight = outR1 + outR2 + outR3;
+#endif
+			*outLeft = weirdMul(*outLeft, wetLevel, 0xFF);
+			*outRight = weirdMul(*outRight, wetLevel, 0xFF);
 		}
 
+		numSamples--;
 		inLeft++;
 		inRight++;
 		outLeft++;
@@ -389,5 +420,3 @@ void BReverbModel::process(const float *inLeft, const float *inRight, float *out
 }
 
 }
-
-#endif
diff --git a/audio/softsynth/mt32/BReverbModel.h b/audio/softsynth/mt32/BReverbModel.h
index d6fcb73..7cf431d 100644
--- a/audio/softsynth/mt32/BReverbModel.h
+++ b/audio/softsynth/mt32/BReverbModel.h
@@ -36,14 +36,14 @@ struct BReverbSettings {
 
 class RingBuffer {
 protected:
-	Bit16s *buffer;
+	Sample *buffer;
 	const Bit32u size;
 	Bit32u index;
 
 public:
 	RingBuffer(const Bit32u size);
 	virtual ~RingBuffer();
-	Bit32s next();
+	Sample next();
 	bool isEmpty() const;
 	void mute();
 };
@@ -51,7 +51,7 @@ public:
 class AllpassFilter : public RingBuffer {
 public:
 	AllpassFilter(const Bit32u size);
-	Bit32s process(const Bit32s in);
+	Sample process(const Sample in);
 };
 
 class CombFilter : public RingBuffer {
@@ -61,8 +61,8 @@ protected:
 
 public:
 	CombFilter(const Bit32u size, const Bit32u useFilterFactor);
-	virtual void process(const Bit32s in); // Actually, no need to make it virtual, but for sure
-	Bit32s getOutputAt(const Bit32u outIndex) const;
+	virtual void process(const Sample in);
+	Sample getOutputAt(const Bit32u outIndex) const;
 	void setFeedbackFactor(const Bit32u useFeedbackFactor);
 };
 
@@ -71,7 +71,7 @@ class DelayWithLowPassFilter : public CombFilter {
 
 public:
 	DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp);
-	void process(const Bit32s in);
+	void process(const Sample in);
 	void setFeedbackFactor(const Bit32u) {}
 };
 
@@ -81,13 +81,13 @@ class TapDelayCombFilter : public CombFilter {
 
 public:
 	TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor);
-	void process(const Bit32s in);
-	Bit32s getLeftOutput() const;
-	Bit32s getRightOutput() const;
+	void process(const Sample in);
+	Sample getLeftOutput() const;
+	Sample getRightOutput() const;
 	void setOutputPositions(const Bit32u useOutL, const Bit32u useOutR);
 };
 
-class BReverbModel : public ReverbModel {
+class BReverbModel {
 	AllpassFilter **allpasses;
 	CombFilter **combs;
 
@@ -100,10 +100,12 @@ class BReverbModel : public ReverbModel {
 public:
 	BReverbModel(const ReverbMode mode);
 	~BReverbModel();
+	// After construction or a close(), open() must be called at least once before any other call (with the exception of close()).
 	void open();
+	// May be called multiple times without an open() in between.
 	void close();
 	void setParameters(Bit8u time, Bit8u level);
-	void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples);
+	void process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples);
 	bool isActive() const;
 };
 
diff --git a/audio/softsynth/mt32/DelayReverb.cpp b/audio/softsynth/mt32/DelayReverb.cpp
deleted file mode 100644
index d80c98a..0000000
--- a/audio/softsynth/mt32/DelayReverb.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU Lesser General Public License as published by
- *  the Free Software Foundation, either version 2.1 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-//#include <cmath>
-//#include <cstring>
-#include "mt32emu.h"
-#include "DelayReverb.h"
-
-namespace MT32Emu {
-
-// CONFIRMED: The values below are found via analysis of digital samples and tracing reverb RAM address / data lines. Checked with all time and level combinations.
-// Obviously:
-// rightDelay = (leftDelay - 2) * 2 + 2
-// echoDelay = rightDelay - 1
-// Leaving these separate in case it's useful for work on other reverb modes...
-static const Bit32u REVERB_TIMINGS[8][3]= {
-	// {leftDelay, rightDelay, feedbackDelay}
-	{402, 802, 801},
-	{626, 1250, 1249},
-	{962, 1922, 1921},
-	{1490, 2978, 2977},
-	{2258, 4514, 4513},
-	{3474, 6946, 6945},
-	{5282, 10562, 10561},
-	{8002, 16002, 16001}
-};
-
-// Reverb amp is found as dryAmp * wetAmp
-static const Bit32u REVERB_AMP[8] = {0x20*0x18, 0x50*0x18, 0x50*0x28, 0x50*0x40, 0x50*0x60, 0x50*0x80, 0x50*0xA8, 0x50*0xF8};
-static const Bit32u REVERB_FEEDBACK67 = 0x60;
-static const Bit32u REVERB_FEEDBACK = 0x68;
-static const float LPF_VALUE = 0x68 / 256.0f;
-
-static const Bit32u BUFFER_SIZE = 16384;
-
-DelayReverb::DelayReverb() {
-	buf = NULL;
-	setParameters(0, 0);
-}
-
-DelayReverb::~DelayReverb() {
-	delete[] buf;
-}
-
-void DelayReverb::open() {
-	if (buf == NULL) {
-		delete[] buf;
-
-		buf = new float[BUFFER_SIZE];
-
-		recalcParameters();
-
-		// mute buffer
-		bufIx = 0;
-		if (buf != NULL) {
-			for (unsigned int i = 0; i < BUFFER_SIZE; i++) {
-				buf[i] = 0.0f;
-			}
-		}
-	}
-}
-
-void DelayReverb::close() {
-	delete[] buf;
-	buf = NULL;
-}
-
-// This method will always trigger a flush of the buffer
-void DelayReverb::setParameters(Bit8u newTime, Bit8u newLevel) {
-	time = newTime;
-	level = newLevel;
-	recalcParameters();
-}
-
-void DelayReverb::recalcParameters() {
-	// Number of samples between impulse and eventual appearance on the left channel
-	delayLeft = REVERB_TIMINGS[time][0];
-	// Number of samples between impulse and eventual appearance on the right channel
-	delayRight = REVERB_TIMINGS[time][1];
-	// Number of samples between a response and that response feeding back/echoing
-	delayFeedback = REVERB_TIMINGS[time][2];
-
-	if (level < 3 || time < 6) {
-		feedback = REVERB_FEEDBACK / 256.0f;
-	} else {
-		feedback = REVERB_FEEDBACK67 / 256.0f;
-	}
-
-	// Overall output amp
-	amp = (level == 0 && time == 0) ? 0.0f : REVERB_AMP[level] / 65536.0f;
-}
-
-void DelayReverb::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) {
-	if (buf == NULL) return;
-
-	for (unsigned int sampleIx = 0; sampleIx < numSamples; sampleIx++) {
-		// The ring buffer write index moves backwards; reads are all done with positive offsets.
-		Bit32u bufIxPrev = (bufIx + 1) % BUFFER_SIZE;
-		Bit32u bufIxLeft = (bufIx + delayLeft) % BUFFER_SIZE;
-		Bit32u bufIxRight = (bufIx + delayRight) % BUFFER_SIZE;
-		Bit32u bufIxFeedback = (bufIx + delayFeedback) % BUFFER_SIZE;
-
-		// Attenuated input samples and feedback response are directly added to the current ring buffer location
-		float lpfIn = amp * (inLeft[sampleIx] + inRight[sampleIx]) + feedback * buf[bufIxFeedback];
-
-		// Single-pole IIR filter found on real devices
-		buf[bufIx] = buf[bufIxPrev] * LPF_VALUE - lpfIn;
-
-		outLeft[sampleIx] = buf[bufIxLeft];
-		outRight[sampleIx] = buf[bufIxRight];
-
-		bufIx = (BUFFER_SIZE + bufIx - 1) % BUFFER_SIZE;
-	}
-}
-
-bool DelayReverb::isActive() const {
-	if (buf == NULL) return false;
-
-	float *b = buf;
-	float max = 0.001f;
-	for (Bit32u i = 0; i < BUFFER_SIZE; i++) {
-		if ((*b < -max) || (*b > max)) return true;
-		b++;
-	}
-	return false;
-}
-
-}
diff --git a/audio/softsynth/mt32/DelayReverb.h b/audio/softsynth/mt32/DelayReverb.h
deleted file mode 100644
index c800383..0000000
--- a/audio/softsynth/mt32/DelayReverb.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU Lesser General Public License as published by
- *  the Free Software Foundation, either version 2.1 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef MT32EMU_DELAYREVERB_H
-#define MT32EMU_DELAYREVERB_H
-
-namespace MT32Emu {
-
-class DelayReverb : public ReverbModel {
-private:
-	Bit8u time;
-	Bit8u level;
-
-	Bit32u bufIx;
-	float *buf;
-
-	Bit32u delayLeft;
-	Bit32u delayRight;
-	Bit32u delayFeedback;
-
-	float amp;
-	float feedback;
-
-	void recalcParameters();
-
-public:
-	DelayReverb();
-	~DelayReverb();
-	void open();
-	void close();
-	void setParameters(Bit8u time, Bit8u level);
-	void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples);
-	bool isActive() const;
-};
-}
-#endif
diff --git a/audio/softsynth/mt32/FreeverbModel.cpp b/audio/softsynth/mt32/FreeverbModel.cpp
deleted file mode 100644
index bd9c70b..0000000
--- a/audio/softsynth/mt32/FreeverbModel.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU Lesser General Public License as published by
- *  the Free Software Foundation, either version 2.1 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "mt32emu.h"
-#include "FreeverbModel.h"
-
-#include "freeverb.h"
-
-namespace MT32Emu {
-
-FreeverbModel::FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp) {
-	freeverb = NULL;
-	scaleTuning = useScaleTuning;
-	filtVal = useFiltVal;
-	wet = useWet;
-	room = useRoom;
-	damp = useDamp;
-}
-
-FreeverbModel::~FreeverbModel() {
-	delete freeverb;
-}
-
-void FreeverbModel::open() {
-	if (freeverb == NULL) {
-		freeverb = new revmodel(scaleTuning);
-	}
-	freeverb->mute();
-
-	// entrance Lowpass filter factor
-	freeverb->setfiltval(filtVal);
-
-	// decay speed of high frequencies in the wet signal
-	freeverb->setdamp(damp);
-}
-
-void FreeverbModel::close() {
-	delete freeverb;
-	freeverb = NULL;
-}
-
-void FreeverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) {
-	freeverb->process(inLeft, inRight, outLeft, outRight, numSamples);
-}
-
-void FreeverbModel::setParameters(Bit8u time, Bit8u level) {
-	// wet signal level
-	// FIXME: need to implement some sort of reverb level ramping
-	freeverb->setwet((float)level / 7.0f * wet);
-
-	// wet signal decay speed
-	static float roomTable[] = {
-		 0.25f,  0.37f, 0.54f, 0.71f, 0.78f, 0.86f, 0.93f, 1.00f,
-		-1.00f, -0.50f, 0.00f, 0.30f, 0.51f, 0.64f, 0.77f, 0.90f,
-		 0.50f,  0.57f, 0.70f, 0.77f, 0.85f, 0.93f, 0.96f, 1.01f};
-	freeverb->setroomsize(roomTable[8 * room + time]);
-}
-
-bool FreeverbModel::isActive() const {
-	// FIXME: Not bothering to do this properly since we'll be replacing Freeverb soon...
-	return false;
-}
-
-}
diff --git a/audio/softsynth/mt32/FreeverbModel.h b/audio/softsynth/mt32/FreeverbModel.h
deleted file mode 100644
index 5ea11f1..0000000
--- a/audio/softsynth/mt32/FreeverbModel.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU Lesser General Public License as published by
- *  the Free Software Foundation, either version 2.1 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef MT32EMU_FREEVERB_MODEL_H
-#define MT32EMU_FREEVERB_MODEL_H
-
-class revmodel;
-
-namespace MT32Emu {
-
-class FreeverbModel : public ReverbModel {
-	revmodel *freeverb;
-	float scaleTuning;
-	float filtVal;
-	float wet;
-	Bit8u room;
-	float damp;
-public:
-	FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp);
-	~FreeverbModel();
-	void open();
-	void close();
-	void setParameters(Bit8u time, Bit8u level);
-	void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples);
-	bool isActive() const;
-};
-
-}
-
-#endif
diff --git a/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp b/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp
new file mode 100644
index 0000000..486942b
--- /dev/null
+++ b/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp
@@ -0,0 +1,343 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 2.1 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//#include <cmath>
+#include "mt32emu.h"
+#include "mmath.h"
+#include "LA32FloatWaveGenerator.h"
+
+namespace MT32Emu {
+
+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) {
+	if (position >= pcmWaveLength) {
+		if (!pcmWaveLooped) {
+			return 0;
+		}
+		position = position % pcmWaveLength;
+	}
+	Bit16s pcmSample = pcmWaveAddress[position];
+	float sampleValue = EXP2F(((pcmSample & 32767) - 32787.0f) / 2048.0f);
+	return ((pcmSample & 32768) == 0) ? sampleValue : -sampleValue;
+}
+
+void LA32WaveGenerator::initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) {
+	this->sawtoothWaveform = sawtoothWaveform;
+	this->pulseWidth = pulseWidth;
+	this->resonance = resonance;
+
+	wavePos = 0.0f;
+	lastFreq = 0.0f;
+
+	pcmWaveAddress = NULL;
+	active = true;
+}
+
+void LA32WaveGenerator::initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated) {
+	this->pcmWaveAddress = pcmWaveAddress;
+	this->pcmWaveLength = pcmWaveLength;
+	this->pcmWaveLooped = pcmWaveLooped;
+	this->pcmWaveInterpolated = pcmWaveInterpolated;
+
+	pcmPosition = 0.0f;
+	active = true;
+}
+
+float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pitch, const Bit32u cutoffRampVal) {
+	if (!active) {
+		return 0.0f;
+	}
+
+	this->amp = amp;
+	this->pitch = pitch;
+
+	float sample = 0.0f;
+
+	// SEMI-CONFIRMED: From sample analysis:
+	// (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc.
+	// This gives results within +/- 2 at the output (before any DAC bitshifting)
+	// when sustaining at levels 156 - 255 with no modifiers.
+	// (2) Tested with a special square wave partial (internal capture ID tva5) at TVA envelope levels 155-255.
+	// This gives deltas between -1 and 0 compared to the real output. Note that this special partial only produces
+	// positive amps, so negative still needs to be explored, as well as lower levels.
+	//
+	// Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing.
+
+	float amp = EXP2F(ampVal / -1024.0f / 4096.0f);
+	float freq = EXP2F(pitch / 4096.0f - 16.0f) * SAMPLE_RATE;
+
+	if (isPCMWave()) {
+		// Render PCM waveform
+		int len = pcmWaveLength;
+		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();
+			return 0.0f;
+		}
+		float positionDelta = freq * 2048.0f / SAMPLE_RATE;
+
+		// Linear interpolation
+		float firstSample = getPCMSample(intPCMPosition);
+		// We observe that for partial structures with ring modulation the interpolation is not applied to the slave PCM partial.
+		// It's assumed that the multiplication circuitry intended to perform the interpolation on the slave PCM partial
+		// is borrowed by the ring modulation circuit (or the LA32 chip has a similar lack of resources assigned to each partial pair).
+		if (pcmWaveInterpolated) {
+			sample = firstSample + (getPCMSample(intPCMPosition + 1) - firstSample) * (pcmPosition - intPCMPosition);
+		} else {
+			sample = firstSample;
+		}
+
+		float newPCMPosition = pcmPosition + positionDelta;
+		if (pcmWaveLooped) {
+			newPCMPosition = fmod(newPCMPosition, (float)pcmWaveLength);
+		}
+		pcmPosition = newPCMPosition;
+	} else {
+		// Render synthesised waveform
+		wavePos *= lastFreq / freq;
+		lastFreq = freq;
+
+		float resAmp = EXP2F(1.0f - (32 - resonance) / 4.0f);
+		{
+			//static const float resAmpFactor = EXP2F(-7);
+			//resAmp = EXP2I(resonance << 10) * resAmpFactor;
+		}
+
+		// The cutoffModifier may not be supposed to be directly added to the cutoff -
+		// it may for example need to be multiplied in some way.
+		// The 240 cutoffVal limit was determined via sample analysis (internal Munt capture IDs: glop3, glop4).
+		// More research is needed to be sure that this is correct, however.
+		float cutoffVal = cutoffRampVal / 262144.0f;
+		if (cutoffVal > MAX_CUTOFF_VALUE) {
+			cutoffVal = MAX_CUTOFF_VALUE;
+		}
+
+		// Wave length in samples
+		float waveLen = SAMPLE_RATE / freq;
+
+		// Init cosineLen
+		float cosineLen = 0.5f * waveLen;
+		if (cutoffVal > MIDDLE_CUTOFF_VALUE) {
+			cosineLen *= EXP2F((cutoffVal - MIDDLE_CUTOFF_VALUE) / -16.0f); // found from sample analysis
+		}
+
+		// Start playing in center of first cosine segment
+		// relWavePos is shifted by a half of cosineLen
+		float relWavePos = wavePos + 0.5f * cosineLen;
+		if (relWavePos > waveLen) {
+			relWavePos -= waveLen;
+		}
+
+		// Ratio of positive segment to wave length
+		float pulseLen = 0.5f;
+		if (pulseWidth > 128) {
+			pulseLen = EXP2F((64 - pulseWidth) / 64.0f);
+			//static const float pulseLenFactor = EXP2F(-192 / 64);
+			//pulseLen = EXP2I((256 - pulseWidthVal) << 6) * pulseLenFactor;
+		}
+		pulseLen *= waveLen;
+
+		float hLen = pulseLen - cosineLen;
+
+		// Ignore pulsewidths too high for given freq
+		if (hLen < 0.0f) {
+			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);
+		}
+
+		// Produce filtered square wave with 2 cosine waves on slopes
+
+		// 1st cosine segment
+		if (relWavePos < cosineLen) {
+			sample = -cos(FLOAT_PI * relWavePos / cosineLen);
+		} else
+
+		// high linear segment
+		if (relWavePos < (cosineLen + hLen)) {
+			sample = 1.f;
+		} else
+
+		// 2nd cosine segment
+		if (relWavePos < (2 * cosineLen + hLen)) {
+			sample = cos(FLOAT_PI * (relWavePos - (cosineLen + hLen)) / cosineLen);
+		} else {
+
+		// low linear segment
+			sample = -1.f;
+		}
+
+		if (cutoffVal < 128.0f) {
+
+			// Attenuate samples below cutoff 50
+			// Found by sample analysis
+			sample *= EXP2F(-0.125f * (128.0f - cutoffVal));
+		} else {
+
+			// Add resonance sine. Effective for cutoff > 50 only
+			float resSample = 1.0f;
+
+			// Resonance decay speed factor
+			float resAmpDecayFactor = Tables::getInstance().resAmpDecayFactor[resonance >> 2];
+
+			// Now relWavePos counts from the middle of first cosine
+			relWavePos = wavePos;
+
+			// negative segments
+			if (!(relWavePos < (cosineLen + hLen))) {
+				resSample = -resSample;
+				relWavePos -= cosineLen + hLen;
+
+				// From the digital captures, the decaying speed of the resonance sine is found a bit different for the positive and the negative segments
+				resAmpDecayFactor += 0.25f;
+			}
+
+			// Resonance sine WG
+			resSample *= sin(FLOAT_PI * relWavePos / cosineLen);
+
+			// Resonance sine amp
+			float resAmpFadeLog2 = -0.125f * resAmpDecayFactor * (relWavePos / cosineLen); // seems to be exact
+			float resAmpFade = EXP2F(resAmpFadeLog2);
+
+			// Now relWavePos set negative to the left from center of any cosine
+			relWavePos = wavePos;
+
+			// negative segment
+			if (!(wavePos < (waveLen - 0.5f * cosineLen))) {
+				relWavePos -= waveLen;
+			} else
+
+			// positive segment
+			if (!(wavePos < (hLen + 0.5f * cosineLen))) {
+				relWavePos -= cosineLen + hLen;
+			}
+
+			// To ensure the output wave has no breaks, two different windows are appied to the beginning and the ending of the resonance sine segment
+			if (relWavePos < 0.5f * cosineLen) {
+				float syncSine = sin(FLOAT_PI * relWavePos / cosineLen);
+				if (relWavePos < 0.0f) {
+					// The window is synchronous square sine here
+					resAmpFade *= syncSine * syncSine;
+				} else {
+					// The window is synchronous sine here
+					resAmpFade *= syncSine;
+				}
+			}
+
+			sample += resSample * resAmp * resAmpFade;
+		}
+
+		// sawtooth waves
+		if (sawtoothWaveform) {
+			sample *= cos(FLOAT_2PI * wavePos / waveLen);
+		}
+
+		wavePos++;
+
+		// wavePos isn't supposed to be > waveLen
+		if (wavePos > waveLen) {
+			wavePos -= waveLen;
+		}
+	}
+
+	// Multiply sample with current TVA value
+	sample *= amp;
+	return sample;
+}
+
+void LA32WaveGenerator::deactivate() {
+	active = false;
+}
+
+bool LA32WaveGenerator::isActive() const {
+	return active;
+}
+
+bool LA32WaveGenerator::isPCMWave() const {
+	return pcmWaveAddress != NULL;
+}
+
+void LA32PartialPair::init(const bool ringModulated, const bool mixed) {
+	this->ringModulated = ringModulated;
+	this->mixed = mixed;
+	masterOutputSample = 0.0f;
+	slaveOutputSample = 0.0f;
+}
+
+void LA32PartialPair::initSynth(const PairType useMaster, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) {
+	if (useMaster == MASTER) {
+		master.initSynth(sawtoothWaveform, pulseWidth, resonance);
+	} else {
+		slave.initSynth(sawtoothWaveform, pulseWidth, resonance);
+	}
+}
+
+void LA32PartialPair::initPCM(const PairType useMaster, const Bit16s *pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped) {
+	if (useMaster == MASTER) {
+		master.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, true);
+	} else {
+		slave.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, !ringModulated);
+	}
+}
+
+void LA32PartialPair::generateNextSample(const PairType useMaster, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff) {
+	if (useMaster == MASTER) {
+		masterOutputSample = master.generateNextSample(amp, pitch, cutoff);
+	} else {
+		slaveOutputSample = slave.generateNextSample(amp, pitch, cutoff);
+	}
+}
+
+float LA32PartialPair::nextOutSample() {
+	float outputSample;
+	if (ringModulated) {
+		float ringModulatedSample = masterOutputSample * slaveOutputSample;
+		outputSample = mixed ? masterOutputSample + ringModulatedSample : ringModulatedSample;
+	} else {
+		outputSample = masterOutputSample + slaveOutputSample;
+	}
+	return outputSample;
+}
+
+void LA32PartialPair::deactivate(const PairType useMaster) {
+	if (useMaster == MASTER) {
+		master.deactivate();
+		masterOutputSample = 0.0f;
+	} else {
+		slave.deactivate();
+		slaveOutputSample = 0.0f;
+	}
+}
+
+bool LA32PartialPair::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
new file mode 100644
index 0000000..9046160
--- /dev/null
+++ b/audio/softsynth/mt32/LA32FloatWaveGenerator.h
@@ -0,0 +1,142 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation, either version 2.1 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MT32EMU_LA32_WAVE_GENERATOR_H
+#define MT32EMU_LA32_WAVE_GENERATOR_H
+
+namespace MT32Emu {
+
+/**
+ * LA32WaveGenerator is aimed to represent the exact model of LA32 wave generator.
+ * The output square wave is created by adding high / low linear segments in-between
+ * the rising and falling cosine segments. Basically, it’s very similar to the phase distortion synthesis.
+ * Behaviour of a true resonance filter is emulated by adding decaying sine wave.
+ * 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 {
+	//***************************************************************************
+	//  The local copy of partial parameters below
+	//***************************************************************************
+
+	bool active;
+
+	// True means the resulting square wave is to be multiplied by the synchronous cosine
+	bool sawtoothWaveform;
+
+	// Logarithmic amp of the wave generator
+	Bit32u amp;
+
+	// Logarithmic frequency of the resulting wave
+	Bit16u pitch;
+
+	// Values in range [1..31]
+	// Value 1 correspong to the minimum resonance
+	Bit8u resonance;
+
+	// Processed value in range [0..255]
+	// Values in range [0..128] have no effect and the resulting wave remains symmetrical
+	// Value 255 corresponds to the maximum possible asymmetric of the resulting wave
+	Bit8u pulseWidth;
+
+	// Composed of the base cutoff in range [78..178] left-shifted by 18 bits and the TVF modifier
+	Bit32u cutoffVal;
+
+	// Logarithmic PCM sample start address
+	const Bit16s *pcmWaveAddress;
+
+	// Logarithmic PCM sample length
+	Bit32u pcmWaveLength;
+
+	// true for looped logarithmic PCM samples
+	bool pcmWaveLooped;
+
+	// false for slave PCM partials in the structures with the ring modulation
+	bool pcmWaveInterpolated;
+
+	//***************************************************************************
+	// Internal variables below
+	//***************************************************************************
+
+	float wavePos;
+	float lastFreq;
+	float pcmPosition;
+
+	float getPCMSample(unsigned int position);
+
+public:
+	// Initialise the WG engine for generation of synth partial samples and set up the invariant parameters
+	void initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance);
+
+	// Initialise the WG engine for generation of PCM partial samples and set up the invariant parameters
+	void initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated);
+
+	// Update parameters with respect to TVP, TVA and TVF, and generate next sample
+	float generateNextSample(const Bit32u amp, const Bit16u pitch, const Bit32u cutoff);
+
+	// Deactivate the WG engine
+	void deactivate();
+
+	// Return active state of the WG engine
+	bool isActive() const;
+
+	// Return true if the WG engine generates PCM wave samples
+	bool isPCMWave() const;
+};
+
+// LA32PartialPair contains a structure of two partials being mixed / ring modulated
+class LA32PartialPair {
+	LA32WaveGenerator master;
+	LA32WaveGenerator 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
+	void init(const bool ringModulated, const bool mixed);
+
+	// Initialise the WG engine for generation of synth partial samples and set up the invariant parameters
+	void initSynth(const PairType master, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance);
+
+	// Initialise the WG engine for generation of PCM partial samples and set up the invariant parameters
+	void initPCM(const PairType master, const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped);
+
+	// 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
+	float nextOutSample();
+
+	// Deactivate the WG engine
+	void deactivate(const PairType master);
+
+	// Return active state of the WG engine
+	bool isActive(const PairType master) const;
+};
+
+} // namespace MT32Emu
+
+#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H
diff --git a/audio/softsynth/mt32/LA32WaveGenerator.cpp b/audio/softsynth/mt32/LA32WaveGenerator.cpp
index 8065069..9ffc2ca 100644
--- a/audio/softsynth/mt32/LA32WaveGenerator.cpp
+++ b/audio/softsynth/mt32/LA32WaveGenerator.cpp
@@ -20,7 +20,9 @@
 #include "mmath.h"
 #include "LA32WaveGenerator.h"
 
-#if MT32EMU_ACCURATE_WG == 0
+#if MT32EMU_USE_FLOAT_SAMPLES
+#include "LA32FloatWaveGenerator.cpp"
+#else
 
 namespace MT32Emu {
 
@@ -129,7 +131,8 @@ void LA32WaveGenerator::advancePosition() {
 	computePositions(highLinearLength, lowLinearLength, resonanceWaveLengthFactor);
 
 	// resonancePhase computation hack
-	*(int*)&resonancePhase = ((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3;
+	int *resonancePhaseAlias = (int *)&resonancePhase;
+	*resonancePhaseAlias = ((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3;
 }
 
 void LA32WaveGenerator::generateNextSquareWaveLogSample() {
@@ -415,4 +418,4 @@ bool LA32PartialPair::isActive(const PairType useMaster) const {
 
 }
 
-#endif // #if MT32EMU_ACCURATE_WG == 0
+#endif // #if MT32EMU_USE_FLOAT_SAMPLES
diff --git a/audio/softsynth/mt32/LA32WaveGenerator.h b/audio/softsynth/mt32/LA32WaveGenerator.h
index 37a4aea..4bc04c7 100644
--- a/audio/softsynth/mt32/LA32WaveGenerator.h
+++ b/audio/softsynth/mt32/LA32WaveGenerator.h
@@ -15,7 +15,9 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#if MT32EMU_ACCURATE_WG == 0
+#if MT32EMU_USE_FLOAT_SAMPLES
+#include "LA32FloatWaveGenerator.h"
+#else
 
 #ifndef MT32EMU_LA32_WAVE_GENERATOR_H
 #define MT32EMU_LA32_WAVE_GENERATOR_H
@@ -243,4 +245,4 @@ public:
 
 #endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H
 
-#endif // #if MT32EMU_ACCURATE_WG == 0
+#endif // #if MT32EMU_USE_FLOAT_SAMPLES
diff --git a/audio/softsynth/mt32/LegacyWaveGenerator.cpp b/audio/softsynth/mt32/LegacyWaveGenerator.cpp
deleted file mode 100644
index 35ca975..0000000
--- a/audio/softsynth/mt32/LegacyWaveGenerator.cpp
+++ /dev/null
@@ -1,347 +0,0 @@
-/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU Lesser General Public License as published by
- *  the Free Software Foundation, either version 2.1 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-//#include <cmath>
-#include "mt32emu.h"
-#include "mmath.h"
-#include "LegacyWaveGenerator.h"
-
-#if MT32EMU_ACCURATE_WG == 1
-
-namespace MT32Emu {
-
-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) {
-	if (position >= pcmWaveLength) {
-		if (!pcmWaveLooped) {
-			return 0;
-		}
-		position = position % pcmWaveLength;
-	}
-	Bit16s pcmSample = pcmWaveAddress[position];
-	float sampleValue = EXP2F(((pcmSample & 32767) - 32787.0f) / 2048.0f);
-	return ((pcmSample & 32768) == 0) ? sampleValue : -sampleValue;
-}
-
-void LA32WaveGenerator::initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) {
-	this->sawtoothWaveform = sawtoothWaveform;
-	this->pulseWidth = pulseWidth;
-	this->resonance = resonance;
-
-	wavePos = 0.0f;
-	lastFreq = 0.0f;
-
-	pcmWaveAddress = NULL;
-	active = true;
-}
-
-void LA32WaveGenerator::initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated) {
-	this->pcmWaveAddress = pcmWaveAddress;
-	this->pcmWaveLength = pcmWaveLength;
-	this->pcmWaveLooped = pcmWaveLooped;
-	this->pcmWaveInterpolated = pcmWaveInterpolated;
-
-	pcmPosition = 0.0f;
-	active = true;
-}
-
-float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pitch, const Bit32u cutoffRampVal) {
-	if (!active) {
-		return 0.0f;
-	}
-
-	this->amp = amp;
-	this->pitch = pitch;
-
-	float sample = 0.0f;
-
-	// SEMI-CONFIRMED: From sample analysis:
-	// (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc.
-	// This gives results within +/- 2 at the output (before any DAC bitshifting)
-	// when sustaining at levels 156 - 255 with no modifiers.
-	// (2) Tested with a special square wave partial (internal capture ID tva5) at TVA envelope levels 155-255.
-	// This gives deltas between -1 and 0 compared to the real output. Note that this special partial only produces
-	// positive amps, so negative still needs to be explored, as well as lower levels.
-	//
-	// Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing.
-
-	float amp = EXP2F(ampVal / -1024.0f / 4096.0f);
-	float freq = EXP2F(pitch / 4096.0f - 16.0f) * SAMPLE_RATE;
-
-	if (isPCMWave()) {
-		// Render PCM waveform
-		int len = pcmWaveLength;
-		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();
-			return 0.0f;
-		}
-		float positionDelta = freq * 2048.0f / SAMPLE_RATE;
-
-		// Linear interpolation
-		float firstSample = getPCMSample(intPCMPosition);
-		// We observe that for partial structures with ring modulation the interpolation is not applied to the slave PCM partial.
-		// It's assumed that the multiplication circuitry intended to perform the interpolation on the slave PCM partial
-		// is borrowed by the ring modulation circuit (or the LA32 chip has a similar lack of resources assigned to each partial pair).
-		if (pcmWaveInterpolated) {
-			sample = firstSample + (getPCMSample(intPCMPosition + 1) - firstSample) * (pcmPosition - intPCMPosition);
-		} else {
-			sample = firstSample;
-		}
-
-		float newPCMPosition = pcmPosition + positionDelta;
-		if (pcmWaveLooped) {
-			newPCMPosition = fmod(newPCMPosition, (float)pcmWaveLength);
-		}
-		pcmPosition = newPCMPosition;
-	} else {
-		// Render synthesised waveform
-		wavePos *= lastFreq / freq;
-		lastFreq = freq;
-
-		float resAmp = EXP2F(1.0f - (32 - resonance) / 4.0f);
-		{
-			//static const float resAmpFactor = EXP2F(-7);
-			//resAmp = EXP2I(resonance << 10) * resAmpFactor;
-		}
-
-		// The cutoffModifier may not be supposed to be directly added to the cutoff -
-		// it may for example need to be multiplied in some way.
-		// The 240 cutoffVal limit was determined via sample analysis (internal Munt capture IDs: glop3, glop4).
-		// More research is needed to be sure that this is correct, however.
-		float cutoffVal = cutoffRampVal / 262144.0f;
-		if (cutoffVal > MAX_CUTOFF_VALUE) {
-			cutoffVal = MAX_CUTOFF_VALUE;
-		}
-
-		// Wave length in samples
-		float waveLen = SAMPLE_RATE / freq;
-
-		// Init cosineLen
-		float cosineLen = 0.5f * waveLen;
-		if (cutoffVal > MIDDLE_CUTOFF_VALUE) {
-			cosineLen *= EXP2F((cutoffVal - MIDDLE_CUTOFF_VALUE) / -16.0f); // found from sample analysis
-		}
-
-		// Start playing in center of first cosine segment
-		// relWavePos is shifted by a half of cosineLen
-		float relWavePos = wavePos + 0.5f * cosineLen;
-		if (relWavePos > waveLen) {
-			relWavePos -= waveLen;
-		}
-
-		// Ratio of positive segment to wave length
-		float pulseLen = 0.5f;
-		if (pulseWidth > 128) {
-			pulseLen = EXP2F((64 - pulseWidth) / 64.0f);
-			//static const float pulseLenFactor = EXP2F(-192 / 64);
-			//pulseLen = EXP2I((256 - pulseWidthVal) << 6) * pulseLenFactor;
-		}
-		pulseLen *= waveLen;
-
-		float hLen = pulseLen - cosineLen;
-
-		// Ignore pulsewidths too high for given freq
-		if (hLen < 0.0f) {
-			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);
-		}
-
-		// Produce filtered square wave with 2 cosine waves on slopes
-
-		// 1st cosine segment
-		if (relWavePos < cosineLen) {
-			sample = -cos(FLOAT_PI * relWavePos / cosineLen);
-		} else
-
-		// high linear segment
-		if (relWavePos < (cosineLen + hLen)) {
-			sample = 1.f;
-		} else
-
-		// 2nd cosine segment
-		if (relWavePos < (2 * cosineLen + hLen)) {
-			sample = cos(FLOAT_PI * (relWavePos - (cosineLen + hLen)) / cosineLen);
-		} else {
-
-		// low linear segment
-			sample = -1.f;
-		}
-
-		if (cutoffVal < 128.0f) {
-
-			// Attenuate samples below cutoff 50
-			// Found by sample analysis
-			sample *= EXP2F(-0.125f * (128.0f - cutoffVal));
-		} else {
-
-			// Add resonance sine. Effective for cutoff > 50 only
-			float resSample = 1.0f;
-
-			// Resonance decay speed factor
-			float resAmpDecayFactor = Tables::getInstance().resAmpDecayFactor[resonance >> 2];
-
-			// Now relWavePos counts from the middle of first cosine
-			relWavePos = wavePos;
-
-			// negative segments
-			if (!(relWavePos < (cosineLen + hLen))) {
-				resSample = -resSample;
-				relWavePos -= cosineLen + hLen;
-
-				// From the digital captures, the decaying speed of the resonance sine is found a bit different for the positive and the negative segments
-				resAmpDecayFactor += 0.25f;
-			}
-
-			// Resonance sine WG
-			resSample *= sin(FLOAT_PI * relWavePos / cosineLen);
-
-			// Resonance sine amp
-			float resAmpFadeLog2 = -0.125f * resAmpDecayFactor * (relWavePos / cosineLen); // seems to be exact
-			float resAmpFade = EXP2F(resAmpFadeLog2);
-
-			// Now relWavePos set negative to the left from center of any cosine
-			relWavePos = wavePos;
-
-			// negative segment
-			if (!(wavePos < (waveLen - 0.5f * cosineLen))) {
-				relWavePos -= waveLen;
-			} else
-
-			// positive segment
-			if (!(wavePos < (hLen + 0.5f * cosineLen))) {
-				relWavePos -= cosineLen + hLen;
-			}
-
-			// To ensure the output wave has no breaks, two different windows are appied to the beginning and the ending of the resonance sine segment
-			if (relWavePos < 0.5f * cosineLen) {
-				float syncSine = sin(FLOAT_PI * relWavePos / cosineLen);
-				if (relWavePos < 0.0f) {
-					// The window is synchronous square sine here
-					resAmpFade *= syncSine * syncSine;
-				} else {
-					// The window is synchronous sine here
-					resAmpFade *= syncSine;
-				}
-			}
-
-			sample += resSample * resAmp * resAmpFade;
-		}
-
-		// sawtooth waves
-		if (sawtoothWaveform) {
-			sample *= cos(FLOAT_2PI * wavePos / waveLen);
-		}
-
-		wavePos++;
-
-		// wavePos isn't supposed to be > waveLen
-		if (wavePos > waveLen) {
-			wavePos -= waveLen;
-		}
-	}
-
-	// Multiply sample with current TVA value
-	sample *= amp;
-	return sample;
-}
-
-void LA32WaveGenerator::deactivate() {
-	active = false;
-}
-
-bool LA32WaveGenerator::isActive() const {
-	return active;
-}
-
-bool LA32WaveGenerator::isPCMWave() const {
-	return pcmWaveAddress != NULL;
-}
-
-void LA32PartialPair::init(const bool ringModulated, const bool mixed) {
-	this->ringModulated = ringModulated;
-	this->mixed = mixed;
-	masterOutputSample = 0.0f;
-	slaveOutputSample = 0.0f;
-}
-
-void LA32PartialPair::initSynth(const PairType useMaster, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) {
-	if (useMaster == MASTER) {
-		master.initSynth(sawtoothWaveform, pulseWidth, resonance);
-	} else {
-		slave.initSynth(sawtoothWaveform, pulseWidth, resonance);
-	}
-}
-
-void LA32PartialPair::initPCM(const PairType useMaster, const Bit16s *pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped) {
-	if (useMaster == MASTER) {
-		master.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, true);
-	} else {
-		slave.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, !ringModulated);
-	}
-}
-
-void LA32PartialPair::generateNextSample(const PairType useMaster, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff) {
-	if (useMaster == MASTER) {
-		masterOutputSample = master.generateNextSample(amp, pitch, cutoff);
-	} else {
-		slaveOutputSample = slave.generateNextSample(amp, pitch, cutoff);
-	}
-}
-
-Bit16s LA32PartialPair::nextOutSample() {
-	float outputSample;
-	if (ringModulated) {
-		float ringModulatedSample = masterOutputSample * slaveOutputSample;
-		outputSample = mixed ? masterOutputSample + ringModulatedSample : ringModulatedSample;
-	} else {
-		outputSample = masterOutputSample + slaveOutputSample;
-	}
-	return Bit16s(outputSample * 8192.0f);
-}
-
-void LA32PartialPair::deactivate(const PairType useMaster) {
-	if (useMaster == MASTER) {
-		master.deactivate();
-		masterOutputSample = 0.0f;
-	} else {
-		slave.deactivate();
-		slaveOutputSample = 0.0f;
-	}
-}
-
-bool LA32PartialPair::isActive(const PairType useMaster) const {
-	return useMaster == MASTER ? master.isActive() : slave.isActive();
-}
-
-}
-
-#endif // #if MT32EMU_ACCURATE_WG == 1
diff --git a/audio/softsynth/mt32/LegacyWaveGenerator.h b/audio/softsynth/mt32/LegacyWaveGenerator.h
deleted file mode 100644
index 81c1b9c..0000000
--- a/audio/softsynth/mt32/LegacyWaveGenerator.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU Lesser General Public License as published by
- *  the Free Software Foundation, either version 2.1 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#if MT32EMU_ACCURATE_WG == 1
-
-#ifndef MT32EMU_LA32_WAVE_GENERATOR_H
-#define MT32EMU_LA32_WAVE_GENERATOR_H
-
-namespace MT32Emu {
-
-/**
- * LA32WaveGenerator is aimed to represent the exact model of LA32 wave generator.
- * The output square wave is created by adding high / low linear segments in-between
- * the rising and falling cosine segments. Basically, it’s very similar to the phase distortion synthesis.
- * Behaviour of a true resonance filter is emulated by adding decaying sine wave.
- * 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 {
-	//***************************************************************************
-	//  The local copy of partial parameters below
-	//***************************************************************************
-
-	bool active;
-
-	// True means the resulting square wave is to be multiplied by the synchronous cosine
-	bool sawtoothWaveform;
-
-	// Logarithmic amp of the wave generator
-	Bit32u amp;
-
-	// Logarithmic frequency of the resulting wave
-	Bit16u pitch;
-
-	// Values in range [1..31]
-	// Value 1 correspong to the minimum resonance
-	Bit8u resonance;
-
-	// Processed value in range [0..255]
-	// Values in range [0..128] have no effect and the resulting wave remains symmetrical
-	// Value 255 corresponds to the maximum possible asymmetric of the resulting wave
-	Bit8u pulseWidth;
-
-	// Composed of the base cutoff in range [78..178] left-shifted by 18 bits and the TVF modifier
-	Bit32u cutoffVal;
-
-	// Logarithmic PCM sample start address
-	const Bit16s *pcmWaveAddress;
-
-	// Logarithmic PCM sample length
-	Bit32u pcmWaveLength;
-
-	// true for looped logarithmic PCM samples
-	bool pcmWaveLooped;
-
-	// false for slave PCM partials in the structures with the ring modulation
-	bool pcmWaveInterpolated;
-
-	//***************************************************************************
-	// Internal variables below
-	//***************************************************************************
-
-	float wavePos;
-	float lastFreq;
-	float pcmPosition;
-
-	float getPCMSample(unsigned int position);
-
-public:
-	// Initialise the WG engine for generation of synth partial samples and set up the invariant parameters
-	void initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance);
-
-	// Initialise the WG engine for generation of PCM partial samples and set up the invariant parameters
-	void initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated);
-
-	// Update parameters with respect to TVP, TVA and TVF, and generate next sample
-	float generateNextSample(const Bit32u amp, const Bit16u pitch, const Bit32u cutoff);
-
-	// Deactivate the WG engine
-	void deactivate();
-
-	// Return active state of the WG engine
-	bool isActive() const;
-
-	// Return true if the WG engine generates PCM wave samples
-	bool isPCMWave() const;
-};
-
-// LA32PartialPair contains a structure of two partials being mixed / ring modulated
-class LA32PartialPair {
-	LA32WaveGenerator master;
-	LA32WaveGenerator 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
-	void init(const bool ringModulated, const bool mixed);
-
-	// Initialise the WG engine for generation of synth partial samples and set up the invariant parameters
-	void initSynth(const PairType master, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance);
-
-	// Initialise the WG engine for generation of PCM partial samples and set up the invariant parameters
-	void initPCM(const PairType master, const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped);
-
-	// 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
-	Bit16s nextOutSample();
-
-	// Deactivate the WG engine
-	void deactivate(const PairType master);
-
-	// Return active state of the WG engine
-	bool isActive(const PairType master) const;
-};
-
-} // namespace MT32Emu
-
-#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H
-
-#endif // #if MT32EMU_ACCURATE_WG == 1
diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp
index 8840431..8a0daf3 100644
--- a/audio/softsynth/mt32/Part.cpp
+++ b/audio/softsynth/mt32/Part.cpp
@@ -67,18 +67,12 @@ Part::Part(Synth *useSynth, unsigned int usePartNum) {
 	pitchBend = 0;
 	activePartialCount = 0;
 	memset(patchCache, 0, sizeof(patchCache));
-	for (int i = 0; i < MT32EMU_MAX_POLY; i++) {
-		freePolys.prepend(new Poly(this));
-	}
 }
 
 Part::~Part() {
 	while (!activePolys.isEmpty()) {
 		delete activePolys.takeFirst();
 	}
-	while (!freePolys.isEmpty()) {
-		delete freePolys.takeFirst();
-	}
 }
 
 void Part::setDataEntryMSB(unsigned char midiDataEntryMSB) {
@@ -431,23 +425,10 @@ void Part::noteOn(unsigned int midiKey, unsigned int velocity) {
 	playPoly(patchCache, NULL, midiKey, key, velocity);
 }
 
-void Part::abortPoly(Poly *poly) {
-	if (poly->startAbort()) {
-		while (poly->isActive()) {
-			if (!synth->prerender()) {
-				synth->printDebug("%s (%s): Ran out of prerender space to abort poly gracefully", name, currentInstr);
-				poly->terminate();
-				break;
-			}
-		}
-	}
-}
-
 bool Part::abortFirstPoly(unsigned int key) {
 	for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) {
 		if (poly->getKey() == key) {
-			abortPoly(poly);
-			return true;
+			return poly->startAbort();
 		}
 	}
 	return false;
@@ -456,8 +437,7 @@ bool Part::abortFirstPoly(unsigned int key) {
 bool Part::abortFirstPoly(PolyState polyState) {
 	for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) {
 		if (poly->getState() == polyState) {
-			abortPoly(poly);
-			return true;
+			return poly->startAbort();
 		}
 	}
 	return false;
@@ -474,8 +454,7 @@ bool Part::abortFirstPoly() {
 	if (activePolys.isEmpty()) {
 		return false;
 	}
-	abortPoly(activePolys.getFirst());
-	return true;
+	return activePolys.getFirst()->startAbort();
 }
 
 void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhythmTemp, unsigned int midiKey, unsigned int key, unsigned int velocity) {
@@ -489,6 +468,7 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt
 	if ((patchTemp->patch.assignMode & 2) == 0) {
 		// Single-assign mode
 		abortFirstPoly(key);
+		if (synth->isAbortingPoly()) return;
 	}
 
 	if (!synth->partialManager->freePartials(needPartials, partNum)) {
@@ -498,12 +478,13 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt
 #endif
 		return;
 	}
+	if (synth->isAbortingPoly()) return;
 
-	if (freePolys.isEmpty()) {
+	Poly *poly = synth->partialManager->assignPolyToPart(this);
+	if (poly == NULL) {
 		synth->printDebug("%s (%s): No free poly to play key %d (velocity %d)", name, currentInstr, midiKey, velocity);
 		return;
 	}
-	Poly *poly = freePolys.takeFirst();
 	if (patchTemp->patch.assignMode & 1) {
 		// Priority to data first received
 		activePolys.prepend(poly);
@@ -596,6 +577,10 @@ unsigned int Part::getActivePartialCount() const {
 	return activePartialCount;
 }
 
+const Poly *Part::getFirstActivePoly() const {
+	return activePolys.getFirst();
+}
+
 unsigned int Part::getActiveNonReleasingPartialCount() const {
 	unsigned int activeNonReleasingPartialCount = 0;
 	for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) {
@@ -606,11 +591,15 @@ unsigned int Part::getActiveNonReleasingPartialCount() const {
 	return activeNonReleasingPartialCount;
 }
 
+Synth *Part::getSynth() const {
+	return synth;
+}
+
 void Part::partialDeactivated(Poly *poly) {
 	activePartialCount--;
 	if (!poly->isActive()) {
 		activePolys.remove(poly);
-		freePolys.prepend(poly);
+		synth->partialManager->polyFreed(poly);
 		synth->polyStateChanged(partNum);
 	}
 }
diff --git a/audio/softsynth/mt32/Part.h b/audio/softsynth/mt32/Part.h
index b658588..2aeeaf5 100644
--- a/audio/softsynth/mt32/Part.h
+++ b/audio/softsynth/mt32/Part.h
@@ -51,13 +51,11 @@ private:
 
 	unsigned int activePartialCount;
 	PatchCache patchCache[4];
-	PolyList freePolys;
 	PolyList activePolys;
 
 	void setPatch(const PatchParam *patch);
 	unsigned int midiKeyToKey(unsigned int midiKey);
 
-	void abortPoly(Poly *poly);
 	bool abortFirstPoly(unsigned int key);
 
 protected:
@@ -110,8 +108,10 @@ public:
 	virtual void setTimbre(TimbreParam *timbre);
 	virtual unsigned int getAbsTimbreNum() const;
 	const char *getCurrentInstr() const;
+	const Poly *getFirstActivePoly() const;
 	unsigned int getActivePartialCount() const;
 	unsigned int getActiveNonReleasingPartialCount() const;
+	Synth *getSynth() const;
 
 	const MemParams::PatchTemp *getPatchTemp() const;
 
diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp
index b80a028..c7848f0 100644
--- a/audio/softsynth/mt32/Partial.cpp
+++ b/audio/softsynth/mt32/Partial.cpp
@@ -131,6 +131,7 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us
 	}
 
 	// FIXME: Sample analysis suggests that the use of panVal is linear, but there are some some quirks that still need to be resolved.
+	// FIXME: I suppose this should be panVal / 8 and undoubtly integer, clarify ASAP
 	stereoVolume.leftVol = panVal / 7.0f;
 	stereoVolume.rightVol = 1.0f - stereoVolume.leftVol;
 
@@ -198,9 +199,6 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us
 	if (!hasRingModulatingSlave()) {
 		la32Pair.deactivate(LA32PartialPair::SLAVE);
 	}
-	// Temporary integration hack
-	stereoVolume.leftVol /= 8192.0f;
-	stereoVolume.rightVol /= 8192.0f;
 }
 
 Bit32u Partial::getAmpValue() {
@@ -232,7 +230,7 @@ Bit32u Partial::getCutoffValue() {
 	return (tvf->getBaseCutoff() << 18) + cutoffModifierRampVal;
 }
 
-unsigned long Partial::generateSamples(Bit16s *partialBuf, unsigned long length) {
+unsigned long Partial::generateSamples(Sample *partialBuf, unsigned long length) {
 	if (!isActive() || alreadyOutputed) {
 		return 0;
 	}
@@ -258,7 +256,7 @@ unsigned long Partial::generateSamples(Bit16s *partialBuf, unsigned long length)
 				}
 			}
 		}
-		*partialBuf++ = la32Pair.nextOutSample();
+		*(partialBuf++) = la32Pair.nextOutSample();
 	}
 	unsigned long renderedSamples = sampleNum;
 	sampleNum = 0;
@@ -288,7 +286,18 @@ Synth *Partial::getSynth() const {
 	return synth;
 }
 
-bool Partial::produceOutput(float *leftBuf, float *rightBuf, unsigned long length) {
+TVA *Partial::getTVA() const {
+	return tva;
+}
+
+void Partial::backupCache(const PatchCache &cache) {
+	if (patchCache == &cache) {
+		cachebackup = cache;
+		patchCache = &cachebackup;
+	}
+}
+
+bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long length) {
 	if (!isActive() || alreadyOutputed || isRingModulatingSlave()) {
 		return false;
 	}
@@ -296,14 +305,18 @@ bool Partial::produceOutput(float *leftBuf, float *rightBuf, unsigned long lengt
 		synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::produceOutput()!", debugPartialNum);
 		return false;
 	}
-	unsigned long numGenerated = generateSamples(myBuffer, length);
+	Sample buffer[MAX_SAMPLES_PER_RUN];
+	unsigned long numGenerated = generateSamples(buffer, length);
 	for (unsigned int i = 0; i < numGenerated; i++) {
-		*leftBuf++ = myBuffer[i] * stereoVolume.leftVol;
-		*rightBuf++ = myBuffer[i] * stereoVolume.rightVol;
-	}
-	for (; numGenerated < length; numGenerated++) {
-		*leftBuf++ = 0.0f;
-		*rightBuf++ = 0.0f;
+#if MT32EMU_USE_FLOAT_SAMPLES
+		*(leftBuf++) += buffer[i] * stereoVolume.leftVol;
+		*(rightBuf++) += buffer[i] * stereoVolume.rightVol;
+#else
+		*leftBuf = Synth::clipBit16s((Bit32s)*leftBuf + Bit32s(buffer[i] * stereoVolume.leftVol));
+		*rightBuf = Synth::clipBit16s((Bit32s)*rightBuf + Bit32s(buffer[i] * stereoVolume.rightVol));
+		leftBuf++;
+		rightBuf++;
+#endif
 	}
 	return true;
 }
diff --git a/audio/softsynth/mt32/Partial.h b/audio/softsynth/mt32/Partial.h
index 21b1bfe..358cb9d 100644
--- a/audio/softsynth/mt32/Partial.h
+++ b/audio/softsynth/mt32/Partial.h
@@ -44,8 +44,6 @@ private:
 	int structurePosition; // 0 or 1 of a structure pair
 	StereoVolume stereoVolume;
 
-	Bit16s myBuffer[MAX_SAMPLES_PER_RUN];
-
 	// Only used for PCM partials
 	int pcmNum;
 	// FIXME: Give this a better name (e.g. pcmWaveInfo)
@@ -56,6 +54,11 @@ private:
 	int pulseWidthVal;
 
 	Poly *poly;
+	Partial *pair;
+
+	TVA *tva;
+	TVP *tvp;
+	TVF *tvf;
 
 	LA32Ramp ampRamp;
 	LA32Ramp cutoffModifierRamp;
@@ -63,18 +66,13 @@ private:
 	// TODO: This should be owned by PartialPair
 	LA32PartialPair la32Pair;
 
+	const PatchCache *patchCache;
+	PatchCache cachebackup;
+
 	Bit32u getAmpValue();
 	Bit32u getCutoffValue();
 
 public:
-	const PatchCache *patchCache;
-	TVA *tva;
-	TVP *tvp;
-	TVF *tvf;
-
-	PatchCache cachebackup;
-
-	Partial *pair;
 	bool alreadyOutputed;
 
 	Partial(Synth *synth, int debugPartialNum);
@@ -97,14 +95,17 @@ public:
 	bool isPCM() const;
 	const ControlROMPCMStruct *getControlROMPCMStruct() const;
 	Synth *getSynth() const;
+	TVA *getTVA() const;
+
+	void backupCache(const PatchCache &cache);
 
 	// Returns true only if data written to buffer
 	// This function (unlike the one below it) returns processed stereo samples
 	// made from combining this single partial with its pair, if it has one.
-	bool produceOutput(float *leftBuf, float *rightBuf, unsigned long length);
+	bool produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long length);
 
 	// This function writes mono sample output to the provided buffer, and returns the number of samples written
-	unsigned long generateSamples(Bit16s *partialBuf, unsigned long length);
+	unsigned long generateSamples(Sample *partialBuf, unsigned long length);
 };
 
 }
diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp
index 436e7a3..905b5b8 100644
--- a/audio/softsynth/mt32/PartialManager.cpp
+++ b/audio/softsynth/mt32/PartialManager.cpp
@@ -25,19 +25,26 @@ namespace MT32Emu {
 PartialManager::PartialManager(Synth *useSynth, Part **useParts) {
 	synth = useSynth;
 	parts = useParts;
-	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+	partialTable = new Partial *[synth->getPartialCount()];
+	freePolys = new Poly *[synth->getPartialCount()];
+	firstFreePolyIndex = 0;
+	for (unsigned int i = 0; i < synth->getPartialCount(); i++) {
 		partialTable[i] = new Partial(synth, i);
+		freePolys[i] = new Poly();
 	}
 }
 
 PartialManager::~PartialManager(void) {
-	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+	for (unsigned int i = 0; i < synth->getPartialCount(); i++) {
 		delete partialTable[i];
+		if (freePolys[i] != NULL) delete freePolys[i];
 	}
+	delete[] partialTable;
+	delete[] freePolys;
 }
 
 void PartialManager::clearAlreadyOutputed() {
-	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+	for (unsigned int i = 0; i < synth->getPartialCount(); i++) {
 		partialTable[i]->alreadyOutputed = false;
 	}
 }
@@ -46,12 +53,12 @@ bool PartialManager::shouldReverb(int i) {
 	return partialTable[i]->shouldReverb();
 }
 
-bool PartialManager::produceOutput(int i, float *leftBuf, float *rightBuf, Bit32u bufferLength) {
+bool PartialManager::produceOutput(int i, Sample *leftBuf, Sample *rightBuf, Bit32u bufferLength) {
 	return partialTable[i]->produceOutput(leftBuf, rightBuf, bufferLength);
 }
 
 void PartialManager::deactivateAll() {
-	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+	for (unsigned int i = 0; i < synth->getPartialCount(); i++) {
 		partialTable[i]->deactivate();
 	}
 }
@@ -69,7 +76,7 @@ Partial *PartialManager::allocPartial(int partNum) {
 	Partial *outPartial = NULL;
 
 	// Get the first inactive partial
-	for (int partialNum = 0; partialNum < MT32EMU_MAX_PARTIALS; partialNum++) {
+	for (unsigned int partialNum = 0; partialNum < synth->getPartialCount(); partialNum++) {
 		if (!partialTable[partialNum]->isActive()) {
 			outPartial = partialTable[partialNum];
 			break;
@@ -83,7 +90,7 @@ Partial *PartialManager::allocPartial(int partNum) {
 
 unsigned int PartialManager::getFreePartialCount(void) {
 	int count = 0;
-	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+	for (unsigned int i = 0; i < synth->getPartialCount(); i++) {
 		if (!partialTable[i]->isActive()) {
 			count++;
 		}
@@ -94,7 +101,7 @@ unsigned int PartialManager::getFreePartialCount(void) {
 // This function is solely used to gather data for debug output at the moment.
 void PartialManager::getPerPartPartialUsage(unsigned int perPartPartialUsage[9]) {
 	memset(perPartPartialUsage, 0, 9 * sizeof(unsigned int));
-	for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+	for (unsigned int i = 0; i < synth->getPartialCount(); i++) {
 		if (partialTable[i]->isActive()) {
 			perPartPartialUsage[partialTable[i]->getOwnerPart()]++;
 		}
@@ -189,7 +196,7 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) {
 			break;
 		}
 #endif
-		if (getFreePartialCount() >= needed) {
+		if (synth->isAbortingPoly() || getFreePartialCount() >= needed) {
 			return true;
 		}
 	}
@@ -206,7 +213,7 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) {
 			if (!abortFirstPolyPreferHeldWhereReserveExceeded(partNum)) {
 				break;
 			}
-			if (getFreePartialCount() >= needed) {
+			if (synth->isAbortingPoly() || getFreePartialCount() >= needed) {
 				return true;
 			}
 		}
@@ -222,7 +229,7 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) {
 			if (!abortFirstPolyPreferHeldWhereReserveExceeded(-1)) {
 				break;
 			}
-			if (getFreePartialCount() >= needed) {
+			if (synth->isAbortingPoly() || getFreePartialCount() >= needed) {
 				return true;
 			}
 		}
@@ -233,7 +240,7 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) {
 		if (!parts[partNum]->abortFirstPolyPreferHeld()) {
 			break;
 		}
-		if (getFreePartialCount() >= needed) {
+		if (synth->isAbortingPoly() || getFreePartialCount() >= needed) {
 			return true;
 		}
 	}
@@ -243,10 +250,39 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) {
 }
 
 const Partial *PartialManager::getPartial(unsigned int partialNum) const {
-	if (partialNum > MT32EMU_MAX_PARTIALS - 1) {
+	if (partialNum > synth->getPartialCount() - 1) {
 		return NULL;
 	}
 	return partialTable[partialNum];
 }
 
+Poly *PartialManager::assignPolyToPart(Part *part) {
+	if (firstFreePolyIndex < synth->getPartialCount()) {
+		Poly *poly = freePolys[firstFreePolyIndex];
+		freePolys[firstFreePolyIndex] = NULL;
+		firstFreePolyIndex++;
+		poly->setPart(part);
+		return poly;
+	}
+	return NULL;
+}
+
+void PartialManager::polyFreed(Poly *poly) {
+	if (0 == firstFreePolyIndex) {
+		synth->printDebug("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;
+			while (activePoly != NULL) {
+				activePoly->getNext();
+				polyCount++;
+			}
+			synth->printDebug("Part: %i, active poly count: %i\n", partNum, polyCount);
+		}
+	}
+	poly->setPart(NULL);
+	firstFreePolyIndex--;
+	freePolys[firstFreePolyIndex] = poly;
+}
+
 }
diff --git a/audio/softsynth/mt32/PartialManager.h b/audio/softsynth/mt32/PartialManager.h
index a1c9266..229b6e8 100644
--- a/audio/softsynth/mt32/PartialManager.h
+++ b/audio/softsynth/mt32/PartialManager.h
@@ -24,17 +24,17 @@ class Synth;
 
 class PartialManager {
 private:
-	Synth *synth; // Only used for sending debug output
+	Synth *synth;
 	Part **parts;
-
-	Partial *partialTable[MT32EMU_MAX_PARTIALS];
+	Poly **freePolys;
+	Partial **partialTable;
 	Bit8u numReservedPartialsForPart[9];
+	Bit32u firstFreePolyIndex;
 
 	bool abortFirstReleasingPolyWhereReserveExceeded(int minPart);
 	bool abortFirstPolyPreferHeldWhereReserveExceeded(int minPart);
 
 public:
-
 	PartialManager(Synth *synth, Part **parts);
 	~PartialManager();
 	Partial *allocPartial(int partNum);
@@ -43,10 +43,12 @@ public:
 	bool freePartials(unsigned int needed, int partNum);
 	unsigned int setReserve(Bit8u *rset);
 	void deactivateAll();
-	bool produceOutput(int i, float *leftBuf, float *rightBuf, Bit32u bufferLength);
+	bool produceOutput(int i, Sample *leftBuf, Sample *rightBuf, Bit32u bufferLength);
 	bool shouldReverb(int i);
 	void clearAlreadyOutputed();
 	const Partial *getPartial(unsigned int partialNum) const;
+	Poly *assignPolyToPart(Part *part);
+	void polyFreed(Poly *poly);
 };
 
 }
diff --git a/audio/softsynth/mt32/Poly.cpp b/audio/softsynth/mt32/Poly.cpp
index 46574f8..1554881 100644
--- a/audio/softsynth/mt32/Poly.cpp
+++ b/audio/softsynth/mt32/Poly.cpp
@@ -19,8 +19,8 @@
 
 namespace MT32Emu {
 
-Poly::Poly(Part *usePart) {
-	part = usePart;
+Poly::Poly() {
+	part = NULL;
 	key = 255;
 	velocity = 255;
 	sustain = false;
@@ -32,10 +32,21 @@ Poly::Poly(Part *usePart) {
 	next = NULL;
 }
 
+void Poly::setPart(Part *usePart) {
+	part = usePart;
+}
+
 void Poly::reset(unsigned int newKey, unsigned int newVelocity, bool newSustain, Partial **newPartials) {
 	if (isActive()) {
-		// FIXME: Throw out some big ugly debug output with a lot of exclamation marks - we should never get here
-		terminate();
+		// This should never happen
+		part->getSynth()->printDebug("Resetting active poly. Active partial count: %i\n", activePartialCount);
+		for (int i = 0; i < 4; i++) {
+			if (partials[i] != NULL && partials[i]->isActive()) {
+				partials[i]->deactivate();
+				activePartialCount--;
+			}
+		}
+		state = POLY_Inactive;
 	}
 
 	key = newKey;
@@ -92,41 +103,24 @@ bool Poly::startDecay() {
 }
 
 bool Poly::startAbort() {
-	if (state == POLY_Inactive) {
+	if (state == POLY_Inactive || part->getSynth()->isAbortingPoly()) {
 		return false;
 	}
 	for (int t = 0; t < 4; t++) {
 		Partial *partial = partials[t];
 		if (partial != NULL) {
 			partial->startAbort();
+			part->getSynth()->abortingPoly = this;
 		}
 	}
 	return true;
 }
 
-void Poly::terminate() {
-	if (state == POLY_Inactive) {
-		return;
-	}
-	for (int t = 0; t < 4; t++) {
-		Partial *partial = partials[t];
-		if (partial != NULL) {
-			partial->deactivate();
-		}
-	}
-	if (state != POLY_Inactive) {
-		// FIXME: Throw out lots of debug output - this should never happen
-		// (Deactivating the partials above should've made them each call partialDeactivated(), ultimately changing the state to POLY_Inactive)
-		state = POLY_Inactive;
-	}
-}
-
 void Poly::backupCacheToPartials(PatchCache cache[4]) {
 	for (int partialNum = 0; partialNum < 4; partialNum++) {
 		Partial *partial = partials[partialNum];
-		if (partial != NULL && partial->patchCache == &cache[partialNum]) {
-			partial->cachebackup = cache[partialNum];
-			partial->patchCache = &partial->cachebackup;
+		if (partial != NULL) {
+			partial->backupCache(cache[partialNum]);
 		}
 	}
 }
@@ -171,11 +165,14 @@ void Poly::partialDeactivated(Partial *partial) {
 	}
 	if (activePartialCount == 0) {
 		state = POLY_Inactive;
+		if (part->getSynth()->abortingPoly == this) {
+			part->getSynth()->abortingPoly = NULL;
+		}
 	}
 	part->partialDeactivated(this);
 }
 
-Poly *Poly::getNext() {
+Poly *Poly::getNext() const {
 	return next;
 }
 
diff --git a/audio/softsynth/mt32/Poly.h b/audio/softsynth/mt32/Poly.h
index 068cf73..33abc35 100644
--- a/audio/softsynth/mt32/Poly.h
+++ b/audio/softsynth/mt32/Poly.h
@@ -44,13 +44,13 @@ private:
 	Poly *next;
 
 public:
-	Poly(Part *part);
+	Poly();
+	void setPart(Part *usePart);
 	void reset(unsigned int key, unsigned int velocity, bool sustain, Partial **partials);
 	bool noteOff(bool pedalHeld);
 	bool stopPedalHold();
 	bool startDecay();
 	bool startAbort();
-	void terminate();
 
 	void backupCacheToPartials(PatchCache cache[4]);
 
@@ -63,7 +63,7 @@ public:
 
 	void partialDeactivated(Partial *partial);
 
-	Poly *getNext();
+	Poly *getNext() const;
 	void setNext(Poly *poly);
 };
 
diff --git a/audio/softsynth/mt32/Structures.h b/audio/softsynth/mt32/Structures.h
index 43d2d1f..421e427 100644
--- a/audio/softsynth/mt32/Structures.h
+++ b/audio/softsynth/mt32/Structures.h
@@ -38,6 +38,12 @@ typedef   signed short int Bit16s;
 typedef unsigned char      Bit8u;
 typedef   signed char      Bit8s;
 
+#if MT32EMU_USE_FLOAT_SAMPLES
+typedef float Sample;
+#else
+typedef Bit16s Sample;
+#endif
+
 // The following structures represent the MT-32's memory
 // Since sysex allows this memory to be written to in blocks of bytes,
 // we keep this packed so that we can copy data into the various
diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp
index 1e1be06..b76dc58 100644
--- a/audio/softsynth/mt32/Synth.cpp
+++ b/audio/softsynth/mt32/Synth.cpp
@@ -26,15 +26,7 @@
 #include "mt32emu.h"
 #include "mmath.h"
 #include "PartialManager.h"
-
-#if MT32EMU_USE_REVERBMODEL == 1
-#include "AReverbModel.h"
-#elif MT32EMU_USE_REVERBMODEL == 2
 #include "BReverbModel.h"
-#else
-#include "FreeverbModel.h"
-#endif
-#include "DelayReverb.h"
 
 namespace MT32Emu {
 
@@ -50,84 +42,22 @@ static const ControlROMMap ControlROMMaps[7] = {
 	// (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp)
 };
 
-static inline Bit16s *streamOffset(Bit16s *stream, Bit32u pos) {
-	return stream == NULL ? NULL : stream + pos;
-}
+static inline void muteStream(Sample *stream, Bit32u len) {
+	if (stream == NULL) return;
 
-static inline void clearIfNonNull(Bit16s *stream, Bit32u len) {
-	if (stream != NULL) {
-		memset(stream, 0, len * sizeof(Bit16s));
-	}
-}
-
-static inline void mix(float *target, const float *stream, Bit32u len) {
-	while (len--) {
-		*target += *stream;
-		stream++;
-		target++;
-	}
-}
-
-static inline void clearFloats(float *leftBuf, float *rightBuf, Bit32u len) {
+#if MT32EMU_USE_FLOAT_SAMPLES
 	// FIXME: Use memset() where compatibility is guaranteed (if this turns out to be a win)
 	while (len--) {
-		*leftBuf++ = 0.0f;
-		*rightBuf++ = 0.0f;
-	}
-}
-
-static inline Bit16s clipBit16s(Bit32s a) {
-	// Clamp values above 32767 to 32767, and values below -32768 to -32768
-	if ((a + 32768) & ~65535) {
-		return (a >> 31) ^ 32767;
-	}
-	return a;
-}
-
-static void floatToBit16s_nice(Bit16s *target, const float *source, Bit32u len, float outputGain) {
-	float gain = outputGain * 16384.0f;
-	while (len--) {
-		// Since we're not shooting for accuracy here, don't worry about the rounding mode.
-		*target = clipBit16s((Bit32s)(*source * gain));
-		source++;
-		target++;
-	}
-}
-
-static void floatToBit16s_pure(Bit16s *target, const float *source, Bit32u len, float /*outputGain*/) {
-	while (len--) {
-		*target = clipBit16s((Bit32s)floor(*source * 8192.0f));
-		source++;
-		target++;
-	}
-}
-
-static void floatToBit16s_reverb(Bit16s *target, const float *source, Bit32u len, float outputGain) {
-	float gain = outputGain * 8192.0f;
-	while (len--) {
-		*target = clipBit16s((Bit32s)floor(*source * gain));
-		source++;
-		target++;
-	}
-}
-
-static void floatToBit16s_generation1(Bit16s *target, const float *source, Bit32u len, float outputGain) {
-	float gain = outputGain * 8192.0f;
-	while (len--) {
-		*target = clipBit16s((Bit32s)floor(*source * gain));
-		*target = (*target & 0x8000) | ((*target << 1) & 0x7FFE);
-		source++;
-		target++;
+		*stream++ = 0.0f;
 	}
+#else
+	memset(stream, 0, len * sizeof(Sample));
+#endif
 }
 
-static void floatToBit16s_generation2(Bit16s *target, const float *source, Bit32u len, float outputGain) {
-	float gain = outputGain * 8192.0f;
-	while (len--) {
-		*target = clipBit16s((Bit32s)floor(*source * gain));
-		*target = (*target & 0x8000) | ((*target << 1) & 0x7FFE) | ((*target >> 14) & 0x0001);
-		source++;
-		target++;
+static inline void advanceStreamPosition(Sample *&stream, Bit32u posDelta) {
+	if (stream != NULL) {
+		stream += posDelta;
 	}
 }
 
@@ -155,28 +85,19 @@ Synth::Synth(ReportHandler *useReportHandler) {
 		isDefaultReportHandler = false;
 	}
 
-#if MT32EMU_USE_REVERBMODEL == 1
-	reverbModels[REVERB_MODE_ROOM] = new AReverbModel(REVERB_MODE_ROOM);
-	reverbModels[REVERB_MODE_HALL] = new AReverbModel(REVERB_MODE_HALL);
-	reverbModels[REVERB_MODE_PLATE] = new AReverbModel(REVERB_MODE_PLATE);
-	reverbModels[REVERB_MODE_TAP_DELAY] = new DelayReverb();
-#elif MT32EMU_USE_REVERBMODEL == 2
 	reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM);
 	reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL);
 	reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE);
 	reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY);
-#else
-	reverbModels[REVERB_MODE_ROOM] = new FreeverbModel(0.76f, 0.687770909f, 0.63f, 0, 0.5f);
-	reverbModels[REVERB_MODE_HALL] = new FreeverbModel(2.0f, 0.712025098f, 0.86f, 1, 0.5f);
-	reverbModels[REVERB_MODE_PLATE] = new FreeverbModel(0.4f, 0.939522749f, 0.38f, 2, 0.05f);
-	reverbModels[REVERB_MODE_TAP_DELAY] = new DelayReverb();
-#endif
 
 	reverbModel = NULL;
 	setDACInputMode(DACInputMode_NICE);
+	setMIDIDelayMode(MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY);
 	setOutputGain(1.0f);
-	setReverbOutputGain(0.68f);
+	setReverbOutputGain(1.0f);
 	partialManager = NULL;
+	midiQueue = NULL;
+	lastReceivedMIDIEventTimestamp = 0;
 	memset(parts, 0, sizeof(parts));
 	renderedSampleCount = 0;
 }
@@ -197,8 +118,8 @@ void ReportHandler::showLCDMessage(const char *data) {
 }
 
 void ReportHandler::printDebug(const char *fmt, va_list list) {
-		vprintf(fmt, list);
-		printf("\n");
+	vprintf(fmt, list);
+	printf("\n");
 }
 
 void Synth::polyStateChanged(int partNum) {
@@ -236,35 +157,37 @@ bool Synth::isReverbOverridden() const {
 }
 
 void Synth::setDACInputMode(DACInputMode mode) {
-	switch(mode) {
-	case DACInputMode_GENERATION1:
-		la32FloatToBit16sFunc = floatToBit16s_generation1;
-		reverbFloatToBit16sFunc = floatToBit16s_reverb;
-		break;
-	case DACInputMode_GENERATION2:
-		la32FloatToBit16sFunc = floatToBit16s_generation2;
-		reverbFloatToBit16sFunc = floatToBit16s_reverb;
-		break;
-	case DACInputMode_PURE:
-		la32FloatToBit16sFunc = floatToBit16s_pure;
-		reverbFloatToBit16sFunc = floatToBit16s_pure;
-		break;
-	case DACInputMode_NICE:
-	default:
-		la32FloatToBit16sFunc = floatToBit16s_nice;
-		reverbFloatToBit16sFunc = floatToBit16s_reverb;
-		break;
-	}
+	dacInputMode = mode;
+}
+
+DACInputMode Synth::getDACInputMode() const {
+	return dacInputMode;
+}
+
+void Synth::setMIDIDelayMode(MIDIDelayMode mode) {
+	midiDelayMode = mode;
+}
+
+MIDIDelayMode Synth::getMIDIDelayMode() const {
+	return midiDelayMode;
 }
 
 void Synth::setOutputGain(float newOutputGain) {
 	outputGain = newOutputGain;
 }
 
+float Synth::getOutputGain() const {
+	return outputGain;
+}
+
 void Synth::setReverbOutputGain(float newReverbOutputGain) {
 	reverbOutputGain = newReverbOutputGain;
 }
 
+float Synth::getReverbOutputGain() const {
+	return reverbOutputGain;
+}
+
 bool Synth::loadControlROM(const ROMImage &controlROMImage) {
 	if (&controlROMImage == NULL) return false;
 	Common::File *file = controlROMImage.getFile();
@@ -343,9 +266,9 @@ bool Synth::loadPCMROM(const ROMImage &pcmROMImage) {
 bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) {
 	ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress];
 	for (int i = 0; i < count; i++) {
-		size_t rAddr = tps[i].pos * 0x800;
-		size_t rLenExp = (tps[i].len & 0x70) >> 4;
-		size_t rLen = 0x800 << rLenExp;
+		Bit32u rAddr = tps[i].pos * 0x800;
+		Bit32u rLenExp = (tps[i].len & 0x70) >> 4;
+		Bit32u rLen = 0x800 << rLenExp;
 		if (rAddr + rLen > pcmROMSize) {
 			printDebug("Control ROM error: Wave map entry %d points to invalid PCM address 0x%04X, length 0x%04X", i, rAddr, rLen);
 			return false;
@@ -407,17 +330,18 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTi
 	return true;
 }
 
-bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage) {
+bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount) {
 	if (isOpen) {
 		return false;
 	}
-	prerenderReadIx = prerenderWriteIx = 0;
+	partialCount = usePartialCount;
+	abortingPoly = NULL;
 #if MT32EMU_MONITOR_INIT
 	printDebug("Initialising Constant Tables");
 #endif
 #if !MT32EMU_REDUCE_REVERB_MEMORY
-	for (int i = 0; i < 4; i++) {
-		reverbModels[i]->open(useProp.sampleRate);
+	for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) {
+		reverbModels[i]->open();
 	}
 #endif
 
@@ -554,6 +478,8 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage) {
 	// For resetting mt32 mid-execution
 	mt32default = mt32ram;
 
+	midiQueue = new MidiEventQueue();
+
 	isOpen = true;
 	isEnabled = false;
 
@@ -568,6 +494,9 @@ void Synth::close() {
 		return;
 	}
 
+	delete midiQueue;
+	midiQueue = NULL;
+
 	delete partialManager;
 	partialManager = NULL;
 
@@ -588,12 +517,78 @@ void Synth::close() {
 	isOpen = false;
 }
 
-void Synth::playMsg(Bit32u msg) {
+void Synth::flushMIDIQueue() {
+	if (midiQueue != NULL) {
+		for (;;) {
+			const MidiEvent *midiEvent = midiQueue->peekMidiEvent();
+			if (midiEvent == NULL) break;
+			if (midiEvent->sysexData == NULL) {
+				playMsgNow(midiEvent->shortMessageData);
+			} else {
+				playSysexNow(midiEvent->sysexData, midiEvent->sysexLength);
+			}
+			midiQueue->dropMidiEvent();
+		}
+		lastReceivedMIDIEventTimestamp = renderedSampleCount;
+	}
+}
+
+void Synth::setMIDIEventQueueSize(Bit32u useSize) {
+	if (midiQueue != NULL) {
+		flushMIDIQueue();
+		delete midiQueue;
+		midiQueue = new MidiEventQueue(useSize);
+	}
+}
+
+Bit32u Synth::getShortMessageLength(Bit32u msg) {
+	if ((msg & 0xF0) == 0xF0) return 1;
+	// NOTE: This calculation isn't quite correct
+	// as it doesn't consider the running status byte
+	return ((msg & 0xE0) == 0xC0) ? 2 : 3;
+}
+
+Bit32u Synth::addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp) {
+	Bit32u transferTime =  Bit32u((double)len * MIDI_DATA_TRANSFER_RATE);
+	// Dealing with wrapping
+	if (Bit32s(timestamp - lastReceivedMIDIEventTimestamp) < 0) {
+		timestamp = lastReceivedMIDIEventTimestamp;
+	}
+	timestamp += transferTime;
+	lastReceivedMIDIEventTimestamp = timestamp;
+	return timestamp;
+}
+
+bool Synth::playMsg(Bit32u msg) {
+	return playMsg(msg, renderedSampleCount);
+}
+
+bool Synth::playMsg(Bit32u msg, Bit32u timestamp) {
+	if (midiQueue == NULL) return false;
+	if (midiDelayMode != MIDIDelayMode_IMMEDIATE) {
+		timestamp = addMIDIInterfaceDelay(getShortMessageLength(msg), timestamp);
+	}
+	return midiQueue->pushShortMessage(msg, timestamp);
+}
+
+bool Synth::playSysex(const Bit8u *sysex, Bit32u len) {
+	return playSysex(sysex, len, renderedSampleCount);
+}
+
+bool Synth::playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp) {
+	if (midiQueue == NULL) return false;
+	if (midiDelayMode == MIDIDelayMode_DELAY_ALL) {
+		timestamp = addMIDIInterfaceDelay(len, timestamp);
+	}
+	return midiQueue->pushSysex(sysex, len, timestamp);
+}
+
+void Synth::playMsgNow(Bit32u msg) {
 	// FIXME: Implement active sensing
 	unsigned char code     = (unsigned char)((msg & 0x0000F0) >> 4);
 	unsigned char chan     = (unsigned char)(msg & 0x00000F);
-	unsigned char note     = (unsigned char)((msg & 0x00FF00) >> 8);
-	unsigned char velocity = (unsigned char)((msg & 0xFF0000) >> 16);
+	unsigned char note     = (unsigned char)((msg & 0x007F00) >> 8);
+	unsigned char velocity = (unsigned char)((msg & 0x7F0000) >> 16);
 	isEnabled = true;
 
 	//printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
@@ -606,11 +601,6 @@ void Synth::playMsg(Bit32u msg) {
 		return;
 	}
 	playMsgOnPart(part, code, note, velocity);
-
-	// This ensures minimum 1-sample delay between sequential MIDI events
-	// Without this, a sequence of NoteOn and immediately succeeding NoteOff messages is always silent
-	// Technically, it's also impossible to send events through the MIDI interface faster than about each millisecond
-	prerender();
 }
 
 void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) {
@@ -692,7 +682,7 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char
 #if MT32EMU_MONITOR_MIDI > 0
 			printDebug("Unknown MIDI Control code: 0x%02x - vel 0x%02x", note, velocity);
 #endif
-			break;
+			return;
 		}
 
 		break;
@@ -709,13 +699,12 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char
 #if MT32EMU_MONITOR_MIDI > 0
 		printDebug("Unknown Midi code: 0x%01x - %02x - %02x", code, note, velocity);
 #endif
-		break;
+		return;
 	}
-
-	//midiOutShortMsg(m_out, msg);
+	reportHandler->onMIDIMessagePlayed();
 }
 
-void Synth::playSysex(const Bit8u *sysex, Bit32u len) {
+void Synth::playSysexNow(const Bit8u *sysex, Bit32u len) {
 	if (len < 2) {
 		printDebug("playSysex: Message is too short for sysex (%d bytes)", len);
 	}
@@ -810,6 +799,7 @@ void Synth::readSysex(unsigned char /*device*/, const Bit8u * /*sysex*/, Bit32u
 }
 
 void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) {
+	reportHandler->onMIDIMessagePlayed();
 	Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]);
 	addr = MT32EMU_MEMADDR(addr);
 	sysex += 3;
@@ -1232,7 +1222,7 @@ void Synth::refreshSystemReverbParameters() {
 	reportHandler->onNewReverbTime(mt32ram.system.reverbTime);
 	reportHandler->onNewReverbLevel(mt32ram.system.reverbLevel);
 
-	ReverbModel *newReverbModel = reverbModels[mt32ram.system.reverbMode];
+	BReverbModel *newReverbModel = reverbModels[mt32ram.system.reverbMode];
 #if MT32EMU_REDUCE_REVERB_MEMORY
 	if (reverbModel != newReverbModel) {
 		if (reverbModel != NULL) {
@@ -1308,179 +1298,226 @@ void Synth::reset() {
 	isEnabled = false;
 }
 
-void Synth::render(Bit16s *stream, Bit32u len) {
-	if (!isEnabled) {
-		memset(stream, 0, len * sizeof(Bit16s) * 2);
-		return;
+MidiEvent::~MidiEvent() {
+	if (sysexData != NULL) {
+		delete[] sysexData;
 	}
-	while (len > 0) {
-		Bit32u thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
-		renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisLen);
-		for (Bit32u i = 0; i < thisLen; i++) {
-			stream[0] = clipBit16s((Bit32s)tmpNonReverbLeft[i] + (Bit32s)tmpReverbDryLeft[i] + (Bit32s)tmpReverbWetLeft[i]);
-			stream[1] = clipBit16s((Bit32s)tmpNonReverbRight[i] + (Bit32s)tmpReverbDryRight[i] + (Bit32s)tmpReverbWetRight[i]);
-			stream += 2;
-		}
-		len -= thisLen;
+}
+
+void MidiEvent::setShortMessage(Bit32u useShortMessageData, Bit32u useTimestamp) {
+	if (sysexData != NULL) {
+		delete[] sysexData;
 	}
+	shortMessageData = useShortMessageData;
+	timestamp = useTimestamp;
+	sysexData = NULL;
+	sysexLength = 0;
 }
 
-bool Synth::prerender() {
-	int newPrerenderWriteIx = (prerenderWriteIx + 1) % MAX_PRERENDER_SAMPLES;
-	if (newPrerenderWriteIx == prerenderReadIx) {
-		// The prerender buffer is full
-		return false;
+void MidiEvent::setSysex(const Bit8u *useSysexData, Bit32u useSysexLength, Bit32u useTimestamp) {
+	if (sysexData != NULL) {
+		delete[] sysexData;
 	}
-	doRenderStreams(
-		prerenderNonReverbLeft + prerenderWriteIx,
-		prerenderNonReverbRight + prerenderWriteIx,
-		prerenderReverbDryLeft + prerenderWriteIx,
-		prerenderReverbDryRight + prerenderWriteIx,
-		prerenderReverbWetLeft + prerenderWriteIx,
-		prerenderReverbWetRight + prerenderWriteIx,
-		1);
-	prerenderWriteIx = newPrerenderWriteIx;
+	shortMessageData = 0;
+	timestamp = useTimestamp;
+	sysexLength = useSysexLength;
+	Bit8u *dstSysexData = new Bit8u[sysexLength];
+	sysexData = dstSysexData;
+	memcpy(dstSysexData, useSysexData, sysexLength);
+}
+
+MidiEventQueue::MidiEventQueue(Bit32u useRingBufferSize) : ringBufferSize(useRingBufferSize) {
+	ringBuffer = new MidiEvent[ringBufferSize];
+	memset(ringBuffer, 0, ringBufferSize * sizeof(MidiEvent));
+	reset();
+}
+
+MidiEventQueue::~MidiEventQueue() {
+	delete[] ringBuffer;
+}
+
+void MidiEventQueue::reset() {
+	startPosition = 0;
+	endPosition = 0;
+}
+
+bool MidiEventQueue::pushShortMessage(Bit32u shortMessageData, Bit32u timestamp) {
+	unsigned int newEndPosition = (endPosition + 1) % ringBufferSize;
+	// Is ring buffer full?
+	if (startPosition == newEndPosition) return false;
+	ringBuffer[endPosition].setShortMessage(shortMessageData, timestamp);
+	endPosition = newEndPosition;
 	return true;
 }
 
-static inline void maybeCopy(Bit16s *out, Bit32u outPos, Bit16s *in, Bit32u inPos, Bit32u len) {
-	if (out == NULL) {
-		return;
-	}
-	memcpy(out + outPos, in + inPos, len * sizeof(Bit16s));
+bool MidiEventQueue::pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp) {
+	unsigned int newEndPosition = (endPosition + 1) % ringBufferSize;
+	// Is ring buffer full?
+	if (startPosition == newEndPosition) return false;
+	ringBuffer[endPosition].setSysex(sysexData, sysexLength, timestamp);
+	endPosition = newEndPosition;
+	return true;
 }
 
-void Synth::copyPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u pos, Bit32u len) {
-	maybeCopy(nonReverbLeft, pos, prerenderNonReverbLeft, prerenderReadIx, len);
-	maybeCopy(nonReverbRight, pos, prerenderNonReverbRight, prerenderReadIx, len);
-	maybeCopy(reverbDryLeft, pos, prerenderReverbDryLeft, prerenderReadIx, len);
-	maybeCopy(reverbDryRight, pos, prerenderReverbDryRight, prerenderReadIx, len);
-	maybeCopy(reverbWetLeft, pos, prerenderReverbWetLeft, prerenderReadIx, len);
-	maybeCopy(reverbWetRight, pos, prerenderReverbWetRight, prerenderReadIx, len);
+const MidiEvent *MidiEventQueue::peekMidiEvent() {
+	return (startPosition == endPosition) ? NULL : &ringBuffer[startPosition];
 }
 
-void Synth::checkPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u &pos, Bit32u &len) {
-	if (prerenderReadIx > prerenderWriteIx) {
-		// There's data in the prerender buffer, and the write index has wrapped.
-		Bit32u prerenderCopyLen = MAX_PRERENDER_SAMPLES - prerenderReadIx;
-		if (prerenderCopyLen > len) {
-			prerenderCopyLen = len;
-		}
-		copyPrerender(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, pos, prerenderCopyLen);
-		len -= prerenderCopyLen;
-		pos += prerenderCopyLen;
-		prerenderReadIx = (prerenderReadIx + prerenderCopyLen) % MAX_PRERENDER_SAMPLES;
-	}
-	if (prerenderReadIx < prerenderWriteIx) {
-		// There's data in the prerender buffer, and the write index is ahead of the read index.
-		Bit32u prerenderCopyLen = prerenderWriteIx - prerenderReadIx;
-		if (prerenderCopyLen > len) {
-			prerenderCopyLen = len;
-		}
-		copyPrerender(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, pos, prerenderCopyLen);
-		len -= prerenderCopyLen;
-		pos += prerenderCopyLen;
-		prerenderReadIx += prerenderCopyLen;
-	}
-	if (prerenderReadIx == prerenderWriteIx) {
-		// If the ring buffer's empty, reset it to start at 0 to minimise wrapping,
-		// which requires two writes instead of one.
-		prerenderReadIx = prerenderWriteIx = 0;
-	}
-}
-
-void Synth::renderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len) {
-	if (!isEnabled) {
-		clearIfNonNull(nonReverbLeft, len);
-		clearIfNonNull(nonReverbRight, len);
-		clearIfNonNull(reverbDryLeft, len);
-		clearIfNonNull(reverbDryRight, len);
-		clearIfNonNull(reverbWetLeft, len);
-		clearIfNonNull(reverbWetRight, len);
-		return;
+void MidiEventQueue::dropMidiEvent() {
+	// Is ring buffer empty?
+	if (startPosition != endPosition) {
+		startPosition = (startPosition + 1) % ringBufferSize;
 	}
-	Bit32u pos = 0;
+}
 
-	// First, check for data in the prerender buffer and spit that out before generating anything new.
-	// Note that the prerender buffer is rarely used - see comments elsewhere for details.
-	checkPrerender(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, pos, len);
+void Synth::render(Sample *stream, Bit32u len) {
+	Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN];
+	Sample tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
+	Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN];
+	Sample tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
+	Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN];
+	Sample tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
 
 	while (len > 0) {
 		Bit32u thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
-		doRenderStreams(
-			streamOffset(nonReverbLeft, pos),
-			streamOffset(nonReverbRight, pos),
-			streamOffset(reverbDryLeft, pos),
-			streamOffset(reverbDryRight, pos),
-			streamOffset(reverbWetLeft, pos),
-			streamOffset(reverbWetRight, pos),
-			thisLen);
+		renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisLen);
+		for (Bit32u i = 0; i < thisLen; i++) {
+#if MT32EMU_USE_FLOAT_SAMPLES
+			*(stream++) = tmpNonReverbLeft[i] + tmpReverbDryLeft[i] + tmpReverbWetLeft[i];
+			*(stream++) = tmpNonReverbRight[i] + tmpReverbDryRight[i] + tmpReverbWetRight[i];
+#else
+			*(stream++) = clipBit16s((Bit32s)tmpNonReverbLeft[i] + (Bit32s)tmpReverbDryLeft[i] + (Bit32s)tmpReverbWetLeft[i]);
+			*(stream++) = clipBit16s((Bit32s)tmpNonReverbRight[i] + (Bit32s)tmpReverbDryRight[i] + (Bit32s)tmpReverbWetRight[i]);
+#endif
+		}
 		len -= thisLen;
-		pos += thisLen;
 	}
 }
 
-// FIXME: Using more temporary buffers than we need to
-void Synth::doRenderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len) {
-	clearFloats(&tmpBufMixLeft[0], &tmpBufMixRight[0], len);
-	if (!reverbEnabled) {
-		for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
-			if (partialManager->produceOutput(i, &tmpBufPartialLeft[0], &tmpBufPartialRight[0], len)) {
-				mix(&tmpBufMixLeft[0], &tmpBufPartialLeft[0], len);
-				mix(&tmpBufMixRight[0], &tmpBufPartialRight[0], len);
-			}
-		}
-		if (nonReverbLeft != NULL) {
-			la32FloatToBit16sFunc(nonReverbLeft, &tmpBufMixLeft[0], len, outputGain);
-		}
-		if (nonReverbRight != NULL) {
-			la32FloatToBit16sFunc(nonReverbRight, &tmpBufMixRight[0], len, outputGain);
-		}
-		clearIfNonNull(reverbDryLeft, len);
-		clearIfNonNull(reverbDryRight, len);
-		clearIfNonNull(reverbWetLeft, len);
-		clearIfNonNull(reverbWetRight, len);
-	} else {
-		for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
-			if (!partialManager->shouldReverb(i)) {
-				if (partialManager->produceOutput(i, &tmpBufPartialLeft[0], &tmpBufPartialRight[0], len)) {
-					mix(&tmpBufMixLeft[0], &tmpBufPartialLeft[0], len);
-					mix(&tmpBufMixRight[0], &tmpBufPartialRight[0], len);
+void Synth::renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) {
+	while (len > 0) {
+		// We need to ensure zero-duration notes will play so add minimum 1-sample delay.
+		Bit32u thisLen = 1;
+		if (!isAbortingPoly()) {
+			const MidiEvent *nextEvent = midiQueue->peekMidiEvent();
+			Bit32s samplesToNextEvent = (nextEvent != NULL) ? Bit32s(nextEvent->timestamp - renderedSampleCount) : MAX_SAMPLES_PER_RUN;
+			if (samplesToNextEvent > 0) {
+				thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
+				if (thisLen > (Bit32u)samplesToNextEvent) {
+					thisLen = samplesToNextEvent;
+				}
+			} else {
+				if (nextEvent->sysexData == NULL) {
+					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 (!isAbortingPoly()) {
+						midiQueue->dropMidiEvent();
+					}
+				} else {
+					playSysexNow(nextEvent->sysexData, nextEvent->sysexLength);
+					midiQueue->dropMidiEvent();
 				}
 			}
 		}
-		if (nonReverbLeft != NULL) {
-			la32FloatToBit16sFunc(nonReverbLeft, &tmpBufMixLeft[0], len, outputGain);
+		doRenderStreams(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, thisLen);
+		advanceStreamPosition(nonReverbLeft, thisLen);
+		advanceStreamPosition(nonReverbRight, thisLen);
+		advanceStreamPosition(reverbDryLeft, thisLen);
+		advanceStreamPosition(reverbDryRight, thisLen);
+		advanceStreamPosition(reverbWetLeft, thisLen);
+		advanceStreamPosition(reverbWetRight, thisLen);
+		len -= thisLen;
+	}
+}
+
+void Synth::convertSamplesToOutput(Sample *target, const Sample *source, Bit32u len, bool reverb) {
+	if (target == NULL) return;
+
+	if (dacInputMode == DACInputMode_PURE) {
+		memcpy(target, source, len * sizeof(Sample));
+		return;
+	}
+
+#if MT32EMU_USE_FLOAT_SAMPLES
+	float gain = reverb ? reverbOutputGain * CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR : 2.0f * outputGain;
+	while (len--) {
+		*(target++) = *(source++) * gain;
+	}
+#else
+	float gain = reverb ? reverbOutputGain * CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR : outputGain;
+	if (!reverb) {
+		switch (dacInputMode) {
+		case DACInputMode_NICE:
+			// Since we're not shooting for accuracy here, don't worry about the rounding mode.
+			gain *= 2.0f;
+			break;
+		case DACInputMode_GENERATION1:
+			while (len--) {
+				*target = clipBit16s(Bit32s(*source * gain));
+				*target = (*target & 0x8000) | ((*target << 1) & 0x7FFE);
+				source++;
+				target++;
+			}
+			return;
+		case DACInputMode_GENERATION2:
+			while (len--) {
+				*target = clipBit16s(Bit32s(*source * gain));
+				*target = (*target & 0x8000) | ((*target << 1) & 0x7FFE) | ((*target >> 14) & 0x0001);
+				source++;
+				target++;
+			}
+			return;
+		default:
+			break;
 		}
-		if (nonReverbRight != NULL) {
-			la32FloatToBit16sFunc(nonReverbRight, &tmpBufMixRight[0], len, outputGain);
+	}
+	while (len--) {
+		*(target++) = clipBit16s(Bit32s(*(source++) * gain));
+	}
+#endif
+}
+
+void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) {
+	if (isEnabled) {
+		Sample tmpBufMixLeft[MAX_SAMPLES_PER_RUN], tmpBufMixRight[MAX_SAMPLES_PER_RUN];
+		muteStream(tmpBufMixLeft, len);
+		muteStream(tmpBufMixRight, len);
+		for (unsigned int i = 0; i < getPartialCount(); i++) {
+			if (!reverbEnabled || !partialManager->shouldReverb(i)) {
+				partialManager->produceOutput(i, tmpBufMixLeft, tmpBufMixRight, len);
+			}
 		}
+		convertSamplesToOutput(nonReverbLeft, tmpBufMixLeft, len, false);
+		convertSamplesToOutput(nonReverbRight, tmpBufMixRight, len, false);
+	} else {
+		muteStream(nonReverbLeft, len);
+		muteStream(nonReverbRight, len);
+	}
 
-		clearFloats(&tmpBufMixLeft[0], &tmpBufMixRight[0], len);
-		for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+	if (isEnabled && reverbEnabled) {
+		Sample tmpBufMixLeft[MAX_SAMPLES_PER_RUN], tmpBufMixRight[MAX_SAMPLES_PER_RUN];
+		muteStream(tmpBufMixLeft, len);
+		muteStream(tmpBufMixRight, len);
+		for (unsigned int i = 0; i < getPartialCount(); i++) {
 			if (partialManager->shouldReverb(i)) {
-				if (partialManager->produceOutput(i, &tmpBufPartialLeft[0], &tmpBufPartialRight[0], len)) {
-					mix(&tmpBufMixLeft[0], &tmpBufPartialLeft[0], len);
-					mix(&tmpBufMixRight[0], &tmpBufPartialRight[0], len);
-				}
+				partialManager->produceOutput(i, tmpBufMixLeft, tmpBufMixRight, len);
 			}
 		}
-		if (reverbDryLeft != NULL) {
-			la32FloatToBit16sFunc(reverbDryLeft, &tmpBufMixLeft[0], len, outputGain);
-		}
-		if (reverbDryRight != NULL) {
-			la32FloatToBit16sFunc(reverbDryRight, &tmpBufMixRight[0], len, outputGain);
-		}
+		convertSamplesToOutput(reverbDryLeft, tmpBufMixLeft, len, false);
+		convertSamplesToOutput(reverbDryRight, tmpBufMixRight, len, false);
 
-		// FIXME: Note that on the real devices, reverb input and output are signed linear 16-bit (well, kinda, there's some fudging) PCM, not float.
-		reverbModel->process(&tmpBufMixLeft[0], &tmpBufMixRight[0], &tmpBufReverbOutLeft[0], &tmpBufReverbOutRight[0], len);
-		if (reverbWetLeft != NULL) {
-			reverbFloatToBit16sFunc(reverbWetLeft, &tmpBufReverbOutLeft[0], len, reverbOutputGain);
-		}
-		if (reverbWetRight != NULL) {
-			reverbFloatToBit16sFunc(reverbWetRight, &tmpBufReverbOutRight[0], len, reverbOutputGain);
-		}
+		Sample tmpBufReverbOutLeft[MAX_SAMPLES_PER_RUN], tmpBufReverbOutRight[MAX_SAMPLES_PER_RUN];
+		reverbModel->process(tmpBufMixLeft, tmpBufMixRight, tmpBufReverbOutLeft, tmpBufReverbOutRight, len);
+		convertSamplesToOutput(reverbWetLeft, tmpBufReverbOutLeft, len, true);
+		convertSamplesToOutput(reverbWetRight, tmpBufReverbOutRight, len, true);
+	} else {
+		muteStream(reverbDryLeft, len);
+		muteStream(reverbDryRight, len);
+		muteStream(reverbWetLeft, len);
+		muteStream(reverbWetRight, len);
 	}
+
 	partialManager->clearAlreadyOutputed();
 	renderedSampleCount += len;
 }
@@ -1489,19 +1526,14 @@ void Synth::printPartialUsage(unsigned long sampleOffset) {
 	unsigned int partialUsage[9];
 	partialManager->getPerPartPartialUsage(partialUsage);
 	if (sampleOffset > 0) {
-		printDebug("[+%lu] Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d  TOTAL: %02d", sampleOffset, partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->getFreePartialCount());
+		printDebug("[+%lu] Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d  TOTAL: %02d", sampleOffset, partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount());
 	} else {
-		printDebug("Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d  TOTAL: %02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->getFreePartialCount());
+		printDebug("Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d  TOTAL: %02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount());
 	}
 }
 
 bool Synth::hasActivePartials() const {
-	if (prerenderReadIx != prerenderWriteIx) {
-		// Data in the prerender buffer means that the current isActive() states are "in the future".
-		// It also means that partials are definitely active at this render point.
-		return true;
-	}
-	for (int partialNum = 0; partialNum < MT32EMU_MAX_PARTIALS; partialNum++) {
+	for (unsigned int partialNum = 0; partialNum < getPartialCount(); partialNum++) {
 		if (partialManager->getPartial(partialNum)->isActive()) {
 			return true;
 		}
@@ -1509,6 +1541,10 @@ bool Synth::hasActivePartials() const {
 	return false;
 }
 
+bool Synth::isAbortingPoly() const {
+	return abortingPoly != NULL;
+}
+
 bool Synth::isActive() const {
 	if (hasActivePartials()) {
 		return true;
@@ -1523,6 +1559,10 @@ const Partial *Synth::getPartial(unsigned int partialNum) const {
 	return partialManager->getPartial(partialNum);
 }
 
+unsigned int Synth::getPartialCount() const {
+	return partialCount;
+}
+
 const Part *Synth::getPart(unsigned int partNum) const {
 	if (partNum > 8) {
 		return NULL;
diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h
index b85e7ae..783d6e2 100644
--- a/audio/softsynth/mt32/Synth.h
+++ b/audio/softsynth/mt32/Synth.h
@@ -27,6 +27,7 @@ class Partial;
 class PartialManager;
 class Part;
 class ROMImage;
+class BReverbModel;
 
 /**
  * Methods for emulating the connection between the LA32 and the DAC, which involves
@@ -44,6 +45,7 @@ enum DACInputMode {
 	// * Much less likely to overdrive than any other mode.
 	// * Half the volume of any of the other modes, meaning its volume relative to the reverb
 	//   output when mixed together directly will sound wrong.
+	// * Output gain is ignored for both LA32 and reverb output.
 	// * Perfect for developers while debugging :)
 	DACInputMode_PURE,
 
@@ -58,7 +60,17 @@ enum DACInputMode {
 	DACInputMode_GENERATION2
 };
 
-typedef void (*FloatToBit16sFunc)(Bit16s *target, const float *source, Bit32u len, float outputGain);
+enum MIDIDelayMode {
+	// Process incoming MIDI events immediately.
+	MIDIDelayMode_IMMEDIATE,
+
+	// Delay incoming short MIDI messages as if they where transferred via a MIDI cable to a real hardware unit and immediate sysex processing.
+	// This ensures more accurate timing of simultaneous NoteOn messages.
+	MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY,
+
+	// Delay all incoming MIDI events as if they where transferred via a MIDI cable to a real hardware unit.
+	MIDIDelayMode_DELAY_ALL
+};
 
 const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41;
 
@@ -217,18 +229,6 @@ public:
 	ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {}
 };
 
-class ReverbModel {
-public:
-	virtual ~ReverbModel() {}
-	// After construction or a close(), open() will be called at least once before any other call (with the exception of close()).
-	virtual void open() = 0;
-	// May be called multiple times without an open() in between.
-	virtual void close() = 0;
-	virtual void setParameters(Bit8u time, Bit8u level) = 0;
-	virtual void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) = 0;
-	virtual bool isActive() const = 0;
-};
-
 class ReportHandler {
 friend class Synth;
 
@@ -244,22 +244,63 @@ protected:
 	virtual void onErrorControlROM() {}
 	virtual void onErrorPCMROM() {}
 	virtual void showLCDMessage(const char *message);
+	virtual void onMIDIMessagePlayed() {}
 	virtual void onDeviceReset() {}
 	virtual void onDeviceReconfig() {}
 	virtual void onNewReverbMode(Bit8u /* mode */) {}
 	virtual void onNewReverbTime(Bit8u /* time */) {}
 	virtual void onNewReverbLevel(Bit8u /* level */) {}
-	virtual void onPartStateChanged(int /* partNum */, bool /* hasActiveNonReleasingPolys */) {}
 	virtual void onPolyStateChanged(int /* partNum */) {}
-	virtual void onPartialStateChanged(int /* partialNum */, int /* oldPartialPhase */, int /* newPartialPhase */) {}
 	virtual void onProgramChanged(int /* partNum */, int /* bankNum */, const char * /* patchName */) {}
 };
 
+/**
+ * Used to safely store timestamped MIDI events in a local queue.
+ */
+struct MidiEvent {
+	Bit32u shortMessageData;
+	const Bit8u *sysexData;
+	Bit32u sysexLength;
+	Bit32u timestamp;
+
+	~MidiEvent();
+	void setShortMessage(Bit32u shortMessageData, Bit32u timestamp);
+	void setSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp);
+};
+
+/**
+ * Simple queue implementation using a ring buffer to store incoming MIDI event before the synth actually processes it.
+ * It is intended to:
+ * - get rid of prerenderer while retaining graceful partial abortion
+ * - add fair emulation of the MIDI interface delays
+ * - extend the synth interface with the default implementation of a typical rendering loop.
+ * THREAD SAFETY:
+ * It is safe to use either in a single thread environment or when there are only two threads - one performs only reading
+ * and one performs only writing. More complicated usage requires external synchronisation.
+ */
+class MidiEventQueue {
+private:
+	MidiEvent *ringBuffer;
+	Bit32u ringBufferSize;
+	volatile Bit32u startPosition;
+	volatile Bit32u endPosition;
+
+public:
+	MidiEventQueue(Bit32u ringBufferSize = DEFAULT_MIDI_EVENT_QUEUE_SIZE);
+	~MidiEventQueue();
+	void reset();
+	bool pushShortMessage(Bit32u shortMessageData, Bit32u timestamp);
+	bool pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp);
+	const MidiEvent *peekMidiEvent();
+	void dropMidiEvent();
+};
+
 class Synth {
 friend class Part;
 friend class RhythmPart;
 friend class Poly;
 friend class Partial;
+friend class PartialManager;
 friend class Tables;
 friend class MemoryRegion;
 friend class TVA;
@@ -286,20 +327,22 @@ private:
 	Bit16s *pcmROMData;
 	size_t pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM
 
-	Bit8s chantable[32];
-
-	Bit32u renderedSampleCount;
+	unsigned int partialCount;
+	Bit8s chantable[32]; // FIXME: Need explanation why 32 is set, obviously it should be 16
 
+	MidiEventQueue *midiQueue;
+	volatile Bit32u lastReceivedMIDIEventTimestamp;
+	volatile Bit32u renderedSampleCount;
 
 	MemParams mt32ram, mt32default;
 
-	ReverbModel *reverbModels[4];
-	ReverbModel *reverbModel;
+	BReverbModel *reverbModels[4];
+	BReverbModel *reverbModel;
 	bool reverbEnabled;
 	bool reverbOverridden;
 
-	FloatToBit16sFunc la32FloatToBit16sFunc;
-	FloatToBit16sFunc reverbFloatToBit16sFunc;
+	MIDIDelayMode midiDelayMode;
+	DACInputMode dacInputMode;
 	float outputGain;
 	float reverbOutputGain;
 
@@ -311,41 +354,18 @@ private:
 	PartialManager *partialManager;
 	Part *parts[9];
 
-	// FIXME: We can reorganise things so that we don't need all these separate tmpBuf, tmp and prerender buffers.
-	// This should be rationalised when things have stabilised a bit (if prerender buffers don't die in the mean time).
-
-	float tmpBufPartialLeft[MAX_SAMPLES_PER_RUN];
-	float tmpBufPartialRight[MAX_SAMPLES_PER_RUN];
-	float tmpBufMixLeft[MAX_SAMPLES_PER_RUN];
-	float tmpBufMixRight[MAX_SAMPLES_PER_RUN];
-	float tmpBufReverbOutLeft[MAX_SAMPLES_PER_RUN];
-	float tmpBufReverbOutRight[MAX_SAMPLES_PER_RUN];
-
-	Bit16s tmpNonReverbLeft[MAX_SAMPLES_PER_RUN];
-	Bit16s tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
-	Bit16s tmpReverbDryLeft[MAX_SAMPLES_PER_RUN];
-	Bit16s tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
-	Bit16s tmpReverbWetLeft[MAX_SAMPLES_PER_RUN];
-	Bit16s tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
-
-	// These ring buffers are only used to simulate delays present on the real device.
-	// In particular, when a partial needs to be aborted to free it up for use by a new Poly,
+	// When a partial needs to be aborted to free it up for use by a new Poly,
 	// the controller will busy-loop waiting for the sound to finish.
-	Bit16s prerenderNonReverbLeft[MAX_PRERENDER_SAMPLES];
-	Bit16s prerenderNonReverbRight[MAX_PRERENDER_SAMPLES];
-	Bit16s prerenderReverbDryLeft[MAX_PRERENDER_SAMPLES];
-	Bit16s prerenderReverbDryRight[MAX_PRERENDER_SAMPLES];
-	Bit16s prerenderReverbWetLeft[MAX_PRERENDER_SAMPLES];
-	Bit16s prerenderReverbWetRight[MAX_PRERENDER_SAMPLES];
-	int prerenderReadIx;
-	int prerenderWriteIx;
-
-	bool prerender();
-	void copyPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u pos, Bit32u len);
-	void checkPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u &pos, Bit32u &len);
-	void doRenderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len);
-
-	void playAddressedSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
+	// We emulate this by delaying new MIDI events processing until abortion finishes.
+	Poly *abortingPoly;
+
+	Bit32u getShortMessageLength(Bit32u msg);
+	Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp);
+
+	void convertSamplesToOutput(Sample *target, const Sample *source, Bit32u len, bool reverb);
+	bool isAbortingPoly() const;
+	void doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len);
+
 	void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len) const;
 	void initMemoryRegions();
 	void deleteMemoryRegions();
@@ -375,6 +395,14 @@ private:
 	void printDebug(const char *fmt, ...);
 
 public:
+	static inline Bit16s clipBit16s(Bit32s sample) {
+		// Clamp values above 32767 to 32767, and values below -32768 to -32768
+		if ((sample + 32768) & ~65535) {
+			return (sample >> 31) ^ 32767;
+		}
+		return (Bit16s)sample;
+	}
+
 	static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum);
 
 	// Optionally sets callbacks for reporting various errors, information and debug messages
@@ -384,18 +412,44 @@ public:
 	// Used to initialise the MT-32. Must be called before any other function.
 	// Returns true if initialization was sucessful, otherwise returns false.
 	// controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth.
-	bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage);
+	// usePartialCount sets the maximum number of partials playing simultaneously for this session.
+	bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount = DEFAULT_MAX_PARTIALS);
 
 	// Closes the MT-32 and deallocates any memory used by the synthesizer
 	void close(void);
 
-	// Sends a 4-byte MIDI message to the MT-32 for immediate playback
-	void playMsg(Bit32u msg);
+	// All the enqueued events are processed by the synth immediately.
+	void flushMIDIQueue();
+
+	// Sets size of the internal MIDI event queue.
+	// The queue is flushed before reallocation.
+	void setMIDIEventQueueSize(Bit32u);
+
+	// Enqueues a MIDI event for subsequent playback.
+	// The minimum delay involves the delay introduced while the event is transferred via MIDI interface
+	// and emulation of the MCU busy-loop while it frees partials for use by a new Poly.
+	// Calls from multiple threads must be synchronised, although,
+	// no synchronisation is required with the rendering thread.
+
+	// 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.
+	bool playMsg(Bit32u msg, Bit32u timestamp);
+	bool playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp);
+	// The MIDI event will be processed ASAP.
+	bool playMsg(Bit32u msg);
+	bool playSysex(const Bit8u *sysex, Bit32u len);
+
+	// WARNING:
+	// The methods below don't ensure minimum 1-sample delay between sequential MIDI events,
+	// and a sequence of NoteOn and immediately succeeding NoteOff messages is always silent.
+
+	// Sends a 4-byte MIDI message to the MT-32 for immediate playback.
+	void playMsgNow(Bit32u msg);
 	void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
 
 	// Sends a string of Sysex commands to the MT-32 for immediate interpretation
 	// The length is in bytes
-	void playSysex(const Bit8u *sysex, Bit32u len);
+	void playSysexNow(const Bit8u *sysex, Bit32u len);
 	void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len);
 	void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len);
 	void writeSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
@@ -405,20 +459,31 @@ public:
 	void setReverbOverridden(bool reverbOverridden);
 	bool isReverbOverridden() const;
 	void setDACInputMode(DACInputMode mode);
+	DACInputMode getDACInputMode() const;
+	void setMIDIDelayMode(MIDIDelayMode mode);
+	MIDIDelayMode getMIDIDelayMode() const;
 
 	// Sets output gain factor. Applied to all output samples and unrelated with the synth's Master volume.
+	// Ignored in DACInputMode_PURE
 	void setOutputGain(float);
+	float getOutputGain() const;
 
 	// Sets output gain factor for the reverb wet output. setOutputGain() doesn't change reverb output gain.
+	// Note: We're currently emulate CM-32L/CM-64 reverb quite accurately and the reverb output level closely
+	// corresponds to the level of digital capture. Although, according to the CM-64 PCB schematic,
+	// there is a difference in the reverb analogue circuit, and the resulting output gain is 0.68
+	// of that for LA32 analogue output. This factor is applied to the reverb output gain.
+	// Ignored in DACInputMode_PURE
 	void setReverbOutputGain(float);
+	float getReverbOutputGain() const;
 
 	// Renders samples to the specified output stream.
 	// The length is in frames, not bytes (in 16-bit stereo,
 	// one frame is 4 bytes).
-	void render(Bit16s *stream, Bit32u len);
+	void render(Sample *stream, Bit32u len);
 
 	// Renders samples to the specified output streams (any or all of which may be NULL).
-	void renderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len);
+	void renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len);
 
 	// Returns true when there is at least one active partial, otherwise false.
 	bool hasActivePartials() const;
@@ -428,6 +493,9 @@ public:
 
 	const Partial *getPartial(unsigned int partialNum) const;
 
+	// Returns the maximum number of partials playing simultaneously.
+	unsigned int getPartialCount() const;
+
 	void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
 
 	// partNum should be 0..7 for Part 1..8, or 8 for Rhythm
diff --git a/audio/softsynth/mt32/TVP.cpp b/audio/softsynth/mt32/TVP.cpp
index c3e64c1..8f68245 100644
--- a/audio/softsynth/mt32/TVP.cpp
+++ b/audio/softsynth/mt32/TVP.cpp
@@ -181,7 +181,7 @@ void TVP::updatePitch() {
 	pitch = (Bit16u)newPitch;
 
 	// FIXME: We're doing this here because that's what the CM-32L does - we should probably move this somewhere more appropriate in future.
-	partial->tva->recalcSustain();
+	partial->getTVA()->recalcSustain();
 }
 
 void TVP::targetPitchOffsetReached() {
diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h
index 8b4580d..bfb80e1 100644
--- a/audio/softsynth/mt32/Tables.h
+++ b/audio/softsynth/mt32/Tables.h
@@ -25,6 +25,11 @@ namespace MT32Emu {
 // The output from the synth is supposed to be resampled to convert the sample rate.
 const unsigned int SAMPLE_RATE = 32000;
 
+// MIDI interface data transfer rate in samples. Used to simulate the transfer delay.
+const double MIDI_DATA_TRANSFER_RATE = (double)SAMPLE_RATE / 31250.0 * 8.0;
+
+const float CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR = 0.68f;
+
 const int MIDDLEC = 60;
 
 class Synth;
diff --git a/audio/softsynth/mt32/freeverb.cpp b/audio/softsynth/mt32/freeverb.cpp
deleted file mode 100644
index 181b878..0000000
--- a/audio/softsynth/mt32/freeverb.cpp
+++ /dev/null
@@ -1,324 +0,0 @@
-// Allpass filter implementation
-//
-// Written by Jezar at Dreampoint, June 2000
-// http://www.dreampoint.co.uk
-// This code is public domain
-
-#include "freeverb.h"
-
-allpass::allpass()
-{
-	bufidx = 0;
-}
-
-void allpass::setbuffer(float *buf, int size)
-{
-	buffer = buf;
-	bufsize = size;
-}
-
-void allpass::mute()
-{
-	for (int i=0; i<bufsize; i++)
-		buffer[i]=0;
-}
-
-void allpass::setfeedback(float val)
-{
-	feedback = val;
-}
-
-float allpass::getfeedback()
-{
-	return feedback;
-}
-
-void allpass::deletebuffer()
-{
-	delete[] buffer;
-	buffer = 0;
-}
-// Comb filter implementation
-//
-// Written by Jezar at Dreampoint, June 2000
-// http://www.dreampoint.co.uk
-// This code is public domain
-
-comb::comb()
-{
-	filterstore = 0;
-	bufidx = 0;
-}
-
-void comb::setbuffer(float *buf, int size)
-{
-	buffer = buf;
-	bufsize = size;
-}
-
-void comb::mute()
-{
-	for (int i=0; i<bufsize; i++)
-		buffer[i]=0;
-}
-
-void comb::setdamp(float val)
-{
-	damp1 = val;
-	damp2 = 1-val;
-}
-
-float comb::getdamp()
-{
-	return damp1;
-}
-
-void comb::setfeedback(float val)
-{
-	feedback = val;
-}
-
-float comb::getfeedback()
-{
-	return feedback;
-}
-
-void comb::deletebuffer()
-{
-	delete[] buffer;
-	buffer = 0;
-}
-// Reverb model implementation
-//
-// Written by Jezar at Dreampoint, June 2000
-// Modifications by Jerome Fisher, 2009, 2011
-// http://www.dreampoint.co.uk
-// This code is public domain
-
-revmodel::revmodel(float scaletuning)
-{
-	int i;
-	int bufsize;
-
-	// Allocate buffers for the components
-	for (i = 0; i < numcombs; i++) {
-		bufsize = int(scaletuning * combtuning[i]);
-		combL[i].setbuffer(new float[bufsize], bufsize);
-		bufsize += int(scaletuning * stereospread);
-		combR[i].setbuffer(new float[bufsize], bufsize);
-	}
-	for (i = 0; i < numallpasses; i++) {
-		bufsize = int(scaletuning * allpasstuning[i]);
-		allpassL[i].setbuffer(new float[bufsize], bufsize);
-		allpassL[i].setfeedback(0.5f);
-		bufsize += int(scaletuning * stereospread);
-		allpassR[i].setbuffer(new float[bufsize], bufsize);
-		allpassR[i].setfeedback(0.5f);
-	}
-
-	// Set default values
-	dry = initialdry;
-	wet = initialwet*scalewet;
-	damp = initialdamp*scaledamp;
-	roomsize = (initialroom*scaleroom) + offsetroom;
-	width = initialwidth;
-	mode = initialmode;
-	update();
-
-	// Buffer will be full of rubbish - so we MUST mute them
-	mute();
-}
-
-revmodel::~revmodel()
-{
-	int i;
-
-	for (i = 0; i < numcombs; i++) {
-		combL[i].deletebuffer();
-		combR[i].deletebuffer();
-	}
-	for (i = 0; i < numallpasses; i++) {
-		allpassL[i].deletebuffer();
-		allpassR[i].deletebuffer();
-	}
-}
-
-void revmodel::mute()
-{
-	int i;
-
-	if (getmode() >= freezemode)
-		return;
-
-	for (i=0;i<numcombs;i++)
-	{
-		combL[i].mute();
-		combR[i].mute();
-	}
-	for (i=0;i<numallpasses;i++)
-	{
-		allpassL[i].mute();
-		allpassR[i].mute();
-	}
-
-	// Init LPF history
-	filtprev1 = 0;
-	filtprev2 = 0;
-}
-
-void revmodel::process(const float *inputL, const float *inputR, float *outputL, float *outputR, long numsamples)
-{
-	float outL,outR,input;
-
-	while (numsamples-- > 0)
-	{
-		int i;
-
-		outL = outR = 0;
-		input = (*inputL + *inputR) * gain;
-
-		// Implementation of 2-stage IIR single-pole low-pass filter
-		// found at the entrance of reverb processing on real devices
-		filtprev1 += (input - filtprev1) * filtval;
-		filtprev2 += (filtprev1 - filtprev2) * filtval;
-		input = filtprev2;
-
-		int s = -1;
-		// Accumulate comb filters in parallel
-		for (i=0; i<numcombs; i++)
-		{
-			outL += s * combL[i].process(input);
-			outR += s * combR[i].process(input);
-			s = -s;
-		}
-
-		// Feed through allpasses in series
-		for (i=0; i<numallpasses; i++)
-		{
-			outL = allpassL[i].process(outL);
-			outR = allpassR[i].process(outR);
-		}
-
-		// Calculate output REPLACING anything already there
-		*outputL = outL*wet1 + outR*wet2;
-		*outputR = outR*wet1 + outL*wet2;
-
-		inputL++;
-		inputR++;
-		outputL++;
-		outputR++;
-	}
-}
-
-void revmodel::update()
-{
-// Recalculate internal values after parameter change
-
-	int i;
-
-	wet1 = wet*(width/2 + 0.5f);
-	wet2 = wet*((1-width)/2);
-
-	if (mode >= freezemode)
-	{
-		roomsize1 = 1;
-		damp1 = 0;
-		gain = muted;
-	}
-	else
-	{
-		roomsize1 = roomsize;
-		damp1 = damp;
-		gain = fixedgain;
-	}
-
-	for (i=0; i<numcombs; i++)
-	{
-		combL[i].setfeedback(roomsize1);
-		combR[i].setfeedback(roomsize1);
-	}
-
-	for (i=0; i<numcombs; i++)
-	{
-		combL[i].setdamp(damp1);
-		combR[i].setdamp(damp1);
-	}
-}
-
-// The following get/set functions are not inlined, because
-// speed is never an issue when calling them, and also
-// because as you develop the reverb model, you may
-// wish to take dynamic action when they are called.
-
-void revmodel::setroomsize(float value)
-{
-	roomsize = (value*scaleroom) + offsetroom;
-	update();
-}
-
-float revmodel::getroomsize()
-{
-	return (roomsize-offsetroom)/scaleroom;
-}
-
-void revmodel::setdamp(float value)
-{
-	damp = value*scaledamp;
-	update();
-}
-
-float revmodel::getdamp()
-{
-	return damp/scaledamp;
-}
-
-void revmodel::setwet(float value)
-{
-	wet = value*scalewet;
-	update();
-}
-
-float revmodel::getwet()
-{
-	return wet/scalewet;
-}
-
-void revmodel::setdry(float value)
-{
-	dry = value*scaledry;
-}
-
-float revmodel::getdry()
-{
-	return dry/scaledry;
-}
-
-void revmodel::setwidth(float value)
-{
-	width = value;
-	update();
-}
-
-float revmodel::getwidth()
-{
-	return width;
-}
-
-void revmodel::setmode(float value)
-{
-	mode = value;
-	update();
-}
-
-float revmodel::getmode()
-{
-	if (mode >= freezemode)
-		return 1;
-	else
-		return 0;
-}
-
-void revmodel::setfiltval(float value)
-{
-	filtval = value;
-}
diff --git a/audio/softsynth/mt32/freeverb.h b/audio/softsynth/mt32/freeverb.h
deleted file mode 100644
index ae4d481..0000000
--- a/audio/softsynth/mt32/freeverb.h
+++ /dev/null
@@ -1,189 +0,0 @@
-#ifndef _freeverb_
-#define _freeverb_
-
-// Reverb model tuning values
-//
-// Written by Jezar at Dreampoint, June 2000
-// http://www.dreampoint.co.uk
-// This code is public domain
-
-const int   numcombs        = 8;
-const int   numallpasses    = 4;
-const float muted           = 0;
-const float fixedgain       = 0.015f;
-const float scalewet        = 3;
-const float scaledry        = 2;
-const float scaledamp       = 0.4f;
-const float scaleroom       = 0.28f;
-const float offsetroom      = 0.7f;
-const float initialroom     = 0.5f;
-const float initialdamp     = 0.5f;
-const float initialwet      = 1/scalewet;
-const float initialdry      = 0;
-const float initialwidth    = 1;
-const float initialmode     = 0;
-const float freezemode      = 0.5f;
-const int   stereospread    = 23;
-
-const int combtuning[]      = {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617};
-const int allpasstuning[]   = {556, 441, 341, 225};
-
-// Macro for killing denormalled numbers
-//
-// Written by Jezar at Dreampoint, June 2000
-// http://www.dreampoint.co.uk
-// Based on IS_DENORMAL macro by Jon Watte
-// This code is public domain
-
-static inline float undenormalise(float x) {
-	union {
-		float f;
-		unsigned int i;
-	} u;
-	u.f = x;
-	if ((u.i & 0x7f800000) == 0) {
-		return 0.0f;
-	}
-	return x;
-}
-
-// Allpass filter declaration
-//
-// Written by Jezar at Dreampoint, June 2000
-// http://www.dreampoint.co.uk
-// This code is public domain
-
-class allpass
-{
-public:
-	                allpass();
-	        void    setbuffer(float *buf, int size);
-	        void    deletebuffer();
-	inline  float   process(float inp);
-	        void    mute();
-	        void    setfeedback(float val);
-	        float   getfeedback();
-// private:
-	float   feedback;
-	float   *buffer;
-	int     bufsize;
-	int     bufidx;
-};
-
-
-// Big to inline - but crucial for speed
-
-inline float allpass::process(float input)
-{
-	float output;
-	float bufout;
-
-	bufout = undenormalise(buffer[bufidx]);
-
-	output = -input + bufout;
-	buffer[bufidx] = input + (bufout*feedback);
-
-	if (++bufidx>=bufsize) bufidx = 0;
-
-	return output;
-}
-
-// Comb filter class declaration
-//
-// Written by Jezar at Dreampoint, June 2000
-// http://www.dreampoint.co.uk
-// This code is public domain
-
-class comb
-{
-public:
-	                comb();
-	        void    setbuffer(float *buf, int size);
-	        void    deletebuffer();
-	inline  float   process(float inp);
-	        void    mute();
-	        void    setdamp(float val);
-	        float   getdamp();
-	        void    setfeedback(float val);
-	        float   getfeedback();
-private:
-	float   feedback;
-	float   filterstore;
-	float   damp1;
-	float   damp2;
-	float   *buffer;
-	int     bufsize;
-	int     bufidx;
-};
-
-
-// Big to inline - but crucial for speed
-
-inline float comb::process(float input)
-{
-	float output;
-
-	output = undenormalise(buffer[bufidx]);
-
-	filterstore = undenormalise((output*damp2) + (filterstore*damp1));
-
-	buffer[bufidx] = input + (filterstore*feedback);
-
-	if (++bufidx>=bufsize) bufidx = 0;
-
-	return output;
-}
-
-// Reverb model declaration
-//
-// Written by Jezar at Dreampoint, June 2000
-// Modifications by Jerome Fisher, 2009
-// http://www.dreampoint.co.uk
-// This code is public domain
-
-class revmodel
-{
-public:
-			       revmodel(float scaletuning);
-						 ~revmodel();
-			void   mute();
-			void   process(const float *inputL, const float *inputR, float *outputL, float *outputR, long numsamples);
-			void   setroomsize(float value);
-			float  getroomsize();
-			void   setdamp(float value);
-			float  getdamp();
-			void   setwet(float value);
-			float  getwet();
-			void   setdry(float value);
-			float  getdry();
-			void   setwidth(float value);
-			float  getwidth();
-			void   setmode(float value);
-			float  getmode();
-			void   setfiltval(float value);
-private:
-			void   update();
-private:
-	float  gain;
-	float  roomsize,roomsize1;
-	float  damp,damp1;
-	float  wet,wet1,wet2;
-	float  dry;
-	float  width;
-	float  mode;
-
-	// LPF stuff
-	float filtval;
-	float filtprev1;
-	float filtprev2;
-
-	// Comb filters
-	comb   combL[numcombs];
-	comb   combR[numcombs];
-
-	// Allpass filters
-	allpass	allpassL[numallpasses];
-	allpass	allpassR[numallpasses];
-};
-
-#endif//_freeverb_
diff --git a/audio/softsynth/mt32/module.mk b/audio/softsynth/mt32/module.mk
index e7afdfd..1c8aa12 100644
--- a/audio/softsynth/mt32/module.mk
+++ b/audio/softsynth/mt32/module.mk
@@ -1,24 +1,19 @@
 MODULE := audio/softsynth/mt32
 
 MODULE_OBJS := \
-	AReverbModel.o \
 	BReverbModel.o \
-	DelayReverb.o \
-	FreeverbModel.o \
 	LA32Ramp.o \
 	LA32WaveGenerator.o \
-	LegacyWaveGenerator.o \
 	Part.o \
 	Partial.o \
 	PartialManager.o \
 	Poly.o \
 	ROMInfo.o \
 	Synth.o \
+	Tables.o \
 	TVA.o \
 	TVF.o \
-	TVP.o \
-	Tables.o \
-	freeverb.o
+	TVP.o
 
 # Include common rules
 include $(srcdir)/rules.mk
diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h
index 971a088..ab96388 100644
--- a/audio/softsynth/mt32/mt32emu.h
+++ b/audio/softsynth/mt32/mt32emu.h
@@ -60,27 +60,24 @@
 #define MT32EMU_MONITOR_TVF 0
 
 // Configuration
-// The maximum number of partials playing simultaneously
-#define MT32EMU_MAX_PARTIALS 32
-// The maximum number of notes playing simultaneously per part.
-// No point making it more than MT32EMU_MAX_PARTIALS, since each note needs at least one partial.
-#define MT32EMU_MAX_POLY 32
 
 // If non-zero, deletes reverb buffers that are not in use to save memory.
 // If zero, keeps reverb buffers for all modes around all the time to avoid allocating/freeing in the critical path.
 #define MT32EMU_REDUCE_REVERB_MEMORY 1
 
-// 0: Use legacy Freeverb
-// 1: Use Accurate Reverb model aka AReverb
-// 2: Use Bit-perfect Boss Reverb model aka BReverb (for developers, not much practical use)
-#define MT32EMU_USE_REVERBMODEL 1
+// 0: Maximum speed at the cost of a bit lower emulation accuracy.
+// 1: Maximum achievable emulation accuracy.
+#define MT32EMU_BOSS_REVERB_PRECISE_MODE 0
 
-// 0: Use refined wave generator based on logarithmic fixed-point computations and LUTs
-// 1: Use legacy accurate wave generator based on float computations
-#define MT32EMU_ACCURATE_WG 0
+// 0: Use 16-bit signed samples and refined wave generator based on logarithmic fixed-point computations and LUTs. Maximum emulation accuracy and speed.
+// 1: Use float samples in the wave generator and renderer. Maximum output quality and minimum noise.
+#define MT32EMU_USE_FLOAT_SAMPLES 0
 
 namespace MT32Emu
 {
+// The default value for the maximum number of partials playing simultaneously.
+const unsigned int DEFAULT_MAX_PARTIALS = 32;
+
 // The higher this number, the more memory will be used, but the more samples can be processed in one run -
 // various parts of sample generation can be processed more efficiently in a single run.
 // A run's maximum length is that given to Synth::render(), so giving a value here higher than render() is ever
@@ -90,11 +87,14 @@ namespace MT32Emu
 // This value must be >= 1.
 const unsigned int MAX_SAMPLES_PER_RUN = 4096;
 
-// This determines the amount of memory available for simulating delays.
-// If set too low, partials aborted to allow other partials to play will not end gracefully, but will terminate
-// abruptly and potentially cause a pop/crackle in the audio output.
-// This value must be >= 1.
-const unsigned int MAX_PRERENDER_SAMPLES = 1024;
+// The default size of the internal MIDI event queue.
+// It holds the incoming MIDI events before the rendering engine actually processes them.
+// The main goal is to fairly emulate the real hardware behaviour which obviously
+// uses an internal MIDI event queue to gather incoming data as well as the delays
+// introduced by transferring data via the MIDI interface.
+// This also facilitates building of an external rendering loop
+// as the queue stores timestamped MIDI events.
+const unsigned int DEFAULT_MIDI_EVENT_QUEUE_SIZE = 1024;
 }
 
 #include "Structures.h"
@@ -103,7 +103,6 @@ const unsigned int MAX_PRERENDER_SAMPLES = 1024;
 #include "Poly.h"
 #include "LA32Ramp.h"
 #include "LA32WaveGenerator.h"
-#include "LegacyWaveGenerator.h"
 #include "TVA.h"
 #include "TVP.h"
 #include "TVF.h"






More information about the Scummvm-git-logs mailing list