/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
 * Copyright (C) 2011-2016 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 <cstdio>

#include "internals.h"

#include "Synth.h"
#include "Analog.h"
#include "BReverbModel.h"
#include "File.h"
#include "MemoryRegion.h"
#include "MidiEventQueue.h"
#include "Part.h"
#include "Partial.h"
#include "PartialManager.h"
#include "Poly.h"
#include "ROMInfo.h"
#include "TVA.h"

namespace MT32Emu {

// MIDI interface data transfer rate in samples. Used to simulate the transfer delay.
static const double MIDI_DATA_TRANSFER_RATE = double(SAMPLE_RATE) / 31250.0 * 8.0;

// FIXME: there should be more specific feature sets for various MT-32 control ROM versions
static const ControlROMFeatureSet OLD_MT32_COMPATIBLE = { true, true, true };
static const ControlROMFeatureSet CM32L_COMPATIBLE = { false, false, false };

static const ControlROMMap ControlROMMaps[7] = {
	//     ID                Features        PCMmap  PCMc  tmbrA  tmbrAO, tmbrAC tmbrB   tmbrBO  tmbrBC tmbrR   trC rhythm rhyC  rsrv   panpot   prog   rhyMax  patMax  sysMax  timMax  sndGrp sGC
	{ "ctrl_mt32_1_04", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x7064, 19 },
	{ "ctrl_mt32_1_05", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x70CA, 19 },
	{ "ctrl_mt32_1_06", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57F4, 0x57E2, 0x5264, 0x5270, 0x5280, 0x521C, 0x70CA, 19 },
	{ "ctrl_mt32_1_07", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57CC, 0x57BA, 0x523C, 0x5248, 0x5258, 0x51F4, 0x70B0, 19 }, // MT-32 revision 1
	{"ctrl_mt32_bluer", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x741C, 85, 0x57E5, 0x5800, 0x57EE, 0x5270, 0x527C, 0x528C, 0x5228, 0x70CE, 19 }, // MT-32 Blue Ridge mod
	{"ctrl_cm32l_1_00",  CM32L_COMPATIBLE,   0x8100, 256, 0x8000, 0x8000, true,  0x8080, 0x8000, true,  0x8500, 64, 0x8580, 85, 0x4F65, 0x4F80, 0x4F6E, 0x48A1, 0x48A5, 0x48BE, 0x48D5, 0x5A6C, 19 },
	{"ctrl_cm32l_1_02",  CM32L_COMPATIBLE,   0x8100, 256, 0x8000, 0x8000, true,  0x8080, 0x8000, true,  0x8500, 64, 0x8580, 85, 0x4F93, 0x4FAE, 0x4F9C, 0x48CB, 0x48CF, 0x48E8, 0x48FF, 0x5A96, 19 }  // CM-32L
	// (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp)
};

static const PartialState PARTIAL_PHASE_TO_STATE[8] = {
	PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK,
	PartialState_SUSTAIN, PartialState_SUSTAIN, PartialState_RELEASE, PartialState_INACTIVE
};

static inline PartialState getPartialState(PartialManager *partialManager, unsigned int partialNum) {
	const Partial *partial = partialManager->getPartial(partialNum);
	return partial->isActive() ? PARTIAL_PHASE_TO_STATE[partial->getTVA()->getPhase()] : PartialState_INACTIVE;
}

#if MT32EMU_USE_FLOAT_SAMPLES
static inline Bit16s convertSample(float sample) {
	return Synth::clipSampleEx(Bit32s(sample * 16384.0f)); // This multiplier takes into account the DAC bit shift
}
#else
static inline float convertSample(Bit16s sample) {
	return float(sample) / 16384.0f; // This multiplier takes into account the DAC bit shift
}
#endif

class SampleFormatConverter {
protected:
#if MT32EMU_USE_FLOAT_SAMPLES
	Bit16s *outBuffer;
#else
	float *outBuffer;
#endif

public:
	Sample *sampleBuffer;

	SampleFormatConverter(Sample *buffer) : outBuffer(NULL), sampleBuffer(buffer) {}

	inline bool isConversionNeeded() {
		return outBuffer != NULL;
	}

	inline void convert(Bit32u len) {
		if (sampleBuffer == NULL) return;
		if (outBuffer == NULL) {
			sampleBuffer += len;
			return;
		}
		Sample *inBuffer = sampleBuffer;
		while (len--) {
			*(outBuffer++) = convertSample(*(inBuffer++));
		}
	}

	inline void addSilence(Bit32u len) {
		if (outBuffer != NULL) {
			Synth::muteSampleBuffer(outBuffer, len);
			outBuffer += len;
		} else if (sampleBuffer != NULL) {
			Synth::muteSampleBuffer(sampleBuffer, len);
			sampleBuffer += len;
		}
	}
};

template <int BUFFER_SIZE_MULTIPLIER = 1>
class BufferedSampleFormatConverter : public SampleFormatConverter {
	Sample renderingBuffer[BUFFER_SIZE_MULTIPLIER * MAX_SAMPLES_PER_RUN];

public:
#if MT32EMU_USE_FLOAT_SAMPLES
	BufferedSampleFormatConverter(Bit16s *buffer)
#else
	BufferedSampleFormatConverter(float *buffer)
#endif
		: SampleFormatConverter(renderingBuffer)
	{
		outBuffer = buffer;
		if (buffer == NULL) sampleBuffer = NULL;
	}
};

class Renderer {
	Synth &synth;

public:
	Renderer(Synth &useSynth) : synth(useSynth) {}

	void render(SampleFormatConverter &converter, Bit32u len);
	void renderStreams(SampleFormatConverter &nonReverbLeft, SampleFormatConverter &nonReverbRight, SampleFormatConverter &reverbDryLeft, SampleFormatConverter &reverbDryRight, SampleFormatConverter &reverbWetLeft, SampleFormatConverter &reverbWetRight, Bit32u len);
	void produceLA32Output(Sample *buffer, Bit32u len);
	void convertSamplesToOutput(Sample *buffer, Bit32u len);
	void doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len);
};

Bit32u Synth::getLibraryVersionInt() {
	return (MT32EMU_VERSION_MAJOR << 16) | (MT32EMU_VERSION_MINOR << 8) | (MT32EMU_VERSION_PATCH);
}

const char *Synth::getLibraryVersionString() {
	return MT32EMU_VERSION;
}

Bit8u Synth::calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum) {
	unsigned int checksum = -initChecksum;
	for (unsigned int i = 0; i < len; i++) {
		checksum -= data[i];
	}
	return Bit8u(checksum & 0x7f);
}

Bit32u Synth::getStereoOutputSampleRate(AnalogOutputMode analogOutputMode) {
	static const unsigned int SAMPLE_RATES[] = {SAMPLE_RATE, SAMPLE_RATE, SAMPLE_RATE * 3 / 2, SAMPLE_RATE * 3};

	return SAMPLE_RATES[analogOutputMode];
}

Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams), mt32default(*new MemParams), renderer(*new Renderer(*this)) {
	opened = false;
	reverbOverridden = false;
	partialCount = DEFAULT_MAX_PARTIALS;
	controlROMMap = NULL;
	controlROMFeatures = NULL;

	if (useReportHandler == NULL) {
		reportHandler = new ReportHandler;
		isDefaultReportHandler = true;
	} else {
		reportHandler = useReportHandler;
		isDefaultReportHandler = false;
	}

	for (int i = 0; i < 4; i++) {
		reverbModels[i] = NULL;
	}
	reverbModel = NULL;
	analog = NULL;
	setDACInputMode(DACInputMode_NICE);
	setMIDIDelayMode(MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY);
	setOutputGain(1.0f);
	setReverbOutputGain(1.0f);
	setReversedStereoEnabled(false);

	patchTempMemoryRegion = NULL;
	rhythmTempMemoryRegion = NULL;
	timbreTempMemoryRegion = NULL;
	patchesMemoryRegion = NULL;
	timbresMemoryRegion = NULL;
	systemMemoryRegion = NULL;
	displayMemoryRegion = NULL;
	resetMemoryRegion = NULL;
	paddedTimbreMaxTable = NULL;

	partialManager = NULL;
	pcmWaves = NULL;
	pcmROMData = NULL;
	soundGroupNames = NULL;
	midiQueue = NULL;
	lastReceivedMIDIEventTimestamp = 0;
	memset(parts, 0, sizeof(parts));
	renderedSampleCount = 0;

	reserved = NULL;
}

Synth::~Synth() {
	close(); // Make sure we're closed and everything is freed
	if (isDefaultReportHandler) {
		delete reportHandler;
	}
	delete &mt32ram;
	delete &mt32default;
	delete &renderer;
}

void ReportHandler::showLCDMessage(const char *data) {
	printf("WRITE-LCD: %s\n", data);
}

void ReportHandler::printDebug(const char *fmt, va_list list) {
	vprintf(fmt, list);
	printf("\n");
}

void Synth::newTimbreSet(Bit8u partNum, Bit8u timbreGroup, Bit8u timbreNumber, const char patchName[]) {
	const char *soundGroupName;
	switch (timbreGroup) {
	case 1:
		timbreNumber += 64;
		// Fall-through
	case 0:
		soundGroupName = soundGroupNames[soundGroupIx[timbreNumber]];
		break;
	case 2:
		soundGroupName = soundGroupNames[controlROMMap->soundGroupsCount - 2];
		break;
	case 3:
		soundGroupName = soundGroupNames[controlROMMap->soundGroupsCount - 1];
		break;
	default:
		soundGroupName = NULL;
		break;
	}
	reportHandler->onProgramChanged(partNum, soundGroupName, patchName);
}

void Synth::printDebug(const char *fmt, ...) {
	va_list ap;
	va_start(ap, fmt);
#if MT32EMU_DEBUG_SAMPLESTAMPS > 0
	reportHandler->printDebug("[%u]", (va_list)&renderedSampleCount);
#endif
	reportHandler->printDebug(fmt, ap);
	va_end(ap);
}

void Synth::setReverbEnabled(bool newReverbEnabled) {
	if (!opened) return;
	if (isReverbEnabled() == newReverbEnabled) return;
	if (newReverbEnabled) {
		bool oldReverbOverridden = reverbOverridden;
		reverbOverridden = false;
		refreshSystemReverbParameters();
		reverbOverridden = oldReverbOverridden;
	} else {
#if MT32EMU_REDUCE_REVERB_MEMORY
		reverbModel->close();
#endif
		reverbModel = NULL;
	}
}

bool Synth::isReverbEnabled() const {
	return reverbModel != NULL;
}

void Synth::setReverbOverridden(bool newReverbOverridden) {
	reverbOverridden = newReverbOverridden;
}

bool Synth::isReverbOverridden() const {
	return reverbOverridden;
}

void Synth::setReverbCompatibilityMode(bool mt32CompatibleMode) {
	if (!opened || (isMT32ReverbCompatibilityMode() == mt32CompatibleMode)) return;
	bool oldReverbEnabled = isReverbEnabled();
	setReverbEnabled(false);
	for (int i = 0; i < 4; i++) {
		delete reverbModels[i];
	}
	initReverbModels(mt32CompatibleMode);
	setReverbEnabled(oldReverbEnabled);
	setReverbOutputGain(reverbOutputGain);
}

bool Synth::isMT32ReverbCompatibilityMode() const {
	return opened && (reverbModels[REVERB_MODE_ROOM]->isMT32Compatible(REVERB_MODE_ROOM));
}

bool Synth::isDefaultReverbMT32Compatible() const {
	return opened && controlROMFeatures->defaultReverbMT32Compatible;
}

void Synth::setDACInputMode(DACInputMode mode) {
#if MT32EMU_USE_FLOAT_SAMPLES
	// We aren't emulating these in float mode, so better to inform the invoker
	if ((mode == DACInputMode_GENERATION1) || (mode == DACInputMode_GENERATION2)) {
		mode = DACInputMode_NICE;
	}
#endif
	dacInputMode = mode;
}

DACInputMode Synth::getDACInputMode() const {
	return dacInputMode;
}

void Synth::setMIDIDelayMode(MIDIDelayMode mode) {
	midiDelayMode = mode;
}

MIDIDelayMode Synth::getMIDIDelayMode() const {
	return midiDelayMode;
}

void Synth::setOutputGain(float newOutputGain) {
	if (newOutputGain < 0.0f) newOutputGain = -newOutputGain;
	outputGain = newOutputGain;
	if (analog != NULL) analog->setSynthOutputGain(newOutputGain);
}

float Synth::getOutputGain() const {
	return outputGain;
}

void Synth::setReverbOutputGain(float newReverbOutputGain) {
	if (newReverbOutputGain < 0.0f) newReverbOutputGain = -newReverbOutputGain;
	reverbOutputGain = newReverbOutputGain;
	if (analog != NULL) analog->setReverbOutputGain(newReverbOutputGain, isMT32ReverbCompatibilityMode());
}

float Synth::getReverbOutputGain() const {
	return reverbOutputGain;
}

void Synth::setReversedStereoEnabled(bool enabled) {
	reversedStereoEnabled = enabled;
}

bool Synth::isReversedStereoEnabled() const {
	return reversedStereoEnabled;
}

bool Synth::loadControlROM(const ROMImage &controlROMImage) {
	File *file = controlROMImage.getFile();
	const ROMInfo *controlROMInfo = controlROMImage.getROMInfo();
	if ((controlROMInfo == NULL)
			|| (controlROMInfo->type != ROMInfo::Control)
			|| (controlROMInfo->pairType != ROMInfo::Full)) {
#if MT32EMU_MONITOR_INIT
		printDebug("Invalid Control ROM Info provided");
#endif
		return false;
	}

#if MT32EMU_MONITOR_INIT
	printDebug("Found Control ROM: %s, %s", controlROMInfo->shortName, controlROMInfo->description);
#endif
	const Bit8u *fileData = file->getData();
	memcpy(controlROMData, fileData, CONTROL_ROM_SIZE);

	// Control ROM successfully loaded, now check whether it's a known type
	controlROMMap = NULL;
	controlROMFeatures = NULL;
	for (unsigned int i = 0; i < sizeof(ControlROMMaps) / sizeof(ControlROMMaps[0]); i++) {
		if (strcmp(controlROMInfo->shortName, ControlROMMaps[i].shortName) == 0) {
			controlROMMap = &ControlROMMaps[i];
			controlROMFeatures = &controlROMMap->featureSet;
			return true;
		}
	}
#if MT32EMU_MONITOR_INIT
	printDebug("Control ROM failed to load");
#endif
	return false;
}

bool Synth::loadPCMROM(const ROMImage &pcmROMImage) {
	File *file = pcmROMImage.getFile();
	const ROMInfo *pcmROMInfo = pcmROMImage.getROMInfo();
	if ((pcmROMInfo == NULL)
			|| (pcmROMInfo->type != ROMInfo::PCM)
			|| (pcmROMInfo->pairType != ROMInfo::Full)) {
		return false;
	}
#if MT32EMU_MONITOR_INIT
	printDebug("Found PCM ROM: %s, %s", pcmROMInfo->shortName, pcmROMInfo->description);
#endif
	size_t fileSize = file->getSize();
	if (fileSize != (2 * pcmROMSize)) {
#if MT32EMU_MONITOR_INIT
		printDebug("PCM ROM file has wrong size (expected %d, got %d)", 2 * pcmROMSize, fileSize);
#endif
		return false;
	}
	const Bit8u *fileData = file->getData();
	for (size_t i = 0; i < pcmROMSize; i++) {
		Bit8u s = *(fileData++);
		Bit8u c = *(fileData++);

		int order[16] = {0, 9, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8};

		Bit16s log = 0;
		for (int u = 0; u < 15; u++) {
			int bit;
			if (order[u] < 8) {
				bit = (s >> (7 - order[u])) & 0x1;
			} else {
				bit = (c >> (7 - (order[u] - 8))) & 0x1;
			}
			log = log | Bit16s(bit << (15 - u));
		}
		pcmROMData[i] = log;
	}
	return true;
}

bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) {
	ControlROMPCMStruct *tps = reinterpret_cast<ControlROMPCMStruct *>(&controlROMData[mapAddress]);
	for (int i = 0; i < count; i++) {
		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;
		}
		pcmWaves[i].addr = rAddr;
		pcmWaves[i].len = rLen;
		pcmWaves[i].loop = (tps[i].len & 0x80) != 0;
		pcmWaves[i].controlROMPCMStruct = &tps[i];
		//int pitch = (tps[i].pitchMSB << 8) | tps[i].pitchLSB;
		//bool unaffectedByMasterTune = (tps[i].len & 0x01) == 0;
		//printDebug("PCM %d: pos=%d, len=%d, pitch=%d, loop=%s, unaffectedByMasterTune=%s", i, rAddr, rLen, pitch, pcmWaves[i].loop ? "YES" : "NO", unaffectedByMasterTune ? "YES" : "NO");
	}
	return false;
}

bool Synth::initCompressedTimbre(Bit16u timbreNum, const Bit8u *src, Bit32u srcLen) {
	// "Compressed" here means that muted partials aren't present in ROM (except in the case of partial 0 being muted).
	// Instead the data from the previous unmuted partial is used.
	if (srcLen < sizeof(TimbreParam::CommonParam)) {
		return false;
	}
	TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre;
	timbresMemoryRegion->write(timbreNum, 0, src, sizeof(TimbreParam::CommonParam), true);
	unsigned int srcPos = sizeof(TimbreParam::CommonParam);
	unsigned int memPos = sizeof(TimbreParam::CommonParam);
	for (int t = 0; t < 4; t++) {
		if (t != 0 && ((timbre->common.partialMute >> t) & 0x1) == 0x00) {
			// This partial is muted - we'll copy the previously copied partial, then
			srcPos -= sizeof(TimbreParam::PartialParam);
		} else if (srcPos + sizeof(TimbreParam::PartialParam) >= srcLen) {
			return false;
		}
		timbresMemoryRegion->write(timbreNum, memPos, src + srcPos, sizeof(TimbreParam::PartialParam));
		srcPos += sizeof(TimbreParam::PartialParam);
		memPos += sizeof(TimbreParam::PartialParam);
	}
	return true;
}

bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, Bit16u count, Bit16u startTimbre, bool compressed) {
	const Bit8u *timbreMap = &controlROMData[mapAddress];
	for (Bit16u i = 0; i < count * 2; i += 2) {
		Bit16u address = (timbreMap[i + 1] << 8) | timbreMap[i];
		if (!compressed && (address + offset + sizeof(TimbreParam) > CONTROL_ROM_SIZE)) {
			printDebug("Control ROM error: Timbre map entry 0x%04x for timbre %d points to invalid timbre address 0x%04x", i, startTimbre, address);
			return false;
		}
		address += offset;
		if (compressed) {
			if (!initCompressedTimbre(startTimbre, &controlROMData[address], CONTROL_ROM_SIZE - address)) {
				printDebug("Control ROM error: Timbre map entry 0x%04x for timbre %d points to invalid timbre at 0x%04x", i, startTimbre, address);
				return false;
			}
		} else {
			timbresMemoryRegion->write(startTimbre, 0, &controlROMData[address], sizeof(TimbreParam), true);
		}
		startTimbre++;
	}
	return true;
}

void Synth::initReverbModels(bool mt32CompatibleMode) {
	reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM, mt32CompatibleMode);
	reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL, mt32CompatibleMode);
	reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE, mt32CompatibleMode);
	reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY, mt32CompatibleMode);
#if !MT32EMU_REDUCE_REVERB_MEMORY
	for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) {
		reverbModels[i]->open();
	}
#endif
}

void Synth::initSoundGroups(char newSoundGroupNames[][9]) {
	memcpy(soundGroupIx, &controlROMData[controlROMMap->soundGroupsTable - sizeof(soundGroupIx)], sizeof(soundGroupIx));
	const SoundGroup *table = reinterpret_cast<SoundGroup *>(&controlROMData[controlROMMap->soundGroupsTable]);
	for (unsigned int i = 0; i < controlROMMap->soundGroupsCount; i++) {
		memcpy(&newSoundGroupNames[i][0], table[i].name, sizeof(table[i].name));
	}
}

bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode) {
	return open(controlROMImage, pcmROMImage, DEFAULT_MAX_PARTIALS, analogOutputMode);
}

bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount, AnalogOutputMode analogOutputMode) {
	if (opened) {
		return false;
	}
	partialCount = usePartialCount;
	abortingPoly = NULL;

	// This is to help detect bugs
	memset(&mt32ram, '?', sizeof(mt32ram));

#if MT32EMU_MONITOR_INIT
	printDebug("Loading Control ROM");
#endif
	if (!loadControlROM(controlROMImage)) {
		printDebug("Init Error - Missing or invalid Control ROM image");
		reportHandler->onErrorControlROM();
		dispose();
		return false;
	}

	initMemoryRegions();

	// 512KB PCM ROM for MT-32, etc.
	// 1MB PCM ROM for CM-32L, LAPC-I, CM-64, CM-500
	// Note that the size below is given in samples (16-bit), not bytes
	pcmROMSize = controlROMMap->pcmCount == 256 ? 512 * 1024 : 256 * 1024;
	pcmROMData = new Bit16s[pcmROMSize];

#if MT32EMU_MONITOR_INIT
	printDebug("Loading PCM ROM");
#endif
	if (!loadPCMROM(pcmROMImage)) {
		printDebug("Init Error - Missing PCM ROM image");
		reportHandler->onErrorPCMROM();
		dispose();
		return false;
	}

#if MT32EMU_MONITOR_INIT
	printDebug("Initialising Reverb Models");
#endif
	bool mt32CompatibleReverb = controlROMFeatures->defaultReverbMT32Compatible;
#if MT32EMU_MONITOR_INIT
	printDebug("Using %s Compatible Reverb Models", mt32CompatibleReverb ? "MT-32" : "CM-32L");
#endif
	initReverbModels(mt32CompatibleReverb);

#if MT32EMU_MONITOR_INIT
	printDebug("Initialising Timbre Bank A");
#endif
	if (!initTimbres(controlROMMap->timbreAMap, controlROMMap->timbreAOffset, 0x40, 0, controlROMMap->timbreACompressed)) {
		dispose();
		return false;
	}

#if MT32EMU_MONITOR_INIT
	printDebug("Initialising Timbre Bank B");
#endif
	if (!initTimbres(controlROMMap->timbreBMap, controlROMMap->timbreBOffset, 0x40, 64, controlROMMap->timbreBCompressed)) {
		dispose();
		return false;
	}

#if MT32EMU_MONITOR_INIT
	printDebug("Initialising Timbre Bank R");
#endif
	if (!initTimbres(controlROMMap->timbreRMap, 0, controlROMMap->timbreRCount, 192, true)) {
		dispose();
		return false;
	}

#if MT32EMU_MONITOR_INIT
	printDebug("Initialising Timbre Bank M");
#endif
	// CM-64 seems to initialise all bytes in this bank to 0.
	memset(&mt32ram.timbres[128], 0, sizeof(mt32ram.timbres[128]) * 64);

	partialManager = new PartialManager(this, parts);

	pcmWaves = new PCMWaveEntry[controlROMMap->pcmCount];

#if MT32EMU_MONITOR_INIT
	printDebug("Initialising PCM List");
#endif
	initPCMList(controlROMMap->pcmTable, controlROMMap->pcmCount);

#if MT32EMU_MONITOR_INIT
	printDebug("Initialising Rhythm Temp");
#endif
	memcpy(mt32ram.rhythmTemp, &controlROMData[controlROMMap->rhythmSettings], controlROMMap->rhythmSettingsCount * 4);

#if MT32EMU_MONITOR_INIT
	printDebug("Initialising Patches");
#endif
	for (Bit8u i = 0; i < 128; i++) {
		PatchParam *patch = &mt32ram.patches[i];
		patch->timbreGroup = i / 64;
		patch->timbreNum = i % 64;
		patch->keyShift = 24;
		patch->fineTune = 50;
		patch->benderRange = 12;
		patch->assignMode = 0;
		patch->reverbSwitch = 1;
		patch->dummy = 0;
	}

#if MT32EMU_MONITOR_INIT
	printDebug("Initialising System");
#endif
	// The MT-32 manual claims that "Standard pitch" is 442Hz.
	mt32ram.system.masterTune = 0x4A; // Confirmed on CM-64
	mt32ram.system.reverbMode = 0; // Confirmed
	mt32ram.system.reverbTime = 5; // Confirmed
	mt32ram.system.reverbLevel = 3; // Confirmed
	memcpy(mt32ram.system.reserveSettings, &controlROMData[controlROMMap->reserveSettings], 9); // Confirmed
	for (Bit8u i = 0; i < 9; i++) {
		// This is the default: {1, 2, 3, 4, 5, 6, 7, 8, 9}
		// An alternative configuration can be selected by holding "Master Volume"
		// and pressing "PART button 1" on the real MT-32's frontpanel.
		// The channel assignment is then {0, 1, 2, 3, 4, 5, 6, 7, 9}
		mt32ram.system.chanAssign[i] = i + 1;
	}
	mt32ram.system.masterVol = 100; // Confirmed

	bool oldReverbOverridden = reverbOverridden;
	reverbOverridden = false;
	refreshSystem();
	reverbOverridden = oldReverbOverridden;

	char(*writableSoundGroupNames)[9] = new char[controlROMMap->soundGroupsCount][9];
	soundGroupNames = writableSoundGroupNames;
	initSoundGroups(writableSoundGroupNames);

	for (int i = 0; i < 9; i++) {
		MemParams::PatchTemp *patchTemp = &mt32ram.patchTemp[i];

		// Note that except for the rhythm part, these patch fields will be set in setProgram() below anyway.
		patchTemp->patch.timbreGroup = 0;
		patchTemp->patch.timbreNum = 0;
		patchTemp->patch.keyShift = 24;
		patchTemp->patch.fineTune = 50;
		patchTemp->patch.benderRange = 12;
		patchTemp->patch.assignMode = 0;
		patchTemp->patch.reverbSwitch = 1;
		patchTemp->patch.dummy = 0;

		patchTemp->outputLevel = 80;
		patchTemp->panpot = controlROMData[controlROMMap->panSettings + i];
		memset(patchTemp->dummyv, 0, sizeof(patchTemp->dummyv));
		patchTemp->dummyv[1] = 127;

		if (i < 8) {
			parts[i] = new Part(this, i);
			parts[i]->setProgram(controlROMData[controlROMMap->programSettings + i]);
		} else {
			parts[i] = new RhythmPart(this, i);
		}
	}

	// For resetting mt32 mid-execution
	mt32default = mt32ram;

	midiQueue = new MidiEventQueue();

	analog = new Analog(analogOutputMode, controlROMFeatures->oldMT32AnalogLPF);
	setOutputGain(outputGain);
	setReverbOutputGain(reverbOutputGain);

	opened = true;
	activated = false;

#if MT32EMU_MONITOR_INIT
	printDebug("*** Initialisation complete ***");
#endif
	return true;
}

void Synth::dispose() {
	opened = false;

	delete midiQueue;
	midiQueue = NULL;

	delete analog;
	analog = NULL;

	delete partialManager;
	partialManager = NULL;

	for (int i = 0; i < 9; i++) {
		delete parts[i];
		parts[i] = NULL;
	}

	delete[] soundGroupNames;
	soundGroupNames = NULL;

	delete[] pcmWaves;
	pcmWaves = NULL;

	delete[] pcmROMData;
	pcmROMData = NULL;

	deleteMemoryRegions();

	for (int i = 0; i < 4; i++) {
		delete reverbModels[i];
		reverbModels[i] = NULL;
	}
	reverbModel = NULL;
	controlROMFeatures = NULL;
	controlROMMap = NULL;
}

void Synth::close() {
	if (opened) {
		dispose();
	}
}

bool Synth::isOpen() const {
	return opened;
}

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

Bit32u Synth::setMIDIEventQueueSize(Bit32u useSize) {
	static const Bit32u MAX_QUEUE_SIZE = (1 << 24); // This results in about 256 Mb - much greater than any reasonable value

	if (midiQueue == NULL) return 0;
	flushMIDIQueue();

	// Find a power of 2 that is >= useSize
	Bit32u binarySize = 1;
	if (useSize < MAX_QUEUE_SIZE) {
		// Using simple linear search as this isn't time critical
		while (binarySize < useSize) binarySize <<= 1;
	} else {
		binarySize = MAX_QUEUE_SIZE;
	}
	delete midiQueue;
	midiQueue = new MidiEventQueue(binarySize);
	return binarySize;
}

Bit32u Synth::getShortMessageLength(Bit32u msg) {
	if ((msg & 0xF0) == 0xF0) {
		switch (msg & 0xFF) {
			case 0xF1:
			case 0xF3:
				return 2;
			case 0xF2:
				return 3;
			default:
				return 1;
		}
	}
	// NOTE: This calculation isn't quite correct
	// as it doesn't consider the running status byte
	return ((msg & 0xE0) == 0xC0) ? 2 : 3;
}

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 ((msg & 0xF8) == 0xF8) {
		reportHandler->onMIDISystemRealtime(Bit8u(msg));
		return true;
	}
	if (midiQueue == NULL) return false;
	if (midiDelayMode != MIDIDelayMode_IMMEDIATE) {
		timestamp = addMIDIInterfaceDelay(getShortMessageLength(msg), timestamp);
	}
	if (!activated) activated = true;
	do {
		if (midiQueue->pushShortMessage(msg, timestamp)) return true;
	} while (reportHandler->onMIDIQueueOverflow());
	return false;
}

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);
	}
	if (!activated) activated = true;
	do {
		if (midiQueue->pushSysex(sysex, len, timestamp)) return true;
	} while (reportHandler->onMIDIQueueOverflow());
	return false;
}

void Synth::playMsgNow(Bit32u msg) {
	if (!opened) return;

	// NOTE: Active sense IS implemented in real hardware. However, realtime processing is clearly out of the library scope.
	//       It is assumed that realtime consumers of the library respond to these MIDI events as appropriate.

	Bit8u code = Bit8u((msg & 0x0000F0) >> 4);
	Bit8u chan = Bit8u(msg & 0x00000F);
	Bit8u note = Bit8u((msg & 0x007F00) >> 8);
	Bit8u velocity = Bit8u((msg & 0x7F0000) >> 16);

	//printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);

	Bit8u part = chantable[chan];
	if (part > 8) {
#if MT32EMU_MONITOR_MIDI > 0
		printDebug("Play msg on unreg chan %d (%d): code=0x%01x, vel=%d", chan, part, code, velocity);
#endif
		return;
	}
	playMsgOnPart(part, code, note, velocity);
}

void Synth::playMsgOnPart(Bit8u part, Bit8u code, Bit8u note, Bit8u velocity) {
	if (!opened) return;

	Bit32u bend;

	if (!activated) activated = true;
	//printDebug("Synth::playMsgOnPart(%02x, %02x, %02x, %02x)", part, code, note, velocity);
	switch (code) {
	case 0x8:
		//printDebug("Note OFF - Part %d", part);
		// The MT-32 ignores velocity for note off
		parts[part]->noteOff(note);
		break;
	case 0x9:
		//printDebug("Note ON - Part %d, Note %d Vel %d", part, note, velocity);
		if (velocity == 0) {
			// MIDI defines note-on with velocity 0 as being the same as note-off with velocity 40
			parts[part]->noteOff(note);
		} else {
			parts[part]->noteOn(note, velocity);
		}
		break;
	case 0xB: // Control change
		switch (note) {
		case 0x01:  // Modulation
			//printDebug("Modulation: %d", velocity);
			parts[part]->setModulation(velocity);
			break;
		case 0x06:
			parts[part]->setDataEntryMSB(velocity);
			break;
		case 0x07:  // Set volume
			//printDebug("Volume set: %d", velocity);
			parts[part]->setVolume(velocity);
			break;
		case 0x0A:  // Pan
			//printDebug("Pan set: %d", velocity);
			parts[part]->setPan(velocity);
			break;
		case 0x0B:
			//printDebug("Expression set: %d", velocity);
			parts[part]->setExpression(velocity);
			break;
		case 0x40: // Hold (sustain) pedal
			//printDebug("Hold pedal set: %d", velocity);
			parts[part]->setHoldPedal(velocity >= 64);
			break;

		case 0x62:
		case 0x63:
			parts[part]->setNRPN();
			break;
		case 0x64:
			parts[part]->setRPNLSB(velocity);
			break;
		case 0x65:
			parts[part]->setRPNMSB(velocity);
			break;

		case 0x79: // Reset all controllers
			//printDebug("Reset all controllers");
			parts[part]->resetAllControllers();
			break;

		case 0x7B: // All notes off
			//printDebug("All notes off");
			parts[part]->allNotesOff();
			break;

		case 0x7C:
		case 0x7D:
		case 0x7E:
		case 0x7F:
			// CONFIRMED:Mok: A real LAPC-I responds to these controllers as follows:
			parts[part]->setHoldPedal(false);
			parts[part]->allNotesOff();
			break;

		default:
#if MT32EMU_MONITOR_MIDI > 0
			printDebug("Unknown MIDI Control code: 0x%02x - vel 0x%02x", note, velocity);
#endif
			return;
		}

		break;
	case 0xC: // Program change
		//printDebug("Program change %01x", note);
		parts[part]->setProgram(note);
		break;
	case 0xE: // Pitch bender
		bend = (velocity << 7) | (note);
		//printDebug("Pitch bender %02x", bend);
		parts[part]->setBend(bend);
		break;
	default:
#if MT32EMU_MONITOR_MIDI > 0
		printDebug("Unknown Midi code: 0x%01x - %02x - %02x", code, note, velocity);
#endif
		return;
	}
	reportHandler->onMIDIMessagePlayed();
}

void Synth::playSysexNow(const Bit8u *sysex, Bit32u len) {
	if (len < 2) {
		printDebug("playSysex: Message is too short for sysex (%d bytes)", len);
	}
	if (sysex[0] != 0xF0) {
		printDebug("playSysex: Message lacks start-of-sysex (0xF0)");
		return;
	}
	// Due to some programs (e.g. Java) sending buffers with junk at the end, we have to go through and find the end marker rather than relying on len.
	Bit32u endPos;
	for (endPos = 1; endPos < len; endPos++) {
		if (sysex[endPos] == 0xF7) {
			break;
		}
	}
	if (endPos == len) {
		printDebug("playSysex: Message lacks end-of-sysex (0xf7)");
		return;
	}
	playSysexWithoutFraming(sysex + 1, endPos - 1);
}

void Synth::playSysexWithoutFraming(const Bit8u *sysex, Bit32u len) {
	if (len < 4) {
		printDebug("playSysexWithoutFraming: Message is too short (%d bytes)!", len);
		return;
	}
	if (sysex[0] != SYSEX_MANUFACTURER_ROLAND) {
		printDebug("playSysexWithoutFraming: Header not intended for this device manufacturer: %02x %02x %02x %02x", int(sysex[0]), int(sysex[1]), int(sysex[2]), int(sysex[3]));
		return;
	}
	if (sysex[2] == SYSEX_MDL_D50) {
		printDebug("playSysexWithoutFraming: Header is intended for model D-50 (not yet supported): %02x %02x %02x %02x", int(sysex[0]), int(sysex[1]), int(sysex[2]), int(sysex[3]));
		return;
	} else if (sysex[2] != SYSEX_MDL_MT32) {
		printDebug("playSysexWithoutFraming: Header not intended for model MT-32: %02x %02x %02x %02x", int(sysex[0]), int(sysex[1]), int(sysex[2]), int(sysex[3]));
		return;
	}
	playSysexWithoutHeader(sysex[1], sysex[3], sysex + 4, len - 4);
}

void Synth::playSysexWithoutHeader(Bit8u device, Bit8u command, const Bit8u *sysex, Bit32u len) {
	if (device > 0x10) {
		// We have device ID 0x10 (default, but changeable, on real MT-32), < 0x10 is for channels
		printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", int(device));
		return;
	}
	// This is checked early in the real devices (before any sysex length checks or further processing)
	// FIXME: Response to SYSEX_CMD_DAT reset with partials active (and in general) is untested.
	if ((command == SYSEX_CMD_DT1 || command == SYSEX_CMD_DAT) && sysex[0] == 0x7F) {
		reset();
		return;
	}

	if (command == SYSEX_CMD_EOD) {
#if MT32EMU_MONITOR_SYSEX > 0
		printDebug("playSysexWithoutHeader: Ignored unsupported command %02x", command);
#endif
		return;
	}
	if (len < 4) {
		printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len);
		return;
	}
	Bit8u checksum = calcSysexChecksum(sysex, len - 1);
	if (checksum != sysex[len - 1]) {
		printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum);
		return;
	}
	len -= 1; // Exclude checksum
	switch (command) {
	case SYSEX_CMD_WSD:
#if MT32EMU_MONITOR_SYSEX > 0
		printDebug("playSysexWithoutHeader: Ignored unsupported command %02x", command);
#endif
		break;
	case SYSEX_CMD_DAT:
		/* Outcommented until we (ever) actually implement handshake communication
		if (hasActivePartials()) {
			printDebug("playSysexWithoutHeader: Got SYSEX_CMD_DAT but partials are active - ignoring");
			// FIXME: We should send SYSEX_CMD_RJC in this case
			break;
		}
		*/
		// FALLTHROUGH
	case SYSEX_CMD_DT1:
		writeSysex(device, sysex, len);
		break;
	case SYSEX_CMD_RQD:
		if (hasActivePartials()) {
			printDebug("playSysexWithoutHeader: Got SYSEX_CMD_RQD but partials are active - ignoring");
			// FIXME: We should send SYSEX_CMD_RJC in this case
			break;
		}
		// FALLTHROUGH
	case SYSEX_CMD_RQ1:
		readSysex(device, sysex, len);
		break;
	default:
		printDebug("playSysexWithoutHeader: Unsupported command %02x", command);
		return;
	}
}

void Synth::readSysex(Bit8u /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/) const {
	// NYI
}

void Synth::writeSysex(Bit8u device, const Bit8u *sysex, Bit32u len) {
	if (!opened) return;
	reportHandler->onMIDIMessagePlayed();
	Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]);
	addr = MT32EMU_MEMADDR(addr);
	sysex += 3;
	len -= 3;
	//printDebug("Sysex addr: 0x%06x", MT32EMU_SYSEXMEMADDR(addr));
	// NOTE: Please keep both lower and upper bounds in each check, for ease of reading

	// Process channel-specific sysex by converting it to device-global
	if (device < 0x10) {
#if MT32EMU_MONITOR_SYSEX > 0
		printDebug("WRITE-CHANNEL: Channel %d temp area 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr));
#endif
		if (/*addr >= MT32EMU_MEMADDR(0x000000) && */addr < MT32EMU_MEMADDR(0x010000)) {
			int offset;
			if (chantable[device] > 8) {
#if MT32EMU_MONITOR_SYSEX > 0
				printDebug(" (Channel not mapped to a part... 0 offset)");
#endif
				offset = 0;
			} else if (chantable[device] == 8) {
#if MT32EMU_MONITOR_SYSEX > 0
				printDebug(" (Channel mapped to rhythm... 0 offset)");
#endif
				offset = 0;
			} else {
				offset = chantable[device] * sizeof(MemParams::PatchTemp);
#if MT32EMU_MONITOR_SYSEX > 0
				printDebug(" (Setting extra offset to %d)", offset);
#endif
			}
			addr += MT32EMU_MEMADDR(0x030000) + offset;
		} else if (/*addr >= MT32EMU_MEMADDR(0x010000) && */ addr < MT32EMU_MEMADDR(0x020000)) {
			addr += MT32EMU_MEMADDR(0x030110) - MT32EMU_MEMADDR(0x010000);
		} else if (/*addr >= MT32EMU_MEMADDR(0x020000) && */ addr < MT32EMU_MEMADDR(0x030000)) {
			int offset;
			if (chantable[device] > 8) {
#if MT32EMU_MONITOR_SYSEX > 0
				printDebug(" (Channel not mapped to a part... 0 offset)");
#endif
				offset = 0;
			} else if (chantable[device] == 8) {
#if MT32EMU_MONITOR_SYSEX > 0
				printDebug(" (Channel mapped to rhythm... 0 offset)");
#endif
				offset = 0;
			} else {
				offset = chantable[device] * sizeof(TimbreParam);
#if MT32EMU_MONITOR_SYSEX > 0
				printDebug(" (Setting extra offset to %d)", offset);
#endif
			}
			addr += MT32EMU_MEMADDR(0x040000) - MT32EMU_MEMADDR(0x020000) + offset;
		} else {
#if MT32EMU_MONITOR_SYSEX > 0
			printDebug(" Invalid channel");
#endif
			return;
		}
	}

	// Process device-global sysex (possibly converted from channel-specific sysex above)
	for (;;) {
		// Find the appropriate memory region
		const MemoryRegion *region = findMemoryRegion(addr);

		if (region == NULL) {
			printDebug("Sysex write to unrecognised address %06x, len %d", MT32EMU_SYSEXMEMADDR(addr), len);
			break;
		}
		writeMemoryRegion(region, addr, region->getClampedLen(addr, len), sysex);

		Bit32u next = region->next(addr, len);
		if (next == 0) {
			break;
		}
		addr += next;
		sysex += next;
		len -= next;
	}
}

void Synth::readMemory(Bit32u addr, Bit32u len, Bit8u *data) {
	if (!opened) return;
	const MemoryRegion *region = findMemoryRegion(addr);
	if (region != NULL) {
		readMemoryRegion(region, addr, len, data);
	}
}

void Synth::initMemoryRegions() {
	// Timbre max tables are slightly more complicated than the others, which are used directly from the ROM.
	// The ROM (sensibly) just has maximums for TimbreParam.commonParam followed by just one TimbreParam.partialParam,
	// so we produce a table with all partialParams filled out, as well as padding for PaddedTimbre, for quick lookup.
	paddedTimbreMaxTable = new Bit8u[sizeof(MemParams::PaddedTimbre)];
	memcpy(&paddedTimbreMaxTable[0], &controlROMData[controlROMMap->timbreMaxTable], sizeof(TimbreParam::CommonParam) + sizeof(TimbreParam::PartialParam)); // commonParam and one partialParam
	int pos = sizeof(TimbreParam::CommonParam) + sizeof(TimbreParam::PartialParam);
	for (int i = 0; i < 3; i++) {
		memcpy(&paddedTimbreMaxTable[pos], &controlROMData[controlROMMap->timbreMaxTable + sizeof(TimbreParam::CommonParam)], sizeof(TimbreParam::PartialParam));
		pos += sizeof(TimbreParam::PartialParam);
	}
	memset(&paddedTimbreMaxTable[pos], 0, 10); // Padding
	patchTempMemoryRegion = new PatchTempMemoryRegion(this, reinterpret_cast<Bit8u *>(&mt32ram.patchTemp[0]), &controlROMData[controlROMMap->patchMaxTable]);
	rhythmTempMemoryRegion = new RhythmTempMemoryRegion(this, reinterpret_cast<Bit8u *>(&mt32ram.rhythmTemp[0]), &controlROMData[controlROMMap->rhythmMaxTable]);
	timbreTempMemoryRegion = new TimbreTempMemoryRegion(this, reinterpret_cast<Bit8u *>(&mt32ram.timbreTemp[0]), paddedTimbreMaxTable);
	patchesMemoryRegion = new PatchesMemoryRegion(this, reinterpret_cast<Bit8u *>(&mt32ram.patches[0]), &controlROMData[controlROMMap->patchMaxTable]);
	timbresMemoryRegion = new TimbresMemoryRegion(this, reinterpret_cast<Bit8u *>(&mt32ram.timbres[0]), paddedTimbreMaxTable);
	systemMemoryRegion = new SystemMemoryRegion(this, reinterpret_cast<Bit8u *>(&mt32ram.system), &controlROMData[controlROMMap->systemMaxTable]);
	displayMemoryRegion = new DisplayMemoryRegion(this);
	resetMemoryRegion = new ResetMemoryRegion(this);
}

void Synth::deleteMemoryRegions() {
	delete patchTempMemoryRegion;
	patchTempMemoryRegion = NULL;
	delete rhythmTempMemoryRegion;
	rhythmTempMemoryRegion = NULL;
	delete timbreTempMemoryRegion;
	timbreTempMemoryRegion = NULL;
	delete patchesMemoryRegion;
	patchesMemoryRegion = NULL;
	delete timbresMemoryRegion;
	timbresMemoryRegion = NULL;
	delete systemMemoryRegion;
	systemMemoryRegion = NULL;
	delete displayMemoryRegion;
	displayMemoryRegion = NULL;
	delete resetMemoryRegion;
	resetMemoryRegion = NULL;

	delete[] paddedTimbreMaxTable;
	paddedTimbreMaxTable = NULL;
}

MemoryRegion *Synth::findMemoryRegion(Bit32u addr) {
	MemoryRegion *regions[] = {
		patchTempMemoryRegion,
		rhythmTempMemoryRegion,
		timbreTempMemoryRegion,
		patchesMemoryRegion,
		timbresMemoryRegion,
		systemMemoryRegion,
		displayMemoryRegion,
		resetMemoryRegion,
		NULL
	};
	for (int pos = 0; regions[pos] != NULL; pos++) {
		if (regions[pos]->contains(addr)) {
			return regions[pos];
		}
	}
	return NULL;
}

void Synth::readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data) {
	unsigned int first = region->firstTouched(addr);
	//unsigned int last = region->lastTouched(addr, len);
	unsigned int off = region->firstTouchedOffset(addr);
	len = region->getClampedLen(addr, len);

	unsigned int m;

	if (region->isReadable()) {
		region->read(first, off, data, len);
	} else {
		// FIXME: We might want to do these properly in future
		for (m = 0; m < len; m += 2) {
			data[m] = 0xff;
			if (m + 1 < len) {
				data[m+1] = Bit8u(region->type);
			}
		}
	}
}

void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data) {
	unsigned int first = region->firstTouched(addr);
	unsigned int last = region->lastTouched(addr, len);
	unsigned int off = region->firstTouchedOffset(addr);
	switch (region->type) {
	case MR_PatchTemp:
		region->write(first, off, data, len);
		//printDebug("Patch temp: Patch %d, offset %x, len %d", off/16, off % 16, len);

		for (unsigned int i = first; i <= last; i++) {
			int absTimbreNum = mt32ram.patchTemp[i].patch.timbreGroup * 64 + mt32ram.patchTemp[i].patch.timbreNum;
			char timbreName[11];
			memcpy(timbreName, mt32ram.timbres[absTimbreNum].timbre.common.name, 10);
			timbreName[10] = 0;
#if MT32EMU_MONITOR_SYSEX > 0
			printDebug("WRITE-PARTPATCH (%d-%d@%d..%d): %d; timbre=%d (%s), outlevel=%d", first, last, off, off + len, i, absTimbreNum, timbreName, mt32ram.patchTemp[i].outputLevel);
#endif
			if (parts[i] != NULL) {
				if (i != 8) {
					// Note: Confirmed on CM-64 that we definitely *should* update the timbre here,
					// but only in the case that the sysex actually writes to those values
					if (i == first && off > 2) {
#if MT32EMU_MONITOR_SYSEX > 0
						printDebug(" (Not updating timbre, since those values weren't touched)");
#endif
					} else {
						parts[i]->setTimbre(&mt32ram.timbres[parts[i]->getAbsTimbreNum()].timbre);
					}
				}
				parts[i]->refresh();
			}
		}
		break;
	case MR_RhythmTemp:
		region->write(first, off, data, len);
		for (unsigned int i = first; i <= last; i++) {
			int timbreNum = mt32ram.rhythmTemp[i].timbre;
			char timbreName[11];
			if (timbreNum < 94) {
				memcpy(timbreName, mt32ram.timbres[128 + timbreNum].timbre.common.name, 10);
				timbreName[10] = 0;
			} else {
				strcpy(timbreName, "[None]");
			}
#if MT32EMU_MONITOR_SYSEX > 0
			printDebug("WRITE-RHYTHM (%d-%d@%d..%d): %d; level=%02x, panpot=%02x, reverb=%02x, timbre=%d (%s)", first, last, off, off + len, i, mt32ram.rhythmTemp[i].outputLevel, mt32ram.rhythmTemp[i].panpot, mt32ram.rhythmTemp[i].reverbSwitch, mt32ram.rhythmTemp[i].timbre, timbreName);
#endif
		}
		if (parts[8] != NULL) {
			parts[8]->refresh();
		}
		break;
	case MR_TimbreTemp:
		region->write(first, off, data, len);
		for (unsigned int i = first; i <= last; i++) {
			char instrumentName[11];
			memcpy(instrumentName, mt32ram.timbreTemp[i].common.name, 10);
			instrumentName[10] = 0;
#if MT32EMU_MONITOR_SYSEX > 0
			printDebug("WRITE-PARTTIMBRE (%d-%d@%d..%d): timbre=%d (%s)", first, last, off, off + len, i, instrumentName);
#endif
			if (parts[i] != NULL) {
				parts[i]->refresh();
			}
		}
		break;
	case MR_Patches:
		region->write(first, off, data, len);
#if MT32EMU_MONITOR_SYSEX > 0
		for (unsigned int i = first; i <= last; i++) {
			PatchParam *patch = &mt32ram.patches[i];
			int patchAbsTimbreNum = patch->timbreGroup * 64 + patch->timbreNum;
			char instrumentName[11];
			memcpy(instrumentName, mt32ram.timbres[patchAbsTimbreNum].timbre.common.name, 10);
			instrumentName[10] = 0;
			Bit8u *n = (Bit8u *)patch;
			printDebug("WRITE-PATCH (%d-%d@%d..%d): %d; timbre=%d (%s) %02X%02X%02X%02X%02X%02X%02X%02X", first, last, off, off + len, i, patchAbsTimbreNum, instrumentName, n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]);
		}
#endif
		break;
	case MR_Timbres:
		// Timbres
		first += 128;
		last += 128;
		region->write(first, off, data, len);
		for (unsigned int i = first; i <= last; i++) {
#if MT32EMU_MONITOR_TIMBRES >= 1
			TimbreParam *timbre = &mt32ram.timbres[i].timbre;
			char instrumentName[11];
			memcpy(instrumentName, timbre->common.name, 10);
			instrumentName[10] = 0;
			printDebug("WRITE-TIMBRE (%d-%d@%d..%d): %d; name=\"%s\"", first, last, off, off + len, i, instrumentName);
#if MT32EMU_MONITOR_TIMBRES >= 2
#define DT(x) printDebug(" " #x ": %d", timbre->x)
			DT(common.partialStructure12);
			DT(common.partialStructure34);
			DT(common.partialMute);
			DT(common.noSustain);

#define DTP(x) \
			DT(partial[x].wg.pitchCoarse); \
			DT(partial[x].wg.pitchFine); \
			DT(partial[x].wg.pitchKeyfollow); \
			DT(partial[x].wg.pitchBenderEnabled); \
			DT(partial[x].wg.waveform); \
			DT(partial[x].wg.pcmWave); \
			DT(partial[x].wg.pulseWidth); \
			DT(partial[x].wg.pulseWidthVeloSensitivity); \
			DT(partial[x].pitchEnv.depth); \
			DT(partial[x].pitchEnv.veloSensitivity); \
			DT(partial[x].pitchEnv.timeKeyfollow); \
			DT(partial[x].pitchEnv.time[0]); \
			DT(partial[x].pitchEnv.time[1]); \
			DT(partial[x].pitchEnv.time[2]); \
			DT(partial[x].pitchEnv.time[3]); \
			DT(partial[x].pitchEnv.level[0]); \
			DT(partial[x].pitchEnv.level[1]); \
			DT(partial[x].pitchEnv.level[2]); \
			DT(partial[x].pitchEnv.level[3]); \
			DT(partial[x].pitchEnv.level[4]); \
			DT(partial[x].pitchLFO.rate); \
			DT(partial[x].pitchLFO.depth); \
			DT(partial[x].pitchLFO.modSensitivity); \
			DT(partial[x].tvf.cutoff); \
			DT(partial[x].tvf.resonance); \
			DT(partial[x].tvf.keyfollow); \
			DT(partial[x].tvf.biasPoint); \
			DT(partial[x].tvf.biasLevel); \
			DT(partial[x].tvf.envDepth); \
			DT(partial[x].tvf.envVeloSensitivity); \
			DT(partial[x].tvf.envDepthKeyfollow); \
			DT(partial[x].tvf.envTimeKeyfollow); \
			DT(partial[x].tvf.envTime[0]); \
			DT(partial[x].tvf.envTime[1]); \
			DT(partial[x].tvf.envTime[2]); \
			DT(partial[x].tvf.envTime[3]); \
			DT(partial[x].tvf.envTime[4]); \
			DT(partial[x].tvf.envLevel[0]); \
			DT(partial[x].tvf.envLevel[1]); \
			DT(partial[x].tvf.envLevel[2]); \
			DT(partial[x].tvf.envLevel[3]); \
			DT(partial[x].tva.level); \
			DT(partial[x].tva.veloSensitivity); \
			DT(partial[x].tva.biasPoint1); \
			DT(partial[x].tva.biasLevel1); \
			DT(partial[x].tva.biasPoint2); \
			DT(partial[x].tva.biasLevel2); \
			DT(partial[x].tva.envTimeKeyfollow); \
			DT(partial[x].tva.envTimeVeloSensitivity); \
			DT(partial[x].tva.envTime[0]); \
			DT(partial[x].tva.envTime[1]); \
			DT(partial[x].tva.envTime[2]); \
			DT(partial[x].tva.envTime[3]); \
			DT(partial[x].tva.envTime[4]); \
			DT(partial[x].tva.envLevel[0]); \
			DT(partial[x].tva.envLevel[1]); \
			DT(partial[x].tva.envLevel[2]); \
			DT(partial[x].tva.envLevel[3]);

			DTP(0);
			DTP(1);
			DTP(2);
			DTP(3);
#undef DTP
#undef DT
#endif
#endif
			// FIXME:KG: Not sure if the stuff below should be done (for rhythm and/or parts)...
			// Does the real MT-32 automatically do this?
			for (unsigned int part = 0; part < 9; part++) {
				if (parts[part] != NULL) {
					parts[part]->refreshTimbre(i);
				}
			}
		}
		break;
	case MR_System:
		region->write(0, off, data, len);

		reportHandler->onDeviceReconfig();
		// FIXME: We haven't properly confirmed any of this behaviour
		// In particular, we tend to reset things such as reverb even if the write contained
		// the same parameters as were already set, which may be wrong.
		// On the other hand, the real thing could be resetting things even when they aren't touched
		// by the write at all.
#if MT32EMU_MONITOR_SYSEX > 0
		printDebug("WRITE-SYSTEM:");
#endif
		if (off <= SYSTEM_MASTER_TUNE_OFF && off + len > SYSTEM_MASTER_TUNE_OFF) {
			refreshSystemMasterTune();
		}
		if (off <= SYSTEM_REVERB_LEVEL_OFF && off + len > SYSTEM_REVERB_MODE_OFF) {
			refreshSystemReverbParameters();
		}
		if (off <= SYSTEM_RESERVE_SETTINGS_END_OFF && off + len > SYSTEM_RESERVE_SETTINGS_START_OFF) {
			refreshSystemReserveSettings();
		}
		if (off <= SYSTEM_CHAN_ASSIGN_END_OFF && off + len > SYSTEM_CHAN_ASSIGN_START_OFF) {
			int firstPart = off - SYSTEM_CHAN_ASSIGN_START_OFF;
			if(firstPart < 0)
				firstPart = 0;
			int lastPart = off + len - SYSTEM_CHAN_ASSIGN_START_OFF;
			if(lastPart > 8)
				lastPart = 8;
			refreshSystemChanAssign(Bit8u(firstPart), Bit8u(lastPart));
		}
		if (off <= SYSTEM_MASTER_VOL_OFF && off + len > SYSTEM_MASTER_VOL_OFF) {
			refreshSystemMasterVol();
		}
		break;
	case MR_Display:
		char buf[SYSEX_BUFFER_SIZE];
		memcpy(&buf, &data[0], len);
		buf[len] = 0;
#if MT32EMU_MONITOR_SYSEX > 0
		printDebug("WRITE-LCD: %s", buf);
#endif
		reportHandler->showLCDMessage(buf);
		break;
	case MR_Reset:
		reset();
		break;
	}
}

void Synth::refreshSystemMasterTune() {
#if MT32EMU_MONITOR_SYSEX > 0
	//FIXME:KG: This is just an educated guess.
	// The LAPC-I documentation claims a range of 427.5Hz-452.6Hz (similar to what we have here)
	// The MT-32 documentation claims a range of 432.1Hz-457.6Hz
	float masterTune = 440.0f * EXP2F((mt32ram.system.masterTune - 64.0f) / (128.0f * 12.0f));
	printDebug(" Master Tune: %f", masterTune);
#endif
}

void Synth::refreshSystemReverbParameters() {
#if MT32EMU_MONITOR_SYSEX > 0
	printDebug(" Reverb: mode=%d, time=%d, level=%d", mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel);
#endif
	if (reverbOverridden) {
#if MT32EMU_MONITOR_SYSEX > 0
		printDebug(" (Reverb overridden - ignoring)");
#endif
		return;
	}
	reportHandler->onNewReverbMode(mt32ram.system.reverbMode);
	reportHandler->onNewReverbTime(mt32ram.system.reverbTime);
	reportHandler->onNewReverbLevel(mt32ram.system.reverbLevel);

	BReverbModel *oldReverbModel = reverbModel;
	if (mt32ram.system.reverbTime == 0 && mt32ram.system.reverbLevel == 0) {
		// Setting both time and level to 0 effectively disables wet reverb output on real devices.
		// Take a shortcut in this case to reduce CPU load.
		reverbModel = NULL;
	} else {
		reverbModel = reverbModels[mt32ram.system.reverbMode];
	}
	if (reverbModel != oldReverbModel) {
#if MT32EMU_REDUCE_REVERB_MEMORY
		if (oldReverbModel != NULL) {
			oldReverbModel->close();
		}
		if (isReverbEnabled()) {
			reverbModel->open();
		}
#else
		if (isReverbEnabled()) {
			reverbModel->mute();
		}
#endif
	}
	if (isReverbEnabled()) {
		reverbModel->setParameters(mt32ram.system.reverbTime, mt32ram.system.reverbLevel);
	}
}

void Synth::refreshSystemReserveSettings() {
	Bit8u *rset = mt32ram.system.reserveSettings;
#if MT32EMU_MONITOR_SYSEX > 0
	printDebug(" Partial reserve: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
#endif
	partialManager->setReserve(rset);
}

void Synth::refreshSystemChanAssign(Bit8u firstPart, Bit8u lastPart) {
	memset(chantable, 0xFF, sizeof(chantable));

	// CONFIRMED: In the case of assigning a channel to multiple parts, the lower part wins.
	for (Bit32u i = 0; i <= 8; i++) {
		if (parts[i] != NULL && i >= firstPart && i <= lastPart) {
			// CONFIRMED: Decay is started for all polys, and all controllers are reset, for every part whose assignment was touched by the sysex write.
			parts[i]->allSoundOff();
			parts[i]->resetAllControllers();
		}
		Bit8u chan = mt32ram.system.chanAssign[i];
		if (chan < 16 && chantable[chan] > 8) {
			chantable[chan] = Bit8u(i);
		}
	}

#if MT32EMU_MONITOR_SYSEX > 0
	Bit8u *rset = mt32ram.system.chanAssign;
	printDebug(" Part assign:     1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
#endif
}

void Synth::refreshSystemMasterVol() {
#if MT32EMU_MONITOR_SYSEX > 0
	printDebug(" Master volume: %d", mt32ram.system.masterVol);
#endif
}

void Synth::refreshSystem() {
	refreshSystemMasterTune();
	refreshSystemReverbParameters();
	refreshSystemReserveSettings();
	refreshSystemChanAssign(0, 8);
	refreshSystemMasterVol();
}

void Synth::reset() {
	if (!opened) return;
#if MT32EMU_MONITOR_SYSEX > 0
	printDebug("RESET");
#endif
	reportHandler->onDeviceReset();
	partialManager->deactivateAll();
	mt32ram = mt32default;
	for (int i = 0; i < 9; i++) {
		parts[i]->reset();
		if (i != 8) {
			parts[i]->setProgram(controlROMData[controlROMMap->programSettings + i]);
		} else {
			parts[8]->refresh();
		}
	}
	refreshSystem();
	isActive();
}

MidiEvent::~MidiEvent() {
	if (sysexData != NULL) {
		delete[] sysexData;
	}
}

void MidiEvent::setShortMessage(Bit32u useShortMessageData, Bit32u useTimestamp) {
	if (sysexData != NULL) {
		delete[] sysexData;
	}
	shortMessageData = useShortMessageData;
	timestamp = useTimestamp;
	sysexData = NULL;
	sysexLength = 0;
}

void MidiEvent::setSysex(const Bit8u *useSysexData, Bit32u useSysexLength, Bit32u useTimestamp) {
	if (sysexData != NULL) {
		delete[] sysexData;
	}
	shortMessageData = 0;
	timestamp = useTimestamp;
	sysexLength = useSysexLength;
	Bit8u *dstSysexData = new Bit8u[sysexLength];
	sysexData = dstSysexData;
	memcpy(dstSysexData, useSysexData, sysexLength);
}

MidiEventQueue::MidiEventQueue(Bit32u useRingBufferSize) : ringBuffer(new MidiEvent[useRingBufferSize]), ringBufferMask(useRingBufferSize - 1) {
	memset(ringBuffer, 0, useRingBufferSize * sizeof(MidiEvent));
	reset();
}

MidiEventQueue::~MidiEventQueue() {
	delete[] ringBuffer;
}

void MidiEventQueue::reset() {
	startPosition = 0;
	endPosition = 0;
}

bool MidiEventQueue::pushShortMessage(Bit32u shortMessageData, Bit32u timestamp) {
	Bit32u newEndPosition = (endPosition + 1) & ringBufferMask;
	// Is ring buffer full?
	if (startPosition == newEndPosition) return false;
	ringBuffer[endPosition].setShortMessage(shortMessageData, timestamp);
	endPosition = newEndPosition;
	return true;
}

bool MidiEventQueue::pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp) {
	Bit32u newEndPosition = (endPosition + 1) & ringBufferMask;
	// Is ring buffer full?
	if (startPosition == newEndPosition) return false;
	ringBuffer[endPosition].setSysex(sysexData, sysexLength, timestamp);
	endPosition = newEndPosition;
	return true;
}

const MidiEvent *MidiEventQueue::peekMidiEvent() {
	return isEmpty() ? NULL : &ringBuffer[startPosition];
}

void MidiEventQueue::dropMidiEvent() {
	// Is ring buffer empty?
	if (startPosition != endPosition) {
		startPosition = (startPosition + 1) & ringBufferMask;
	}
}

bool MidiEventQueue::isFull() const {
	return startPosition == ((endPosition + 1) & ringBufferMask);
}

bool MidiEventQueue::isEmpty() const {
	return startPosition == endPosition;
}

Bit32u Synth::getStereoOutputSampleRate() const {
	return (analog == NULL) ? SAMPLE_RATE : analog->getOutputSampleRate();
}

void Renderer::render(SampleFormatConverter &converter, Bit32u len) {
	if (!synth.opened) {
		converter.addSilence(len << 1);
		return;
	}

	if (!synth.activated) {
		synth.renderedSampleCount += synth.analog->getDACStreamsLength(len);
		synth.analog->process(NULL, NULL, NULL, NULL, NULL, NULL, NULL, len);
		converter.addSilence(len << 1);
		return;
	}

	// As in AnalogOutputMode_ACCURATE mode output is upsampled, buffer size MAX_SAMPLES_PER_RUN is more than enough.
	Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
	Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
	Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN], tmpReverbWetRight[MAX_SAMPLES_PER_RUN];

	while (len > 0) {
		Bit32u thisPassLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
		synth.renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, synth.analog->getDACStreamsLength(thisPassLen));
		synth.analog->process(converter.sampleBuffer, tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisPassLen);
		converter.convert(thisPassLen << 1);
		len -= thisPassLen;
	}
}

void Synth::render(Bit16s *stream, Bit32u len) {
#if MT32EMU_USE_FLOAT_SAMPLES
	BufferedSampleFormatConverter<2> converter(stream);
#else
	SampleFormatConverter converter(stream);
#endif
	renderer.render(converter, len);
}

void Synth::render(float *stream, Bit32u len) {
#if MT32EMU_USE_FLOAT_SAMPLES
	SampleFormatConverter converter(stream);
#else
	BufferedSampleFormatConverter<2> converter(stream);
#endif
	renderer.render(converter, len);
}

void Renderer::renderStreams(
	SampleFormatConverter &nonReverbLeft, SampleFormatConverter &nonReverbRight,
	SampleFormatConverter &reverbDryLeft, SampleFormatConverter &reverbDryRight,
	SampleFormatConverter &reverbWetLeft, SampleFormatConverter &reverbWetRight,
	Bit32u len)
{
	if (!synth.opened) {
		nonReverbLeft.addSilence(len);
		nonReverbRight.addSilence(len);
		reverbDryLeft.addSilence(len);
		reverbDryRight.addSilence(len);
		reverbWetLeft.addSilence(len);
		reverbWetRight.addSilence(len);
		return;
	}

	while (len > 0) {
		// We need to ensure zero-duration notes will play so add minimum 1-sample delay.
		Bit32u thisLen = 1;
		if (!synth.isAbortingPoly()) {
			const MidiEvent *nextEvent = synth.midiQueue->peekMidiEvent();
			Bit32s samplesToNextEvent = (nextEvent != NULL) ? Bit32s(nextEvent->timestamp - synth.renderedSampleCount) : MAX_SAMPLES_PER_RUN;
			if (samplesToNextEvent > 0) {
				thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
				if (thisLen > Bit32u(samplesToNextEvent)) {
					thisLen = samplesToNextEvent;
				}
			} else {
				if (nextEvent->sysexData == NULL) {
					synth.playMsgNow(nextEvent->shortMessageData);
					// If a poly is aborting we don't drop the event from the queue.
					// Instead, we'll return to it again when the abortion is done.
					if (!synth.isAbortingPoly()) {
						synth.midiQueue->dropMidiEvent();
					}
				} else {
					synth.playSysexNow(nextEvent->sysexData, nextEvent->sysexLength);
					synth.midiQueue->dropMidiEvent();
				}
			}
		}
		doRenderStreams(
			nonReverbLeft.sampleBuffer, nonReverbRight.sampleBuffer,
			reverbDryLeft.sampleBuffer, reverbDryRight.sampleBuffer,
			reverbWetLeft.sampleBuffer, reverbWetRight.sampleBuffer,
			thisLen);
		nonReverbLeft.convert(thisLen);
		nonReverbRight.convert(thisLen);
		reverbDryLeft.convert(thisLen);
		reverbDryRight.convert(thisLen);
		reverbWetLeft.convert(thisLen);
		reverbWetRight.convert(thisLen);
		len -= thisLen;
	}
}

void Synth::renderStreams(
	Bit16s *nonReverbLeft, Bit16s *nonReverbRight,
	Bit16s *reverbDryLeft, Bit16s *reverbDryRight,
	Bit16s *reverbWetLeft, Bit16s *reverbWetRight,
	Bit32u len)
{
#if MT32EMU_USE_FLOAT_SAMPLES
	BufferedSampleFormatConverter<> convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight);
	BufferedSampleFormatConverter<> convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight);
	BufferedSampleFormatConverter<> convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight);
#else
	SampleFormatConverter convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight);
	SampleFormatConverter convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight);
	SampleFormatConverter convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight);
#endif
	renderer.renderStreams(
		convNonReverbLeft, convNonReverbRight,
		convReverbDryLeft, convReverbDryRight,
		convReverbWetLeft, convReverbWetRight,
		len);
}

void Synth::renderStreams(
	float *nonReverbLeft, float *nonReverbRight,
	float *reverbDryLeft, float *reverbDryRight,
	float *reverbWetLeft, float *reverbWetRight,
	Bit32u len)
{
#if MT32EMU_USE_FLOAT_SAMPLES
	SampleFormatConverter convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight);
	SampleFormatConverter convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight);
	SampleFormatConverter convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight);
#else
	BufferedSampleFormatConverter<> convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight);
	BufferedSampleFormatConverter<> convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight);
	BufferedSampleFormatConverter<> convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight);
#endif
	renderer.renderStreams(
		convNonReverbLeft, convNonReverbRight,
		convReverbDryLeft, convReverbDryRight,
		convReverbWetLeft, convReverbWetRight,
		len);
}

// In GENERATION2 units, the output from LA32 goes to the Boss chip already bit-shifted.
// In NICE mode, it's also better to increase volume before the reverb processing to preserve accuracy.
void Renderer::produceLA32Output(Sample *buffer, Bit32u len) {
#if MT32EMU_USE_FLOAT_SAMPLES
	(void)buffer;
	(void)len;
#else
	switch (synth.dacInputMode) {
		case DACInputMode_GENERATION2:
			while (len--) {
				*buffer = (*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE) | ((*buffer >> 14) & 0x0001);
				++buffer;
			}
			break;
		case DACInputMode_NICE:
			while (len--) {
				*buffer = Synth::clipSampleEx(SampleEx(*buffer) << 1);
				++buffer;
			}
			break;
		default:
			break;
	}
#endif
}

void Renderer::convertSamplesToOutput(Sample *buffer, Bit32u len) {
#if MT32EMU_USE_FLOAT_SAMPLES
	(void)buffer;
	(void)len;
#else
	if (synth.dacInputMode == DACInputMode_GENERATION1) {
		while (len--) {
			*buffer = Sample((*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE));
			++buffer;
		}
	}
#endif
}

void Renderer::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) {
	// Even if LA32 output isn't desired, we proceed anyway with temp buffers
	Sample tmpBufNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpBufNonReverbRight[MAX_SAMPLES_PER_RUN];
	if (nonReverbLeft == NULL) nonReverbLeft = tmpBufNonReverbLeft;
	if (nonReverbRight == NULL) nonReverbRight = tmpBufNonReverbRight;

	Sample tmpBufReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpBufReverbDryRight[MAX_SAMPLES_PER_RUN];
	if (reverbDryLeft == NULL) reverbDryLeft = tmpBufReverbDryLeft;
	if (reverbDryRight == NULL) reverbDryRight = tmpBufReverbDryRight;

	if (synth.activated) {
		Synth::muteSampleBuffer(nonReverbLeft, len);
		Synth::muteSampleBuffer(nonReverbRight, len);
		Synth::muteSampleBuffer(reverbDryLeft, len);
		Synth::muteSampleBuffer(reverbDryRight, len);

		for (unsigned int i = 0; i < synth.getPartialCount(); i++) {
			if (synth.partialManager->shouldReverb(i)) {
				synth.partialManager->produceOutput(i, reverbDryLeft, reverbDryRight, len);
			} else {
				synth.partialManager->produceOutput(i, nonReverbLeft, nonReverbRight, len);
			}
		}

		produceLA32Output(reverbDryLeft, len);
		produceLA32Output(reverbDryRight, len);

		if (synth.isReverbEnabled()) {
			synth.reverbModel->process(reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, len);
			if (reverbWetLeft != NULL) convertSamplesToOutput(reverbWetLeft, len);
			if (reverbWetRight != NULL) convertSamplesToOutput(reverbWetRight, len);
		} else {
			Synth::muteSampleBuffer(reverbWetLeft, len);
			Synth::muteSampleBuffer(reverbWetRight, len);
		}

		// Don't bother with conversion if the output is going to be unused
		if (nonReverbLeft != tmpBufNonReverbLeft) {
			produceLA32Output(nonReverbLeft, len);
			convertSamplesToOutput(nonReverbLeft, len);
		}
		if (nonReverbRight != tmpBufNonReverbRight) {
			produceLA32Output(nonReverbRight, len);
			convertSamplesToOutput(nonReverbRight, len);
		}
		if (reverbDryLeft != tmpBufReverbDryLeft) convertSamplesToOutput(reverbDryLeft, len);
		if (reverbDryRight != tmpBufReverbDryRight) convertSamplesToOutput(reverbDryRight, len);
	} else {
		// Avoid muting buffers that wasn't requested
		if (nonReverbLeft != tmpBufNonReverbLeft) Synth::muteSampleBuffer(nonReverbLeft, len);
		if (nonReverbRight != tmpBufNonReverbRight) Synth::muteSampleBuffer(nonReverbRight, len);
		if (reverbDryLeft != tmpBufReverbDryLeft) Synth::muteSampleBuffer(reverbDryLeft, len);
		if (reverbDryRight != tmpBufReverbDryRight) Synth::muteSampleBuffer(reverbDryRight, len);
		Synth::muteSampleBuffer(reverbWetLeft, len);
		Synth::muteSampleBuffer(reverbWetRight, len);
	}

	synth.partialManager->clearAlreadyOutputed();
	synth.renderedSampleCount += len;
}

void Synth::printPartialUsage(Bit32u sampleOffset) {
	unsigned int partialUsage[9];
	partialManager->getPerPartPartialUsage(partialUsage);
	if (sampleOffset > 0) {
		printDebug("[+%u] 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], getPartialCount() - partialManager->getFreePartialCount());
	}
}

bool Synth::hasActivePartials() const {
	if (!opened) {
		return false;
	}
	for (unsigned int partialNum = 0; partialNum < getPartialCount(); partialNum++) {
		if (partialManager->getPartial(partialNum)->isActive()) {
			return true;
		}
	}
	return false;
}

bool Synth::isActive() {
	if (!opened) {
		return false;
	}
	if (!midiQueue->isEmpty() || hasActivePartials()) {
		return true;
	}
	if (isReverbEnabled() && reverbModel->isActive()) {
		return true;
	}
	activated = false;
	return false;
}

Bit32u Synth::getPartialCount() const {
	return partialCount;
}

void Synth::getPartStates(bool *partStates) const {
	if (!opened) {
		memset(partStates, 0, 9 * sizeof(bool));
		return;
	}
	for (int partNumber = 0; partNumber < 9; partNumber++) {
		const Part *part = parts[partNumber];
		partStates[partNumber] = part->getActiveNonReleasingPartialCount() > 0;
	}
}

Bit32u Synth::getPartStates() const {
	if (!opened) return 0;
	bool partStates[9];
	getPartStates(partStates);
	Bit32u bitSet = 0;
	for (int partNumber = 8; partNumber >= 0; partNumber--) {
		bitSet = (bitSet << 1) | (partStates[partNumber] ? 1 : 0);
	}
	return bitSet;
}

void Synth::getPartialStates(PartialState *partialStates) const {
	if (!opened) {
		memset(partialStates, 0, partialCount * sizeof(PartialState));
		return;
	}
	for (unsigned int partialNum = 0; partialNum < partialCount; partialNum++) {
		partialStates[partialNum] = getPartialState(partialManager, partialNum);
	}
}

void Synth::getPartialStates(Bit8u *partialStates) const {
	if (!opened) {
		memset(partialStates, 0, ((partialCount + 3) >> 2));
		return;
	}
	for (unsigned int quartNum = 0; (4 * quartNum) < partialCount; quartNum++) {
		Bit8u packedStates = 0;
		for (unsigned int i = 0; i < 4; i++) {
			unsigned int partialNum = (4 * quartNum) + i;
			if (partialCount <= partialNum) break;
			PartialState partialState = getPartialState(partialManager, partialNum);
			packedStates |= (partialState & 3) << (2 * i);
		}
		partialStates[quartNum] = packedStates;
	}
}

Bit32u Synth::getPlayingNotes(Bit8u partNumber, Bit8u *keys, Bit8u *velocities) const {
	Bit32u playingNotes = 0;
	if (opened && (partNumber < 9)) {
		const Part *part = parts[partNumber];
		const Poly *poly = part->getFirstActivePoly();
		while (poly != NULL) {
			keys[playingNotes] = Bit8u(poly->getKey());
			velocities[playingNotes] = Bit8u(poly->getVelocity());
			playingNotes++;
			poly = poly->getNext();
		}
	}
	return playingNotes;
}

const char *Synth::getPatchName(Bit8u partNumber) const {
	return (!opened || partNumber > 8) ? NULL : parts[partNumber]->getCurrentInstr();
}

const Part *Synth::getPart(Bit8u partNum) const {
	if (partNum > 8) {
		return NULL;
	}
	return parts[partNum];
}

void MemoryRegion::read(unsigned int entry, unsigned int off, Bit8u *dst, unsigned int len) const {
	off += entry * entrySize;
	// This method should never be called with out-of-bounds parameters,
	// or on an unsupported region - seeing any of this debug output indicates a bug in the emulator
	if (off > entrySize * entries - 1) {
#if MT32EMU_MONITOR_SYSEX > 0
		synth->printDebug("read[%d]: parameters start out of bounds: entry=%d, off=%d, len=%d", type, entry, off, len);
#endif
		return;
	}
	if (off + len > entrySize * entries) {
#if MT32EMU_MONITOR_SYSEX > 0
		synth->printDebug("read[%d]: parameters end out of bounds: entry=%d, off=%d, len=%d", type, entry, off, len);
#endif
		len = entrySize * entries - off;
	}
	Bit8u *src = getRealMemory();
	if (src == NULL) {
#if MT32EMU_MONITOR_SYSEX > 0
		synth->printDebug("read[%d]: unreadable region: entry=%d, off=%d, len=%d", type, entry, off, len);
#endif
		return;
	}
	memcpy(dst, src + off, len);
}

void MemoryRegion::write(unsigned int entry, unsigned int off, const Bit8u *src, unsigned int len, bool init) const {
	unsigned int memOff = entry * entrySize + off;
	// This method should never be called with out-of-bounds parameters,
	// or on an unsupported region - seeing any of this debug output indicates a bug in the emulator
	if (off > entrySize * entries - 1) {
#if MT32EMU_MONITOR_SYSEX > 0
		synth->printDebug("write[%d]: parameters start out of bounds: entry=%d, off=%d, len=%d", type, entry, off, len);
#endif
		return;
	}
	if (off + len > entrySize * entries) {
#if MT32EMU_MONITOR_SYSEX > 0
		synth->printDebug("write[%d]: parameters end out of bounds: entry=%d, off=%d, len=%d", type, entry, off, len);
#endif
		len = entrySize * entries - off;
	}
	Bit8u *dest = getRealMemory();
	if (dest == NULL) {
#if MT32EMU_MONITOR_SYSEX > 0
		synth->printDebug("write[%d]: unwritable region: entry=%d, off=%d, len=%d", type, entry, off, len);
#endif
		return;
	}

	for (unsigned int i = 0; i < len; i++) {
		Bit8u desiredValue = src[i];
		Bit8u maxValue = getMaxValue(memOff);
		// maxValue == 0 means write-protected unless called from initialisation code, in which case it really means the maximum value is 0.
		if (maxValue != 0 || init) {
			if (desiredValue > maxValue) {
#if MT32EMU_MONITOR_SYSEX > 0
				synth->printDebug("write[%d]: Wanted 0x%02x at %d, but max 0x%02x", type, desiredValue, memOff, maxValue);
#endif
				desiredValue = maxValue;
			}
			dest[memOff] = desiredValue;
		} else if (desiredValue != 0) {
#if MT32EMU_MONITOR_SYSEX > 0
			// Only output debug info if they wanted to write non-zero, since a lot of things cause this to spit out a lot of debug info otherwise.
			synth->printDebug("write[%d]: Wanted 0x%02x at %d, but write-protected", type, desiredValue, memOff);
#endif
		}
		memOff++;
	}
}

} // namespace MT32Emu
