[Scummvm-cvs-logs] CVS: scummvm/sound/softsynth/mt32 i386.cpp,1.1,1.2 i386.h,1.1,1.2 mt32emu.h,1.1,1.2 part.cpp,1.1,1.2 part.h,1.1,1.2 partial.cpp,1.1,1.2 partial.h,1.1,1.2 partialManager.cpp,1.1,1.2 partialManager.h,1.1,1.2 structures.h,1.1,1.2 synth.cpp,1.1,1.2 synth.h,1.1,1.2 tables.cpp,1.1,1.2 tables.h,1.1,1.2

Jerome Fisher kingguppy at users.sourceforge.net
Sun Mar 20 08:25:32 CET 2005


Update of /cvsroot/scummvm/scummvm/sound/softsynth/mt32
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv5689

Modified Files:
	i386.cpp i386.h mt32emu.h part.cpp part.h partial.cpp 
	partial.h partialManager.cpp partialManager.h structures.h 
	synth.cpp synth.h tables.cpp tables.h 
Log Message:
Merged with latest Munt CVS.
* Added support for most of the extended capabilities of the CM-32L/LAPC-I (more rhythm timbres, more rhythm key mappings, more PCM samples).
* The control ROM is now identified by searching for matching ID strings at various locations in the file. There are now a lot of safety checks to help ensure that a bad ROM will not crash the emulator.
* Three control ROM versions are now identified and mapped out - an original MT-32 control ROM, an original CM-32L ROM, and the Blue Ridge modified MT-32 ROM.
* Now supports the expression controller properly.
* Sustain is now turned off correctly again.
* "All Notes Off" no longer stops notes held by the sustain controller.
* Implemented "Reset all controllers".
* Stereo pan positions are no longer inverted.
* Volume, pitch, filter and envelopes are now more accurately calculated. Overall, the sound emulation is much more accurate.
* Waveforms are now slightly more accurate (in terms of pitch), necessitating a regeneration.
* Handling of unusual sysex messages has been improved (fixes sysex messages from Java's MIDI classes on Windows).
* Fixed a reverb bug during periods of silence.


Index: i386.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/softsynth/mt32/i386.cpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- i386.cpp	25 Dec 2004 18:34:44 -0000	1.1
+++ i386.cpp	20 Mar 2005 16:24:55 -0000	1.2
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2004 Various contributors
+/* Copyright (c) 2003-2005 Various contributors
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -465,7 +465,7 @@
 #endif
 }
 
-float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr, int revLevel) {
+float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr) {
 	float output;
 
 	// 1st number of coefficients array is overall input scale factor, or filter gain
@@ -542,11 +542,10 @@
 #else
 	output = atti386_iir_filter_sse(&output, hist1_ptr, coef_ptr);
 #endif
-	output *= ResonInv[revLevel];
 	return output;
 }
 
-float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel) {
+float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr) {
 	float output;
 
 	// 1st number of coefficients array is overall input scale factor, or filter gain
@@ -624,7 +623,6 @@
 #else
 	output = atti386_iir_filter_3DNow(output, hist1_ptr, coef_ptr);
 #endif
-	output *= ResonInv[revLevel];
 	return output;
 }
 

Index: i386.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/softsynth/mt32/i386.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- i386.h	25 Dec 2004 18:34:44 -0000	1.1
+++ i386.h	20 Mar 2005 16:24:55 -0000	1.2
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2004 Various contributors
+/* Copyright (c) 2003-2005 Various contributors
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -30,9 +30,9 @@
 // Function that detects the availablity of 3DNow instructions
 bool Detect3DNow();
 
-float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr, int revLevel);
-float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel);
-float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLevel);
+float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr);
+float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr);
+float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr);
 
 #if MT32EMU_USE_MMX > 0
 int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf);

Index: mt32emu.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/softsynth/mt32/mt32emu.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- mt32emu.h	25 Dec 2004 18:34:44 -0000	1.1
+++ mt32emu.h	20 Mar 2005 16:24:55 -0000	1.2
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2004 Various contributors
+/* Copyright (c) 2003-2005 Various contributors
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to

Index: part.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/softsynth/mt32/part.cpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- part.cpp	25 Dec 2004 18:34:44 -0000	1.1
+++ part.cpp	20 Mar 2005 16:24:55 -0000	1.2
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2004 Various contributors
+/* Copyright (c) 2003-2005 Various contributors
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -66,18 +66,18 @@
 	this->partNum = usePartNum;
 	patchCache[0].dirty = true;
 	holdpedal = false;
+	patchTemp = &synth->mt32ram.patchSettings[partNum];
 	if (usePartNum == 8) {
 		// Nasty hack for rhythm
-		patchTemp = NULL;
 		timbreTemp = NULL;
 	} else {
 		sprintf(name, "Part %d", partNum + 1);
-		patchTemp = &synth->mt32ram.patchSettings[partNum];
 		timbreTemp = &synth->mt32ram.timbreSettings[partNum];
 	}
 	currentInstr[0] = 0;
 	currentInstr[10] = 0;
-	volume = voltable[102]; //FIXME:KG: Original was just volume=102; I assume this is intended
+	expression = 127;
+	volumeMult = 0;
 	volumesetting.leftvol = 32767;
 	volumesetting.rightvol = 32767;
 	bend = 0.0f;
@@ -86,9 +86,12 @@
 }
 
 void Part::setHoldPedal(bool pedalval) {
-	if (holdpedal && !pedalval)
+	if (holdpedal && !pedalval) {
+		holdpedal = false;
 		stopPedalHold();
-	holdpedal = pedalval;
+	} else {
+		holdpedal = pedalval;
+	}
 }
 
 void RhythmPart::setBend(unsigned int midiBend) {
@@ -130,20 +133,20 @@
 }
 
 void RhythmPart::refresh() {
+	updateVolume();
 	// (Re-)cache all the mapped timbres ahead of time
-	for (unsigned int drumNum = 0; drumNum < 64; drumNum++) {
+	for (unsigned int drumNum = 0; drumNum < synth->controlROMMap->rhythmSettingsCount; drumNum++) {
 		int drumTimbreNum = rhythmTemp[drumNum].timbre;
-		if (drumTimbreNum >= 94)
+		if (drumTimbreNum >= 127) // 94 on MT-32
 			continue;
 		Bit16s pan = rhythmTemp[drumNum].panpot; // They use R-L 0-14...
 		// FIXME:KG: Panning cache should be backed up to partials using it, too
-		// FIXME:KG: If I don't have left/right mixed up here, it's pure luck
 		if (pan < 7) {
-			drumPan[drumNum].leftvol = 32767;
-			drumPan[drumNum].rightvol = pan * 4681;
-		} else {
+			drumPan[drumNum].leftvol = pan * 4681;
 			drumPan[drumNum].rightvol = 32767;
-			drumPan[drumNum].leftvol = (14 - pan) * 4681;
+		} else {
+			drumPan[drumNum].rightvol = (14 - pan) * 4681;
+			drumPan[drumNum].leftvol = 32767;
 		}
 		PatchCache *cache = drumCache[drumNum];
 		backupCacheToPartials(cache);
@@ -159,6 +162,7 @@
 }
 
 void Part::refresh() {
+	updateVolume();
 	backupCacheToPartials(patchCache);
 	for (int t = 0; t < 4; t++) {
 		// Common parameters, stored redundantly
@@ -171,8 +175,12 @@
 	memcpy(currentInstr, timbreTemp->common.name, 10);
 }
 
+const char *Part::getCurrentInstr() const {
+	return &currentInstr[0];
+}
+
 void RhythmPart::refreshTimbre(unsigned int absTimbreNum) {
-	for (int m = 0; m < 64; m++) {
+	for (int m = 0; m < 85; m++) {
 		if (rhythmTemp[m].timbre == absTimbreNum - 128)
 			drumCache[m][0].dirty = true;
 	}
@@ -225,12 +233,16 @@
 	patchTemp->patch = *patch;
 }
 
+void RhythmPart::setTimbre(TimbreParam *timbre) {
+	synth->printDebug("%s: Attempted to call setTimbre() - doesn't make sense for rhythm", name);
+}
+
 void Part::setTimbre(TimbreParam *timbre) {
 	*timbreTemp = *timbre;
 }
 
 unsigned int RhythmPart::getAbsTimbreNum() const {
-	synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm");
+	synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm", name);
 	return 0;
 }
 
@@ -245,16 +257,10 @@
 void Part::setProgram(unsigned int patchNum) {
 	setPatch(&synth->mt32ram.patches[patchNum]);
 	setTimbre(&synth->mt32ram.timbres[getAbsTimbreNum()].timbre);
-#if 0
-	// Immediately stop all partials on this part (this is apparently *not* the correct behaviour)
-	for (int m = 0; m < MT32EMU_MAX_POLY; m++) {
-		AbortPoly(poly);
-	}
-#endif
 
 	refresh();
 
-	allStop(); //FIXME:KG: Is this correct?
+	allSoundOff(); //FIXME:KG: Is this correct?
 }
 
 void Part::backupCacheToPartials(PatchCache cache[4]) {
@@ -336,29 +342,16 @@
 
 		// Calculate and cache TVA envelope stuff
 		cache[t].ampEnv = timbre->partial[t].tva;
-		for (int i = 0; i < 4; i++)
-			cache[t].ampEnv.envlevel[i] = (char)((float)cache[t].ampEnv.envlevel[i] * 1.27f);
 		cache[t].ampEnv.level = (char)((float)cache[t].ampEnv.level * 1.27f);
-		float tvelo = ((float)timbre->partial[t].tva.velosens / 100.0f);
-		float velo = fabs(tvelo-0.5f) * 2.0f;
-		velo *= 63.0f;
-		cache[t].ampEnv.velosens = (char)velo;
-		if (tvelo<0.5f)
-			cache[t].ampenvdir = 1;
-		else
-			cache[t].ampenvdir = 0;
 
 		cache[t].ampbias[0] = fixBiaslevel(cache[t].ampEnv.biaspoint1, &cache[t].ampdir[0]);
 		cache[t].ampblevel[0] = 12 - cache[t].ampEnv.biaslevel1;
 		cache[t].ampbias[1] = fixBiaslevel(cache[t].ampEnv.biaspoint2, &cache[t].ampdir[1]);
 		cache[t].ampblevel[1] = 12 - cache[t].ampEnv.biaslevel2;
 		cache[t].ampdepth = cache[t].ampEnv.envvkf * cache[t].ampEnv.envvkf;
-		cache[t].ampsustain = cache[t].ampEnv.envlevel[3];
-		cache[t].amplevel = cache[t].ampEnv.level;
 
 		// Calculate and cache filter stuff
 		cache[t].filtEnv = timbre->partial[t].tvf;
-		cache[t].tvfdepth = cache[t].filtEnv.envdkf;
 		cache[t].filtkeyfollow  = fixKeyfollow(cache[t].filtEnv.keyfollow);
 		cache[t].filtEnv.envdepth = (char)((float)cache[t].filtEnv.envdepth * 1.27);
 		cache[t].tvfbias = fixBiaslevel(cache[t].filtEnv.biaspoint, &cache[t].tvfdir);
@@ -367,7 +360,7 @@
 
 		// Calculate and cache LFO stuff
 		cache[t].lfodepth = timbre->partial[t].lfo.depth;
-		cache[t].lfoperiod = lfotable[(int)timbre->partial[t].lfo.rate];
+		cache[t].lfoperiod = synth->tables.lfoPeriod[(int)timbre->partial[t].lfo.rate];
 		cache[t].lforate = timbre->partial[t].lfo.rate;
 		cache[t].modsense = timbre->partial[t].lfo.modsense;
 	}
@@ -391,8 +384,25 @@
 	return name;
 }
 
-void Part::setVolume(int vol) {
-	volume = voltable[vol];
+void Part::updateVolume() {
+	volumeMult = synth->tables.volumeMult[patchTemp->outlevel * expression / 127];
+}
+
+int Part::getVolume() const {
+	// FIXME: Use the mappings for this in the control ROM
+	return patchTemp->outlevel * 127 / 100;
+}
+
+void Part::setVolume(int midiVolume) {
+	// FIXME: Use the mappings for this in the control ROM
+	patchTemp->outlevel = (Bit8u)(midiVolume * 100 / 127);
+	updateVolume();
+	synth->printDebug("%s (%s): Set volume to %d", name, currentInstr, midiVolume);
+}
+
+void Part::setExpression(int midiExpression) {
+	expression = midiExpression;
+	updateVolume();
 }
 
 void RhythmPart::setPan(unsigned int midiPan)
@@ -404,27 +414,30 @@
 void Part::setPan(unsigned int midiPan) {
 	// FIXME:KG: Tweaked this a bit so that we have a left 100%, centre and right 100%
 	// (But this makes the range somewhat skewed)
+	// Check against the real thing
+	// NOTE: Panning is inverted compared to GM.
 	if (midiPan < 64) {
-		volumesetting.leftvol = 32767;
-		volumesetting.rightvol = (Bit16s)(midiPan * 512);
+		volumesetting.leftvol = (Bit16s)(midiPan * 512);
+		volumesetting.rightvol = 32767;
 	} else if (midiPan == 64) {
 		volumesetting.leftvol = 32767;
 		volumesetting.rightvol = 32767;
 	} else {
-		volumesetting.rightvol = 32767;
-		volumesetting.leftvol = (Bit16s)((127 - midiPan) * 520);
+		volumesetting.rightvol = (Bit16s)((127 - midiPan) * 520);
+		volumesetting.leftvol = 32767;
 	}
+	patchTemp->panpot = (Bit8u)(midiPan * 14 / 127);
 	//synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot);
 }
 
 void RhythmPart::playNote(unsigned int key, int vel) {
-	if (key < 24 || key > 87) {
+	if (key < 24 || key > 108)/*> 87 on MT-32)*/ {
 		synth->printDebug("%s: Attempted to play invalid key %d", name, key);
 		return;
 	}
 	int drumNum = key - 24;
 	int drumTimbreNum = rhythmTemp[drumNum].timbre;
-	if (drumTimbreNum >= 94) {
+	if (drumTimbreNum >= 127) { // 94 on MT-32
 		synth->printDebug("%s: Attempted to play unmapped key %d", name, key);
 		return;
 	}
@@ -432,7 +445,7 @@
 	TimbreParam *timbre = &synth->mt32ram.timbres[absTimbreNum].timbre;
 	memcpy(currentInstr, timbre->common.name, 10);
 #if MT32EMU_MONITOR_INSTRUMENTS == 1
-	synth->printDebug("%s (%s): starting poly (drum %d, timbre %d) - Vel %d Key %d Vol %d", name, currentInstr, drumNum, absTimbreNum, vel, key, volume);
+	synth->printDebug("%s (%s): starting poly (drum %d, timbre %d) - Vel %d Key %d", name, currentInstr, drumNum, absTimbreNum, vel, key);
 #endif
 	if (drumCache[drumNum][0].dirty) {
 		cacheTimbre(drumCache[drumNum], timbre);
@@ -462,7 +475,7 @@
 		}
 	}
 #if MT32EMU_MONITOR_INSTRUMENTS == 1
-	synth->printDebug("%s (%s): starting poly - Vel %d Key %d Vol %d", name, currentInstr, vel, key, volume);
+	synth->printDebug("%s (%s): starting poly - Vel %d Key %d", name, currentInstr, vel, key);
 #endif
 	if (patchCache[0].dirty) {
 		cacheTimbre(patchCache, timbreTemp);
@@ -515,7 +528,7 @@
 		synth->printDebug("%s (%s): No partials to play for this instrument", name, this->currentInstr);
 
 	tpoly->sustain = cache[0].sustain;
-	tpoly->volumeptr = &volume;
+	tpoly->volumeptr = &volumeMult;
 
 	for (int x = 0; x < 4; x++) {
 		if (tpoly->partials[x] != NULL) {
@@ -540,7 +553,23 @@
 	tpoly->isPlaying = false;
 }
 
-void Part::allStop() {
+void Part::allNotesOff() {
+	// Note: Unchecked on real MT-32, but the MIDI specification states that all notes off (0x7B)
+	// should treat the hold pedal as usual.
+	// All *sound* off (0x78) should stop notes immediately regardless of the hold pedal.
+	// The latter controller is not implemented on the MT-32 (according to the docs).
+	for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
+		dpoly *tpoly = &polyTable[q];
+		if (tpoly->isPlaying) {
+			if (holdpedal)
+				tpoly->pedalhold = true;
+			else if (tpoly->sustain)
+				startDecayPoly(tpoly);
+		}
+	}
+}
+
+void Part::allSoundOff() {
 	for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
 		dpoly *tpoly = &polyTable[q];
 		if (tpoly->isPlaying) {

Index: part.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/softsynth/mt32/part.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- part.h	25 Dec 2004 18:34:44 -0000	1.1
+++ part.h	20 Mar 2005 16:24:55 -0000	1.2
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2004 Various contributors
+/* Copyright (c) 2003-2005 Various contributors
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -57,7 +57,10 @@
 	Synth *synth;
 	char name[8]; // "Part 1".."Part 8", "Rhythm"
 	char currentInstr[11];
-	Bit32u volume;
+	int expression;
+	Bit32u volumeMult;
+
+	void updateVolume();
 	void backupCacheToPartials(PatchCache cache[4]);
 	void cacheTimbre(PatchCache cache[4], const TimbreParam *timbre);
 	void playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel);
@@ -67,18 +70,22 @@
 	Part(Synth *synth, unsigned int usePartNum);
 	virtual void playNote(unsigned int key, int vel);
 	void stopNote(unsigned int key);
-	void allStop();
-	void setVolume(int vol);
+	void allNotesOff();
+	void allSoundOff();
+	int getVolume() const;
+	void setVolume(int midiVolume);
+	void setExpression(int midiExpression);
 	virtual void setPan(unsigned int midiPan);
 	virtual void setBend(unsigned int midiBend);
 	virtual void setModulation(unsigned int midiModulation);
-	virtual void setProgram(unsigned int patchNum);
+	virtual void setProgram(unsigned int midiProgram);
 	void setHoldPedal(bool pedalval);
 	void stopPedalHold();
 	virtual void refresh();
 	virtual void refreshTimbre(unsigned int absTimbreNum);
-	void setTimbre(TimbreParam *timbre);
+	virtual void setTimbre(TimbreParam *timbre);
 	virtual unsigned int getAbsTimbreNum() const;
+	const char *getCurrentInstr() const;
 };
 
 class RhythmPart: public Part {
@@ -86,12 +93,13 @@
 	const MemParams::RhythmTemp *rhythmTemp;
 
 	// This caches the timbres/settings in use by the rhythm part
-	PatchCache drumCache[64][4];
-	StereoVolume drumPan[64];
+	PatchCache drumCache[85][4];
+	StereoVolume drumPan[85];
 public:
 	RhythmPart(Synth *synth, unsigned int usePartNum);
-	void refreshTimbre(unsigned int timbreNum);
 	void refresh();
+	void refreshTimbre(unsigned int timbreNum);
+	void setTimbre(TimbreParam *timbre);
 	void playNote(unsigned int key, int vel);
 	unsigned int getAbsTimbreNum() const;
 	void setPan(unsigned int midiPan);

Index: partial.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/softsynth/mt32/partial.cpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- partial.cpp	25 Dec 2004 18:34:44 -0000	1.1
+++ partial.cpp	20 Mar 2005 16:24:55 -0000	1.2
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2004 Various contributors
+/* Copyright (c) 2003-2005 Various contributors
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -50,10 +50,11 @@
 	for (int i = 0; i < 3; i++) {
 		delete[] noteLookupStorage.waveforms[i];
 	}
+	delete[] noteLookupStorage.wavTable;
 #endif
 }
 
-int Partial::getOwnerPart() {
+int Partial::getOwnerPart() const {
 	return ownerPart;
 }
 
@@ -61,6 +62,10 @@
 	return ownerPart > -1;
 }
 
+const dpoly *Partial::getDpoly() const {
+	return this->poly;
+}
+
 void Partial::activate(int part) {
 	// This just marks the partial as being assigned to a part
 	ownerPart = part;
@@ -119,10 +124,18 @@
 	int keyfollow = ((key - MIDDLEC) * patchCache->filtkeyfollow) / 4096;
 	if (keyfollow > 108)
 		keyfollow = 108;
-	if (keyfollow < -108)
+	else if (keyfollow < -108)
 		keyfollow = -108;
-	filtVal = keytable[keyfollow + 108];
-	realVal = keytable[(key - MIDDLEC) + 108];
+	filtVal = synth->tables.tvfKeyfollowMult[keyfollow + 108];
+	realVal = synth->tables.tvfKeyfollowMult[(noteVal - MIDDLEC) + 108];
+}
+
+int Partial::getKey() const {
+	if (poly == NULL) {
+		return -1;
+	} else {
+		return poly->key;
+	}
 }
 
 void Partial::startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial) {
@@ -138,13 +151,27 @@
 	play = true;
 	initKeyFollow(poly->freqnum); // Initialises noteVal, filtVal and realVal
 #if MT32EMU_ACCURATENOTES == 0
-	noteLookup = &noteLookups[noteVal - LOWEST_NOTE];
+	noteLookup = &synth->tables.noteLookups[noteVal - LOWEST_NOTE];
 #else
-	TableInitialiser::initNote(synth, &noteLookupStorage, noteVal, (float)synth->myProp.sampleRate, synth->masterTune, synth->PCMList, NULL);
+	Tables::initNote(synth, &noteLookupStorage, noteVal, (float)synth->myProp.sampleRate, synth->masterTune, synth->pcmWaves, NULL);
 #endif
+	keyLookup = &synth->tables.keyLookups[poly->freqnum - 12];
+
+	if (patchCache->PCMPartial) {
+		pcmNum = patchCache->pcm;
+		if (synth->controlROMMap->pcmCount > 128) {
+			// CM-32L, etc. support two "banks" of PCMs, selectable by waveform type parameter.
+			if (patchCache->waveform > 1) {
+				pcmNum += 128;
+			}
+		}
+		pcmWave = &synth->pcmWaves[pcmNum];
+	} else {
+		pcmWave = NULL;
+	}
 
 	lfoPos = 0;
-	pulsewidth = patchCache->pulsewidth + pwveltable[patchCache->pwsens][poly->vel];
+	pulsewidth = patchCache->pulsewidth + synth->tables.pwVelfollowAdd[patchCache->pwsens][poly->vel];
 	if (pulsewidth > 100) {
 		pulsewidth = 100;
 	} else if (pulsewidth < 0) {
@@ -189,6 +216,7 @@
 	// Generate samples
 
 	Bit16s *partialBuf = &myBuffer[0];
+	Bit32u volume = *poly->volumeptr;
 	while (length--) {
 		Bit32s envval;
 		Bit32s sample = 0;
@@ -199,24 +227,19 @@
 					deactivate();
 					break;
 				}
-				if (ampval > 127) {
-					ampval = 127;
+				if (ampval > 100) {
+					ampval = 100;
 				}
 
-				ampval = voltable[ampval];
-				int tmpvel;
-				if (patchCache->ampenvdir == 1)
-					tmpvel = 127 - poly->vel;
-				else
-					tmpvel = poly->vel;
-				ampval = (ampval * ampveltable[tmpvel][(int)patchCache->ampEnv.velosens]) >> 8;
+				ampval = synth->tables.volumeMult[ampval];
+				ampval = FIXEDPOINT_UMULT(ampval, synth->tables.tvaVelfollowMult[poly->vel][(int)patchCache->ampEnv.velosens], 8);
 				//if (envs[EnvelopeType_amp].sustaining)
 				ampEnvVal = ampval;
 			}
 			--envs[EnvelopeType_amp].count;
 		}
 
-		int lfoat = 0x1000;
+		unsigned int lfoShift = 0x1000;
 		if (pitchSustain) {
 			// Calculate LFO position
 			// LFO does not kick in completely until pitch envelope sustains
@@ -225,30 +248,24 @@
 				if (lfoPos >= patchCache->lfoperiod)
 					lfoPos = 0;
 				int lfoatm = FIXEDPOINT_UDIV(lfoPos, patchCache->lfoperiod, 16);
-				int lfoatr = sintable[lfoatm];
-				lfoat = lfoptable[patchCache->lfodepth][lfoatr];
+				int lfoatr = synth->tables.sintable[lfoatm];
+				lfoShift = synth->tables.lfoShift[patchCache->lfodepth][lfoatr];
 			}
 		} else {
 			// Calculate Pitch envelope
 			envval = getPitchEnvelope();
 			int pd = patchCache->pitchEnv.depth;
-			pitchEnvVal = penvtable[pd][envval];
+			pitchEnvVal = synth->tables.pitchEnvVal[pd][envval];
 		}
 
 		int delta;
-		// These two are only for PCM partials, obviously
-		PCMWaveEntry *pcmWave = NULL; // Initialise to please compiler
-		Bit32u pcmAddr = 0; // Initialise to please compiler
 
 		// Wrap positions or end if necessary
 		if (patchCache->PCMPartial) {
 			// PCM partial
-			int len;
-			pcmWave = &synth->PCMList[patchCache->pcm];
 
-			delta = noteLookup->wavTable[patchCache->pcm];
-			pcmAddr = pcmWave->addr;
-			len = pcmWave->len;
+			delta = noteLookup->wavTable[pcmNum];
+			int len = pcmWave->len;
 			if (partialOff.pcmplace >= len) {
 				if (pcmWave->loop) {
 					//partialOff.pcmplace = partialOff.pcmoffset = 0;
@@ -261,21 +278,20 @@
 			}
 		} else {
 			// Synthesis partial
-			delta = 0x10707;
-			partialOff.pcmplace %= (Bit16u)(noteLookup->div << 1);
+			delta = 0x10000;
+			partialOff.pcmplace %= (Bit16u)noteLookup->div2;
 		}
 
 		// Build delta for position of next sample
 		// Fix delta code
-		Bit64u tdelta = (Bit64u)delta;
+		Bit32u tdelta = delta;
 #if MT32EMU_ACCURATENOTES == 0
-		tdelta = (tdelta * fineShift) >> 12;
+		tdelta = FIXEDPOINT_UMULT(tdelta, fineShift, 12);
 #endif
-		tdelta = (tdelta * pitchEnvVal) >> 12;
-		tdelta = (tdelta * lfoat) >> 12;
-		tdelta = (tdelta * bendShift) >> 12;
+		tdelta = FIXEDPOINT_UMULT(tdelta, pitchEnvVal, 12);
+		tdelta = FIXEDPOINT_UMULT(tdelta, lfoShift, 12);
+		tdelta = FIXEDPOINT_UMULT(tdelta, bendShift, 12);
 		delta = (int)tdelta;
-		Bit32u volume = *poly->volumeptr;
 
 		// Get waveform - either PCM or synthesized sawtooth or square
 		if (ampEnvVal > 0) {
@@ -283,20 +299,21 @@
 				// Render PCM sample
 				int ra, rb, dist;
 				Bit32u taddr;
+				Bit32u pcmAddr = pcmWave->addr;
 				if (delta < 0x10000) {
 					// Linear sound interpolation
 					taddr = pcmAddr + partialOff.pcmplace;
-					ra = synth->romfile[taddr];
+					ra = synth->pcmROMData[taddr];
 					taddr++;
 					if (taddr == pcmAddr + pcmWave->len) {
 						// Past end of PCM
 						if (pcmWave->loop) {
-							rb = synth->romfile[pcmAddr];
+							rb = synth->pcmROMData[pcmAddr];
 						} else {
 							rb = 0;
 						}
 					} else {
-						rb = synth->romfile[taddr];
+						rb = synth->pcmROMData[taddr];
 					}
 					dist = rb - ra;
 					sample = (ra + ((dist * (Bit32s)(partialOff.pcmoffset >> 8)) >> 8));
@@ -306,7 +323,7 @@
 					// a point.  This is too slow.  The following approximates this as fast as possible
 					int idelta = delta >> 16;
 					taddr = pcmAddr + partialOff.pcmplace;
-					ra = synth->romfile[taddr++];
+					ra = synth->pcmROMData[taddr++];
 					for (int ix = 0; ix < idelta - 1; ix++) {
 						if (taddr == pcmAddr + pcmWave->len) {
 							// Past end of PCM
@@ -317,38 +334,31 @@
 								break;
 							}
 						}
-						ra += synth->romfile[taddr++];
+						ra += synth->pcmROMData[taddr++];
 					}
 					sample = ra / idelta;
 				}
 			} else {
 				// Render synthesised sample
-				Bit32u div = noteLookup->div;
-				int wf = patchCache->waveform;
 				int toff = partialOff.pcmplace;
 				int minorplace = partialOff.pcmoffset >> 14;
-
+				Bit32s filterInput;
 				Bit32s filtval = getFiltEnvelope();
 
 				//synth->printDebug("Filtval: %d", filtval);
 
-				if (wf==0) {
+				if ((patchCache->waveform & 1) == 0) {
 					// Square waveform.  Made by combining two pregenerated bandlimited
 					// sawtooth waveforms
-					// Pulse width is not yet correct
-					if (div == 0) {
-						synth->printDebug("ERROR: div=0 generating square wave, this should never happen!");
-						div = 1;
-					}
-					Bit32u ofsA = toff % div;
-					Bit32u ofsB = toff + FIXEDPOINT_UMULT(div, pulsetable[pulsewidth], 8);
-					ofsB = ofsB % div;
-					Bit16s pa = noteLookup->waveforms[0][(ofsA << 2) + minorplace];
-					Bit16s pb = noteLookup->waveforms[0][(ofsB << 2) + minorplace];
-					sample = (pa - pb) * 4;
+					Bit32u ofsA = ((toff << 2) + minorplace) % noteLookup->waveformSize[0];
+					int width = FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[pulsewidth], 7);
+					Bit32u ofsB = (ofsA + width) % noteLookup->waveformSize[0];
+					Bit16s pa = noteLookup->waveforms[0][ofsA];
+					Bit16s pb = noteLookup->waveforms[0][ofsB];
+					filterInput = pa - pb;
 					// Non-bandlimited squarewave
 					/*
-					ofs = ((div << 1) * pulsetable[patchCache->pulsewidth]) >> 8;
+					ofs = FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[patchCache->pulsewidth], 8);
 					if (toff < ofs)
 						sample = 1 * WGAMP;
 					else
@@ -360,32 +370,38 @@
 					// square wave and multiplies it by a full cosine
 					int waveoff = (toff << 2) + minorplace;
 					if (toff < noteLookup->sawTable[pulsewidth])
-						sample = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]];
+						filterInput = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]];
 					else
-						sample = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]];
-					sample = sample * 4;
+						filterInput = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]];
 					// This is the correct way
 					// Seems slow to me (though bandlimited) -- doesn't seem to
 					// sound any better though
 					/*
 					//int pw = (patchCache->pulsewidth * pulsemod[filtval]) >> 8;
 
-					Bit32u ofs = toff % div;
+					Bit32u ofs = toff % (noteLookup->div2 >> 1);
 
-					Bit32u ofs3 = toff + FIXEDPOINT_UMULT(div, pulsetable[patchCache->pulsewidth], 8);
-					ofs3 = ofs3 % div;
+					Bit32u ofs3 = toff + FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[patchCache->pulsewidth], 9);
+					ofs3 = ofs3 % (noteLookup->div2 >> 1);
 
 					pa = noteLookup->waveforms[0][ofs];
 					pb = noteLookup->waveforms[0][ofs3];
-					sample = ((pa - pb) * noteLookup->waveforms[2][toff]) / WGAMP;
-					sample = sample *4;
+					sample = ((pa - pb) * noteLookup->waveforms[2][toff]) / 2;
 					*/
 				}
 
 				//Very exact filter
 				if (filtval > ((FILTERGRAN * 15) / 16))
 					filtval = ((FILTERGRAN * 15) / 16);
-				sample = (Bit32s)floor((synth->iirFilter)((float)sample, &history[0], filtcoeff[filtval][(int)patchCache->filtEnv.resonance], patchCache->filtEnv.resonance));
+				sample = (Bit32s)(floorf((synth->iirFilter)((float)filterInput, &history[0], synth->tables.filtCoeff[filtval][(int)patchCache->filtEnv.resonance])) / synth->tables.resonanceFactor[patchCache->filtEnv.resonance]);
+				if (sample < -32768) {
+					synth->printDebug("Overdriven amplitude for %d: %d:=%d < -32768", patchCache->waveform, filterInput, sample);
+					sample = -32768;
+				}
+				else if (sample > 32767) {
+					synth->printDebug("Overdriven amplitude for %d: %d:=%d > 32767", patchCache->waveform, filterInput, sample);
+					sample = 32767;
+				}
 			}
 		}
 
@@ -553,7 +569,7 @@
 		}
 	} else if (useNoisePair) {
 		// Generate noise for pairless ring mix
-		pairBuf = smallnoise;
+		pairBuf = synth->tables.noiseBuf;
 	}
 
 	Bit16s *myBuf = generateSamples(length);
@@ -561,7 +577,7 @@
 	if (myBuf == NULL && pairBuf == NULL)
 		return false;
 
-	Bit16s * p1buf, * p2buf;
+	Bit16s *p1buf, *p2buf;
 
 	if (structurePosition == 0 || pairBuf == NULL) {
 		p1buf = myBuf;
@@ -574,31 +590,31 @@
 	//synth->printDebug("mixType: %d", mixType);
 
 	Bit16s *mixedBuf;
-	switch(mixType) {
-		case 0:
-			// Standard sound mix
-			mixedBuf = mixBuffers(p1buf, p2buf, length);
-			break;
+	switch (mixType) {
+	case 0:
+		// Standard sound mix
+		mixedBuf = mixBuffers(p1buf, p2buf, length);
+		break;
 
-		case 1:
-			// Ring modulation with sound mix
-			mixedBuf = mixBuffersRingMix(p1buf, p2buf, length);
-			break;
+	case 1:
+		// Ring modulation with sound mix
+		mixedBuf = mixBuffersRingMix(p1buf, p2buf, length);
+		break;
 
-		case 2:
-			// Ring modulation alone
-			mixedBuf = mixBuffersRing(p1buf, p2buf, length);
-			break;
+	case 2:
+		// Ring modulation alone
+		mixedBuf = mixBuffersRing(p1buf, p2buf, length);
+		break;
 
-		case 3:
-			// Stereo mixing.  One partial to one speaker channel, one to another.
-			// FIXME:KG: Surely we should be multiplying by the left/right volumes here?
-			mixBuffersStereo(p1buf, p2buf, partialBuf, length);
-			return true;
+	case 3:
+		// Stereo mixing.  One partial to one speaker channel, one to another.
+		// FIXME:KG: Surely we should be multiplying by the left/right volumes here?
+		mixBuffersStereo(p1buf, p2buf, partialBuf, length);
+		return true;
 
-		default:
-			mixedBuf = mixBuffers(p1buf, p2buf, length);
-			break;
+	default:
+		mixedBuf = mixBuffers(p1buf, p2buf, length);
+		break;
 	}
 
 	if (mixedBuf == NULL)
@@ -626,13 +642,10 @@
 Bit32s Partial::getFiltEnvelope() {
 	int reshigh;
 
-	int cutoff,depth,keyfollow, realfollow;
+	int cutoff, depth;
 
 	EnvelopeStatus *tStat  = &envs[EnvelopeType_filt];
 
-	keyfollow = filtVal;
-	realfollow = realVal;
-
 	if (tStat->decaying) {
 		reshigh = tStat->envbase;
 		reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize));
@@ -653,9 +666,17 @@
 				tStat->envstat++;
 				tStat->envpos = 0;
 				if (tStat->envstat == 3) {
-					tStat->envsize = lasttimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]];
+					tStat->envsize = synth->tables.envTime[(int)patchCache->filtEnv.envtime[tStat->envstat]];
 				} else {
-					tStat->envsize = (envtimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8;
+					Bit32u envTime = (int)patchCache->filtEnv.envtime[tStat->envstat];
+					if(tStat->envstat > 1) {
+						int envDiff = abs(patchCache->filtEnv.envlevel[tStat->envstat] - patchCache->filtEnv.envlevel[tStat->envstat - 1]);
+						if(envTime > synth->tables.envDeltaMaxTime[envDiff]) {
+							envTime = synth->tables.envDeltaMaxTime[envDiff];
+						}
+					}
+
+					tStat->envsize = (synth->tables.envTime[envTime] * keyLookup->envTimeMult[(int)patchCache->filtEnv.envtkf]) >> 8;
 				}
 
 				tStat->envsize++;
@@ -676,48 +697,52 @@
 	depth = patchCache->filtEnv.envdepth;
 
 	//int sensedep = (depth * 127-patchCache->filtEnv.envsense) >> 7;
-	depth = (depth * filveltable[poly->vel][(int)patchCache->filtEnv.envsense]) >> 8;
+	depth = FIXEDPOINT_UMULT(depth, synth->tables.tvfVelfollowMult[poly->vel][(int)patchCache->filtEnv.envsense], 8);
 
 	int bias = patchCache->tvfbias;
 	int dist;
 
-	if (bias!=0) {
+	if (bias != 0) {
 		//FIXME:KG: Is this really based on pitch (as now), or key pressed?
 		//synth->printDebug("Cutoff before %d", cutoff);
 		if (patchCache->tvfdir == 0) {
 			if (noteVal < bias) {
 				dist = bias - noteVal;
-				cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8;
+				cutoff = FIXEDPOINT_UMULT(cutoff, synth->tables.tvfBiasMult[patchCache->tvfblevel][dist], 8);
 			}
 		} else {
 			// > Bias
 			if (noteVal > bias) {
 				dist = noteVal - bias;
-				cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8;
+				cutoff = FIXEDPOINT_UMULT(cutoff, synth->tables.tvfBiasMult[patchCache->tvfblevel][dist], 8);
 			}
 
 		}
 		//synth->printDebug("Cutoff after %d", cutoff);
 	}
 
-	depth = (depth * noteLookup->fildepTable[patchCache->tvfdepth]) >> 8;
+	depth = (depth * keyLookup->envDepthMult[patchCache->filtEnv.envdkf]) >> 8;
 	reshigh = (reshigh * depth) >> 7;
 
 	Bit32s tmp;
 
-	cutoff *= keyfollow;
-	cutoff /= realfollow;
+	cutoff *= filtVal;
+	cutoff /= realVal; //FIXME:KG: With filter keyfollow 0, this makes no sense. What's correct?
 
-	reshigh *= keyfollow;
-	reshigh /= realfollow;
+	reshigh *= filtVal;
+	reshigh /= realVal; //FIXME:KG: As above for cutoff
 
-	if (cutoff>100)
+	if (patchCache->waveform == 1) {
+		reshigh = (reshigh * 65) / 100;
+	}
+
+	if (cutoff > 100)
 		cutoff = 100;
-	else if (cutoff<0)
+	else if (cutoff < 0)
 		cutoff = 0;
-	if (reshigh>100)
+	if (reshigh > 100)
 		reshigh = 100;
-	else if (reshigh<0)
+	else if (reshigh < 0)
 		reshigh = 0;
 	tmp = noteLookup->nfiltTable[cutoff][reshigh];
 	//tmp *= keyfollow;
@@ -743,7 +768,7 @@
 
 	if (tStat->decaying) {
 		tc = tStat->envbase;
-		tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
+		tc += (tStat->envdist * tStat->envpos) / tStat->envsize;
 		if (tc < 0)
 			tc = 0;
 		if ((tStat->envpos >= tStat->envsize) || (tc == 0)) {
@@ -752,47 +777,56 @@
 			return 0;
 		}
 	} else {
-		if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
-			if (tStat->envstat==-1)
+		if ((tStat->envstat == -1) || (tStat->envpos >= tStat->envsize)) {
+			if (tStat->envstat == -1)
 				tStat->envbase = 0;
 			else
 				tStat->envbase = patchCache->ampEnv.envlevel[tStat->envstat];
 			tStat->envstat++;
 			tStat->envpos = 0;
+			if (tStat->envstat == 4) {
+				//synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
+				tc = patchCache->ampEnv.envlevel[3];
+				if (!poly->sustain)
+					startDecay(EnvelopeType_amp, tc);
+				else
+					tStat->sustaining = true;
+				goto PastCalc;
+			}
+			Bit8u targetLevel = patchCache->ampEnv.envlevel[tStat->envstat];
+			tStat->envdist = targetLevel - tStat->envbase;
+			Bit32u envTime = patchCache->ampEnv.envtime[tStat->envstat];
+			if (targetLevel == 0) {
+				tStat->envsize = synth->tables.envDecayTime[envTime];
+			} else {
+				int envLevelDelta = abs(tStat->envdist);
+				if (envTime > synth->tables.envDeltaMaxTime[envLevelDelta]) {
+					envTime = synth->tables.envDeltaMaxTime[envLevelDelta];
+				}
+				tStat->envsize = synth->tables.envTime[envTime];
+			}
 
-			switch(tStat->envstat) {
+			// Time keyfollow is used by all sections of the envelope (confirmed on CM-32L)
+			tStat->envsize = FIXEDPOINT_UMULT(tStat->envsize, keyLookup->envTimeMult[(int)patchCache->ampEnv.envtkf], 8);
+
+			switch (tStat->envstat) {
 			case 0:
 				//Spot for velocity time follow
 				//Only used for first attack
-				tStat->envsize = (envtimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]] * veltkeytable[(int)patchCache->ampEnv.envvkf][poly->vel]) >> 8;
+				tStat->envsize = FIXEDPOINT_UMULT(tStat->envsize, synth->tables.envTimeVelfollowMult[(int)patchCache->ampEnv.envvkf][poly->vel], 8);
 				//synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
 				break;
+			case 1:
+			case 2:
 			case 3:
-				// Final attack envelope uses same time table as the decay
-				//tStat->envsize = decaytimetable[patchCache->ampEnv.envtime[tStat->envstat]];
-				tStat->envsize = lasttimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]];
 				//synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
 				break;
-			case 4:
-				//synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
-				tc = patchCache->ampsustain;
-				if (!poly->sustain)
-					startDecay(EnvelopeType_amp, tc);
-				else
-					tStat->sustaining = true;
-
-				goto PastCalc;
 			default:
-				//Spot for timekey follow
-				//Only used in subsquent envelope parameters, including the decay
-				tStat->envsize = (envtimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8;
-
-				//synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
+				synth->printDebug("Invalid TVA envelope number %d hit!", tStat->envstat);
 				break;
 			}
 
 			tStat->envsize++;
-			tStat->envdist = patchCache->ampEnv.envlevel[tStat->envstat] - tStat->envbase;
 
 			if (tStat->envdist != 0) {
 				tStat->counter = abs(tStat->envsize / tStat->envdist);
@@ -806,7 +840,7 @@
 		tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
 		tStat->count = tStat->counter;
 PastCalc:
-		tc = (tc * (Bit32s)patchCache->amplevel) >> 7;
+		tc = (tc * (Bit32s)patchCache->ampEnv.level) / 100;
 	}
 
 	// Prevlevel storage is bottle neck
@@ -821,13 +855,13 @@
 				// < Bias
 				if (noteVal < bias) {
 					int dist = bias - noteVal;
-					tc = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8;
+					tc = FIXEDPOINT_UMULT(tc, synth->tables.tvaBiasMult[patchCache->ampblevel[i]][dist], 8);
 				}
 			} else {
 				// > Bias
 				if (noteVal > bias) {
 					int dist = noteVal - bias;
-					tc = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8;
+					tc = FIXEDPOINT_UMULT(tc, synth->tables.tvaBiasMult[patchCache->ampblevel[i]][dist], 8);
 				}
 			}
 		}
@@ -863,7 +897,14 @@
 				tStat->envstat++;
 
 				tStat->envbase = patchCache->pitchEnv.level[tStat->envstat];
-				tStat->envsize = (envtimetable[(int)patchCache->pitchEnv.time[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8;
+
+				Bit32u envTime = patchCache->pitchEnv.time[tStat->envstat];
+				int envDiff = abs(patchCache->pitchEnv.level[tStat->envstat] - patchCache->pitchEnv.level[tStat->envstat + 1]);
+				if (envTime > synth->tables.envDeltaMaxTime[envDiff]) {
+					envTime = synth->tables.envDeltaMaxTime[envDiff];
+				}
+
+				tStat->envsize = (synth->tables.envTime[envTime] * keyLookup->envTimeMult[(int)patchCache->pitchEnv.timekeyfollow]) >> 8;
 
 				tStat->envpos = 0;
 				tStat->envsize++;
@@ -892,17 +933,17 @@
 	tStat->envpos = 0;
 	tStat->envbase = startval;
 
-	switch(envnum) {
+	switch (envnum) {
 	case EnvelopeType_amp:
-		tStat->envsize = (decaytimetable[(int)patchCache->ampEnv.envtime[4]] * noteLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8;
+		tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->ampEnv.envtime[4]], keyLookup->envTimeMult[(int)patchCache->ampEnv.envtkf], 8);
 		tStat->envdist = -startval;
 		break;
 	case EnvelopeType_filt:
-		tStat->envsize = (decaytimetable[(int)patchCache->filtEnv.envtime[4]] * noteLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8;
+		tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->filtEnv.envtime[4]], keyLookup->envTimeMult[(int)patchCache->filtEnv.envtkf], 8);
 		tStat->envdist = -startval;
 		break;
 	case EnvelopeType_pitch:
-		tStat->envsize = (decaytimetable[(int)patchCache->pitchEnv.time[3]] * noteLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8 ;
+		tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->pitchEnv.time[3]], keyLookup->envTimeMult[(int)patchCache->pitchEnv.timekeyfollow], 8);
 		tStat->envdist = patchCache->pitchEnv.level[4] - startval;
 		break;
 	default:

Index: partial.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/softsynth/mt32/partial.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- partial.h	25 Dec 2004 18:34:44 -0000	1.1
+++ partial.h	20 Mar 2005 16:24:55 -0000	1.2
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2004 Various contributors
+/* Copyright (c) 2003-2005 Various contributors
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -60,8 +60,6 @@
 
 	Bit16s myBuffer[MAX_SAMPLE_OUTPUT];
 
-	bool play;
-
 	// Keyfollowed note value
 #if MT32EMU_ACCURATENOTES == 1
 	NoteLookup noteLookupStorage;
@@ -71,12 +69,15 @@
 	int fineShift;
 #endif
 	const NoteLookup *noteLookup; // LUTs for this noteVal
+	const KeyLookup *keyLookup; // LUTs for the clamped (12..108) key
 
 	// Keyfollowed filter values
 	int realVal;
 	int filtVal;
 
-	EnvelopeStatus envs[3];
+	// Only used for PCM partials
+	int pcmNum;
+	PCMWaveEntry *pcmWave;
 
 	int pulsewidth;
 
@@ -109,6 +110,9 @@
 
 public:
 	const PatchCache *patchCache;
+	EnvelopeStatus envs[3];
+	bool play;
+
 	PatchCache cachebackup;
 
 	Partial *pair;
@@ -118,7 +122,9 @@
 	Partial(Synth *synth);
 	~Partial();
 
-	int getOwnerPart();
+	int getOwnerPart() const;
+	int getKey() const;
+	const dpoly *getDpoly() const;
 	bool isActive();
 	void activate(int part);
 	void deactivate(void);

Index: partialManager.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/softsynth/mt32/partialManager.cpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- partialManager.cpp	25 Dec 2004 18:34:44 -0000	1.1
+++ partialManager.cpp	20 Mar 2005 16:24:55 -0000	1.2
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2004 Various contributors
+/* Copyright (c) 2003-2005 Various contributors
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -264,3 +264,9 @@
 	}
 	return needed == 0;
 }
+
+const Partial *PartialManager::getPartial(unsigned int partialNum) const {
+	if (partialNum > MT32EMU_MAX_PARTIALS - 1)
+		return NULL;
+	return partialTable[partialNum];
+}

Index: partialManager.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/softsynth/mt32/partialManager.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- partialManager.h	25 Dec 2004 18:34:44 -0000	1.1
+++ partialManager.h	20 Mar 2005 16:24:55 -0000	1.2
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2004 Various contributors
+/* Copyright (c) 2003-2005 Various contributors
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -35,6 +35,7 @@
 	Bit32s partialPart[9]; // The count of partials played per part
 
 public:
+
 	PartialManager(Synth *synth);
 	~PartialManager();
 	Partial *allocPartial(int partNum);
@@ -47,6 +48,7 @@
 	bool shouldReverb(int i);
 	void clearAlreadyOutputed();
 	void getPerPartPartialUsage(int usage[9]);
+	const Partial *getPartial(unsigned int partialNum) const;
 };
 
 }

Index: structures.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/softsynth/mt32/structures.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- structures.h	25 Dec 2004 18:34:44 -0000	1.1
+++ structures.h	20 Mar 2005 16:24:55 -0000	1.2
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2004 Various contributors
+/* Copyright (c) 2003-2005 Various contributors
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -26,6 +26,11 @@
 
 const unsigned int MAX_SAMPLE_OUTPUT = 4096;
 
+// MT32EMU_MEMADDR() converts from sysex-padded, MT32EMU_SYSEXMEMADDR converts to it
+// Roland provides documentation using the sysex-padded addresses, so we tend to use that in code and output
+#define MT32EMU_MEMADDR(x) ((((x) & 0x7f0000) >> 2) | (((x) & 0x7f00) >> 1) | ((x) & 0x7f))
+#define MT32EMU_SYSEXMEMADDR(x) ((((x) & 0x1FC000) << 2) | (((x) & 0x3F80) << 1) | ((x) & 0x7f))
+
 #ifdef _MSC_VER
 #define  MT32EMU_ALIGN_PACKED __declspec(align(1))
 typedef unsigned __int64   Bit64u;
@@ -68,7 +73,7 @@
 			Bit8u fine;  // 0-100 (-50 to +50 (cents?))
 			Bit8u keyfollow;  // 0-16 (-1,-1/2,0,1,1/8,1/4,3/8,1/2,5/8,3/4,7/8,1,5/4,3/2,2.s1,s2)
 			Bit8u bender;  // 0,1 (ON/OFF)
-			Bit8u waveform; //  0-1 (SQU/SAW)
+			Bit8u waveform; // MT-32: 0-1 (SQU/SAW); LAPC-I: WG WAVEFORM/PCM BANK 0 - 3 (SQU/1, SAW/1, SQU/2, SAW/2)
 			Bit8u pcmwave; // 0-127 (1-128)
 			Bit8u pulsewid; // 0-100
 			Bit8u pwvelo; // 0-14 (-7 - +7)
@@ -129,28 +134,32 @@
 } MT32EMU_ALIGN_PACKED;
 
 struct MemParams {
+	// NOTE: The MT-32 documentation only specifies PatchTemp areas for parts 1-8.
+	// The LAPC-I documentation specified an additional area for rhythm at the end,
+	// where all parameters but fine tune, assign mode and output level are ignored
 	struct PatchTemp {
 		PatchParam patch;
 		Bit8u outlevel; // OUTPUT LEVEL 0-100
 		Bit8u panpot; // PANPOT 0-14 (R-L)
 		Bit8u dummyv[6];
-	} MT32EMU_ALIGN_PACKED patchSettings[8];
+	} MT32EMU_ALIGN_PACKED patchSettings[9];
 
 	struct RhythmTemp {
 		Bit8u timbre; // TIMBRE  0-94 (M1-M64,R1-30,OFF)
 		Bit8u outlevel; // OUTPUT LEVEL 0-100
 		Bit8u panpot; // PANPOT 0-14 (R-L)
 		Bit8u reverbSwitch;  // REVERB SWITCH 0-1 (OFF,ON)
-	} MT32EMU_ALIGN_PACKED rhythmSettings[86]; // FIXME: Was 64, but let's support the next model...
+	} MT32EMU_ALIGN_PACKED rhythmSettings[85];
 
 	TimbreParam MT32EMU_ALIGN_PACKED timbreSettings[8];
 
 	PatchParam MT32EMU_ALIGN_PACKED patches[128];
 
+	// NOTE: There are only 30 timbres in the "rhythm" bank for MT-32; the additional 34 are for LAPC-I and above
 	struct PaddedTimbre {
 		TimbreParam timbre;
 		Bit8u padding[10];
-	} MT32EMU_ALIGN_PACKED timbres[64 + 64 + 64 + 30]; // Group A, Group B, Memory, Rhythm
+	} MT32EMU_ALIGN_PACKED timbres[64 + 64 + 64 + 64]; // Group A, Group B, Memory, Rhythm
 
 	struct SystemArea {
 		Bit8u masterTune; // MASTER TUNE 0-127 432.1-457.6Hz
@@ -163,18 +172,6 @@
 	} MT32EMU_ALIGN_PACKED system;
 };
 
-struct MemBanks {
-	Bit8u pTemp[8][sizeof(MemParams::PatchTemp)];
-	Bit8u rTemp[86][sizeof(MemParams::RhythmTemp)];
-	Bit8u tTemp[8][sizeof(TimbreParam)];
-	Bit8u patchBank[128][sizeof(PatchParam)];
-	Bit8u timbreBank[64 + 64 + 64 + 30][sizeof(MemParams::PaddedTimbre)];
-	Bit8u systemBank[sizeof(MemParams::SystemArea)];
-	// System memory 0x100000
-	// Display 0x200000
-	// Reset 0x7F0000
-};
-
 #if defined(_MSC_VER) || defined (__MINGW32__)
 #pragma pack(pop)
 #else
@@ -227,9 +224,7 @@
 	int ampdir[2];
 
 	int ampdepth;
-	int ampenvdir;
 	int amplevel;
-	int tvfdepth;
 
 	bool useBender;
 	float benderRange; // 0.0, 1.0, .., 24.0 (semitones)
@@ -238,7 +233,6 @@
 	TimbreParam::partialParam::tvaParam ampEnv;
 	TimbreParam::partialParam::tvfParam filtEnv;
 
-	Bit32s ampsustain;
 	Bit32s pitchsustain;
 	Bit32s filtsustain;
 

Index: synth.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/softsynth/mt32/synth.cpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- synth.cpp	25 Dec 2004 18:34:44 -0000	1.1
+++ synth.cpp	20 Mar 2005 16:24:55 -0000	1.2
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2004 Various contributors
+/* Copyright (c) 2003-2005 Various contributors
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -30,7 +30,15 @@
 
 const int MAX_SYSEX_SIZE = 512;
 
-float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLevel) {
+const ControlROMMap ControlROMMaps[3] = {
+	// ID    IDc IDbytes                     PCMmap  PCMc  tmbrA   tmbrAO, tmbrB   tmbrBO, tmbrR   trC  rhythm  rhyC  rsrv    panpot  prog
+	{0x4010, 22, "\000 ver1.07 10 Oct, 87 ", 0x3000,  128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200,  30, 0x73fe,  85,  0x57B1, 0x57BA, 0x57CC}, // MT-32 revision 1
+	{0x4010, 22, "\000verX.XX  30 Sep, 88 ", 0x3000,  128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200,  30, 0x741C,  85,  0x57E5, 0x57EE, 0x5800}, // MT-32 Blue Ridge mod
+	{0x2205, 22, "\000CM32/LAPC1.02 891205", 0x8100,  256, 0x8000, 0x8000, 0x8080, 0x8000, 0x8500,  64, 0x8580,  85,  0x4F93, 0x4F9C, 0x4FAE}  // CM-32L
+	// (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp)
+};
+
+float iir_filter_normal(float input, float *hist1_ptr, float *coef_ptr) {
 	float *hist2_ptr;
 	float output,new_hist;
 
@@ -60,8 +68,6 @@
 	*hist2_ptr++ = *hist1_ptr;
 	*hist1_ptr++ = new_hist;
 
-	output *= ResonInv[revLevel];
-
 	return(output);
 }
 
@@ -79,7 +85,6 @@
 	isOpen = false;
 	reverbModel = NULL;
 	partialManager = NULL;
-	memset(noteLookups, 0, sizeof(noteLookups));
 	memset(parts, 0, sizeof(parts));
 }
 
@@ -112,7 +117,7 @@
 		delete reverbModel;
 	reverbModel = new revmodel();
 
-	switch(newRevMode) {
+	switch (newRevMode) {
 	case 0:
 		reverbModel->setroomsize(.1f);
 		reverbModel->setdamp(.75f);
@@ -204,10 +209,21 @@
 	if (file == NULL) {
 		return false;
 	}
-	bool rc = (file->read(controlROMData, sizeof(controlROMData)) == sizeof(controlROMData));
+	bool rc = (file->read(controlROMData, CONTROL_ROM_SIZE) == CONTROL_ROM_SIZE);
 
 	closeFile(file);
-	return rc;
+	if (!rc)
+		return rc;
+
+	// Control ROM successfully loaded, now check whether it's a known type
+	controlROMMap = NULL;
+	for (unsigned int i = 0; i < sizeof (ControlROMMaps) / sizeof (ControlROMMaps[0]); i++) {
+		if (memcmp(&controlROMData[ControlROMMaps[i].idPos], ControlROMMaps[i].idBytes, ControlROMMaps[i].idLen) == 0) {
+			controlROMMap = &ControlROMMaps[i];
+			return true;
+		}
+	}
+	return false;
 }
 
 bool Synth::loadPCMROM(const char *filename) {
@@ -216,7 +232,8 @@
 		return false;
 	}
 	bool rc = true;
-	for (int i = 0; ; i++) {
+	int i;
+	for (i = 0; i < pcmROMSize; i++) {
 		Bit8u s;
 		if (!file->readBit8u(&s)) {
 			if (!file->isEOF()) {
@@ -229,7 +246,7 @@
 			if (!file->isEOF()) {
 				rc = false;
 			} else {
-				printDebug("ROM file has an odd number of bytes! Ignoring last");
+				printDebug("PCM ROM file has an odd number of bytes! Ignoring last");
 			}
 			break;
 		}
@@ -237,7 +254,6 @@
 		short e;
 		int bit;
 		int u;
-
 		int order[16] = {0, 9, 1 ,2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8};
 
 		e = 0;
@@ -259,7 +275,7 @@
 		e = (int)((float)e * (x/3200));
 		*/
 
-		// File is encoded in dB, convert to PCM
+		// File is companded (dB?), convert to linear PCM
 		// MINDB = -96
 		// MAXDB = -15
 		float testval;
@@ -271,72 +287,95 @@
 		if (e > 0)
 			vol = -vol;
 
-		romfile[i] = (Bit16s)vol;
-
+		pcmROMData[i] = (Bit16s)vol;
+	}
+	if (i != pcmROMSize) {
+		printDebug("PCM ROM file is too short (expected %d, got %d)", pcmROMSize, i);
+		rc = false;
 	}
 	closeFile(file);
 	return rc;
 }
 
-struct ControlROMPCMStruct
-{
-	Bit8u pos;
-	Bit8u len;
-	Bit8u pitchLSB;
-	Bit8u pitchMSB;
-};
-
-void Synth::initPCMList() {
-	ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[0x3000];
-	for (int i = 0; i < 128; i++) {
+bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) {
+	ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress];
+	for (int i = 0; i < count; i++) {
 		int rAddr = tps[i].pos * 0x800;
 		int rLenExp = (tps[i].len & 0x70) >> 4;
 		int rLen = 0x800 << rLenExp;
 		bool rLoop = (tps[i].len & 0x80) != 0;
 		//Bit8u rFlag = tps[i].len & 0x0F;
 		Bit16u rTuneOffset = (tps[i].pitchMSB << 8) | tps[i].pitchLSB;
-		//FIXME:KG: Pick a number, any number. The one below sounded best to me in listening tests, but needs to be confirmed.
-		double STANDARDFREQ = 432.1;
-		float rTune = (float)(STANDARDFREQ * pow(2.0, (0x5000 - rTuneOffset) / 4096.0 - 9.0 / 12.0));
+		// The number below is confirmed to a reasonable degree of accuracy on CM-32L
+		double STANDARDFREQ = 442.0;
+		float rTune = (float)(STANDARDFREQ * pow(2.0, (0x5000 - rTuneOffset) / 4056.0 - 9.0 / 12.0));
 		//printDebug("%f,%d,%d", pTune, tps[i].pitchCoarse, tps[i].pitchFine);
-		PCMList[i].addr = rAddr;
-		PCMList[i].len = rLen;
-		PCMList[i].loop = rLoop;
-		PCMList[i].tune = rTune;
+		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 = rLoop;
+		pcmWaves[i].tune = rTune;
 	}
+	return false;
 }
 
-void Synth::initRhythmTimbre(int timbreNum, const Bit8u *mem) {
+bool Synth::initRhythmTimbre(int timbreNum, const Bit8u *mem, unsigned int memLen) {
+	if (memLen < sizeof(TimbreParam::commonParam)) {
+		return false;
+	}
 	TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre;
 	memcpy(&timbre->common, mem, 14);
-	mem += 14;
+	unsigned int memPos = 14;
 	char drumname[11];
 	strncpy(drumname, timbre->common.name, 10);
 	drumname[10] = 0;
 	for (int t = 0; t < 4; t++) {
 		if (((timbre->common.pmute >> t) & 0x1) == 0x1) {
-			memcpy(&timbre->partial[t], mem, 58);
-			mem += 58;
+			if (memPos + 58 >= memLen) {
+				return false;
+			}
+			memcpy(&timbre->partial[t], mem + memPos, 58);
+			memPos += 58;
 		}
 	}
+	return true;
 }
 
-void Synth::initRhythmTimbres() {
-	const Bit8u *drumMap = &controlROMData[0x3200];
+bool Synth::initRhythmTimbres(Bit16u mapAddress, Bit16u count) {
+	const Bit8u *drumMap = &controlROMData[mapAddress];
 	int timbreNum = 192;
-	for (Bit16u i = 0; i < 60; i += 2) {
+	for (Bit16u i = 0; i < count * 2; i += 2) {
 		Bit16u address = (drumMap[i + 1] << 8) | drumMap[i];
-		initRhythmTimbre(timbreNum++, &controlROMData[address]);
+		/*
+		// This check is nonsensical when the control ROM is the full 64KB addressable by 16-bit absolute pointers (which it is)
+		if (address >= CONTROL_ROM_SIZE) {
+			printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre address 0x%04x", i, address);
+			return false;
+		}
+		*/
+		if (!initRhythmTimbre(timbreNum++, &controlROMData[address], CONTROL_ROM_SIZE - address)) {
+			printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre 0x%04x", i, address);
+			return false;
+		}
 	}
+	return true;
 }
 
-void Synth::initTimbres(Bit16u mapAddress, int startTimbre) {
+bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int startTimbre) {
 	for (Bit16u i = mapAddress; i < mapAddress + 0x80; i += 2) {
 		Bit16u address = (controlROMData[i + 1] << 8) | controlROMData[i];
-		address = address + (mapAddress - 0x8000);
+		if (address + sizeof(TimbreParam) > CONTROL_ROM_SIZE) {
+			printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre address 0x%04x", i, address);
+			return false;
+		}
+		address = address + offset;
 		TimbreParam *timbre = &mt32ram.timbres[startTimbre++].timbre;
 		memcpy(timbre, &controlROMData[address], sizeof(TimbreParam));
 	}
+	return true;
 }
 
 bool Synth::open(SynthProperties &useProp) {
@@ -351,41 +390,59 @@
 
 	// This is to help detect bugs
 	memset(&mt32ram, '?', sizeof(mt32ram));
-	for (int i = 128; i < 192; i++) {
-		// If something sets a patch to point to an uninitialised memory timbre, don't play anything
-		mt32ram.timbres[i].timbre.common.pmute = 0;
-	}
 
 	printDebug("Loading Control ROM");
-	if (!loadControlROM("MT32_CONTROL.ROM")) {
-		printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM");
-		report(ReportType_errorControlROM, &errno);
-		return false;
+	if (!loadControlROM("CM32L_CONTROL.ROM")) {
+		if (!loadControlROM("MT32_CONTROL.ROM")) {
+			printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM");
+			report(ReportType_errorControlROM, &errno);
+			return false;
+		}
 	}
 
+	// 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];
+
 	printDebug("Loading PCM ROM");
-	if (!loadPCMROM("MT32_PCM.ROM")) {
-		printDebug("Init Error - Missing MT32_PCM.ROM");
-		report(ReportType_errorPCMROM, &errno);
-		return false;
+	if (!loadPCMROM("CM32L_PCM.ROM")) {
+		if (!loadPCMROM("MT32_PCM.ROM")) {
+			printDebug("Init Error - Missing MT32_PCM.ROM");
+			report(ReportType_errorPCMROM, &errno);
+			return false;
+		}
 	}
 
-	partialManager = new PartialManager(this);
-
-	printDebug("Initialising PCM List");
-	initPCMList();
-
 	printDebug("Initialising Timbre Bank A");
-	initTimbres(0x8000, 0);
+	if (!initTimbres(controlROMMap->timbreAMap, controlROMMap->timbreAOffset, 0)) {
+		return false;
+	}
 
 	printDebug("Initialising Timbre Bank B");
-	initTimbres(0xC000, 64);
+	if (!initTimbres(controlROMMap->timbreBMap, controlROMMap->timbreBOffset, 64)) {
+		return false;
+	}
 
 	printDebug("Initialising Timbre Bank R");
-	initRhythmTimbres();
+	if (!initRhythmTimbres(controlROMMap->timbreRMap, controlROMMap->timbreRCount)) {
+		return false;
+	}
+
+	printDebug("Initialising Timbre Bank M");
+	// 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);
+
+	pcmWaves = new PCMWaveEntry[controlROMMap->pcmCount];
+
+	printDebug("Initialising PCM List");
+	initPCMList(controlROMMap->pcmTable, controlROMMap->pcmCount);
 
 	printDebug("Initialising Rhythm Temp");
-	memcpy(mt32ram.rhythmSettings, &controlROMData[0x741C], 344);
+	memcpy(mt32ram.rhythmSettings, &controlROMData[controlROMMap->rhythmSettings], controlROMMap->rhythmSettingsCount * 4);
 
 	printDebug("Initialising Patches");
 	for (Bit8u i = 0; i < 128; i++) {
@@ -401,16 +458,12 @@
 	}
 
 	printDebug("Initialising System");
-	//FIXME: Confirm that these are all correct
 	// The MT-32 manual claims that "Standard pitch" is 442Hz.
-	// I assume they mean this is the MT-32 default pitch, and not concert pitch,
-	// since the latter has been internationally defined as 440Hz for decades.
-	// Regardless, I'm setting the default masterTune to 440Hz
-	mt32ram.system.masterTune = 0x40;
-	mt32ram.system.reverbMode = 0;
-	mt32ram.system.reverbTime = 5;
-	mt32ram.system.reverbLevel = 3;
-	memcpy(mt32ram.system.reserveSettings, &controlROMData[0x57E5], 9);
+	mt32ram.system.masterTune = 0x40; // Confirmed on CM-64 as 0x4A, but SCUMM games use 0x40 and we don't want to initialise twice
+	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"
@@ -418,16 +471,16 @@
 		// The channel assignment is then {0, 1, 2, 3, 4, 5, 6, 7, 9}
 		mt32ram.system.chanAssign[i] = i + 1;
 	}
-	mt32ram.system.masterVol = 100;
+	mt32ram.system.masterVol = 100; // Confirmed
 	if (!refreshSystem())
 		return false;
 
 	for (int i = 0; i < 8; i++) {
 		mt32ram.patchSettings[i].outlevel = 80;
-		mt32ram.patchSettings[i].panpot = controlROMData[0x5800 + i];
+		mt32ram.patchSettings[i].panpot = controlROMData[controlROMMap->panSettings + i];
 		memset(mt32ram.patchSettings[i].dummyv, 0, sizeof(mt32ram.patchSettings[i].dummyv));
 		parts[i] = new Part(this, i);
-		parts[i]->setProgram(controlROMData[0x57EE + i]);
+		parts[i]->setProgram(controlROMData[controlROMMap->programSettings + i]);
 	}
 	parts[8] = new RhythmPart(this, 8);
 
@@ -467,7 +520,7 @@
 	if (!isOpen)
 		return;
 
-	TableInitialiser::freeNotes();
+	tables.freeNotes();
 	if (partialManager != NULL) {
 		delete partialManager;
 		partialManager = NULL;
@@ -488,14 +541,18 @@
 		delete myProp.baseDir;
 		myProp.baseDir = NULL;
 	}
+
+	delete[] pcmWaves;
+	delete[] pcmROMData;
 	isOpen = false;
 }
 
 void Synth::playMsg(Bit32u msg) {
-	unsigned char code = (unsigned char)((msg & 0xf0) >> 4);
-	unsigned char chan = (unsigned char)(msg & 0xf);
-	unsigned char note = (unsigned char)((msg & 0xff00) >> 8);
-	unsigned char velocity = (unsigned char)((msg & 0xff0000) >> 16);
+	// 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);
 	isEnabled = true;
 
 	//printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
@@ -534,7 +591,6 @@
 			parts[part]->setModulation(velocity);
 			break;
 		case 0x07:  // Set volume
-			//if (part!=3) return;
 			//printDebug("Volume set: %d", velocity);
 			parts[part]->setVolume(velocity);
 			break;
@@ -544,20 +600,26 @@
 			break;
 		case 0x0B:
 			//printDebug("Expression set: %d", velocity);
-			parts[part]->setVolume(velocity);
+			parts[part]->setExpression(velocity);
 			break;
-		case 0x40: // Hold pedal
+		case 0x40: // Hold (sustain) pedal
 			//printDebug("Hold pedal set: %d", velocity);
 			parts[part]->setHoldPedal(velocity>=64);
 			break;
 
 		case 0x79: // Reset all controllers
-			printDebug("Reset all controllers (NYI)");
+			//printDebug("Reset all controllers");
+			//FIXME: Check for accuracy against real thing
+			parts[part]->setVolume(100);
+			parts[part]->setExpression(127);
+			parts[part]->setPan(64);
+			parts[part]->setBend(0x2000);
+			parts[part]->setHoldPedal(false);
 			break;
 
 		case 0x7B: // All notes off
 			//printDebug("All notes off");
-			parts[part]->allStop();
+			parts[part]->allNotesOff();
 			break;
 
 		default:
@@ -583,53 +645,49 @@
 	//midiOutShortMsg(m_out, msg);
 }
 
-void Synth::playSysex(const Bit8u * sysex,Bit32u len) {
-	if (len < 3) {
+void Synth::playSysex(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)");
+	if (sysex[0] != 0xF0) {
+		printDebug("playSysex: Message lacks start-of-sysex (0xF0)");
 		return;
 	}
-	if (sysex[len - 1] != 0xf7) {
+	// 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, len - 2);
+	playSysexWithoutFraming(sysex + 1, endPos - 1);
 }
 
-void Synth::playSysexWithoutFraming(const Bit8u * sysex, Bit32u len) {
+void Synth::playSysexWithoutFraming(const Bit8u *sysex, Bit32u len) {
 	if (len < 4) {
 		printDebug("playSysexWithoutFraming: Message is too short (%d bytes)!", len);
 		return;
 	}
-	if (sysex[0] != 0x41) {
+	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] == 0x14) {
-		printDebug("playSysexWithoutFraming: Header is intended for Roland 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] != 0x16) {
-		printDebug("playSysexWithoutFraming: Header not intended for MT-32: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
+	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;
 	}
-	if (sysex[3] != 0x12) {
-		printDebug("playSysexWithoutFraming: Unsupported command %02x", sysex[3]);
+	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 + 4, len - 4);
+	playSysexWithoutHeader(sysex[1], sysex[3], sysex + 4, len - 4);
 }
 
-// MEMADDR() converts from sysex-padded, SYSEXMEMADDR converts to it
-// Roland provides documentation using the sysex-padded addresses, so we tend to use that int code and output
-#define MEMADDR(x) ((((x) & 0x7f0000) >> 2) | (((x) & 0x7f00) >> 1) | ((x) & 0x7f))
-#define SYSEXMEMADDR(x) ((((x) & 0x1FC000) << 2) | (((x) & 0x3F80) << 1) | ((x) & 0x7f))
-
-#define NUMTOUCHED(x,y) (((x) + sizeof(y) - 1) / sizeof(y))
-
-void Synth::playSysexWithoutHeader(unsigned char device, const Bit8u *sysex, Bit32u len) {
+void Synth::playSysexWithoutHeader(unsigned char device, unsigned char 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);
@@ -645,15 +703,47 @@
 		return;
 	}
 	len -= 1; // Exclude checksum
+	switch (command) {
+	case SYSEX_CMD_DT1:
+		writeSysex(device, sysex, len);
+		break;
+	case SYSEX_CMD_RQ1:
+		readSysex(device, sysex, len);
+		break;
+	default:
+		printDebug("playSysexWithoutFraming: Unsupported command %02x", command);
+		return;
+	}
+}
+
+void Synth::readSysex(unsigned char device, const Bit8u *sysex, Bit32u len) {
+}
+
+const MemoryRegion memoryRegions[8] = {
+	{MR_PatchTemp,  MT32EMU_MEMADDR(0x030000), sizeof(MemParams::PatchTemp), 9},
+	{MR_RhythmTemp, MT32EMU_MEMADDR(0x030110), sizeof(MemParams::RhythmTemp), 85},
+	{MR_TimbreTemp, MT32EMU_MEMADDR(0x040000), sizeof(TimbreParam), 8},
+	{MR_Patches,    MT32EMU_MEMADDR(0x050000), sizeof(PatchParam), 128},
+	{MR_Timbres,    MT32EMU_MEMADDR(0x080000), sizeof(MemParams::PaddedTimbre), 64 + 64 + 64 + 64},
+	{MR_System,     MT32EMU_MEMADDR(0x100000), sizeof(MemParams::SystemArea), 1},
+	{MR_Display,    MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1},
+	{MR_Reset,      MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1}
+};
+
+const int NUM_REGIONS = sizeof(memoryRegions) / sizeof(MemoryRegion);
+
+void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) {
 	Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]);
-	addr = MEMADDR(addr);
+	addr = MT32EMU_MEMADDR(addr);
 	sysex += 3;
 	len -= 3;
-	//printDebug("Sysex addr: 0x%06x", SYSEXMEMADDR(addr));
+	//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) {
-		printDebug("WRITE-CHANNEL: Channel %d temp area 0x%06x", device, SYSEXMEMADDR(addr));
-		if (/*addr >= MEMADDR(0x000000) && */addr < MEMADDR(0x010000)) {
+		printDebug("WRITE-CHANNEL: Channel %d temp area 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr));
+		if (/*addr >= MT32EMU_MEMADDR(0x000000) && */addr < MT32EMU_MEMADDR(0x010000)) {
 			int offset;
 			if (chantable[device] == -1) {
 				printDebug(" (Channel not mapped to a partial... 0 offset)");
@@ -665,10 +755,10 @@
 				offset = chantable[device] * sizeof(MemParams::PatchTemp);
 				printDebug(" (Setting extra offset to %d)", offset);
 			}
-			addr += MEMADDR(0x030000) + offset;
-		} else if (/*addr >= 0x010000 && */ addr < MEMADDR(0x020000)) {
-			addr += MEMADDR(0x030110) - MEMADDR(0x010000);
-		} else if (/*addr >= 0x020000 && */ addr < MEMADDR(0x030000)) {
+			addr += MT32EMU_MEMADDR(0x030000) + offset;
+		} else if (/*addr >= 0x010000 && */ addr < MT32EMU_MEMADDR(0x020000)) {
+			addr += MT32EMU_MEMADDR(0x030110) - MT32EMU_MEMADDR(0x010000);
+		} else if (/*addr >= 0x020000 && */ addr < MT32EMU_MEMADDR(0x030000)) {
 			int offset;
 			if (chantable[device] == -1) {
 				printDebug(" (Channel not mapped to a partial... 0 offset)");
@@ -680,53 +770,132 @@
 				offset = chantable[device] * sizeof(TimbreParam);
 				printDebug(" (Setting extra offset to %d)", offset);
 			}
-			addr += MEMADDR(0x040000) - MEMADDR(0x020000) + offset;
+			addr += MT32EMU_MEMADDR(0x040000) - MT32EMU_MEMADDR(0x020000) + offset;
 		} else {
-			printDebug("PlaySysexWithoutHeader: Invalid channel %d address 0x%06x", device, SYSEXMEMADDR(addr));
+			printDebug("PlaySysexWithoutHeader: Invalid channel %d address 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr));
 			return;
 		}
 	}
-	if (addr >= MEMADDR(0x030000) && addr < MEMADDR(0x030110)) {
-		int off = addr - MEMADDR(0x030000);
-		if (off + len > sizeof(mt32ram.patchSettings)) {
-			printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
-			return;
+
+	// Process device-global sysex (possibly converted from channel-specific sysex above)
+	for (;;) {
+		// Find the appropriate memory region
+		int regionNum;
+		const MemoryRegion *region = NULL; // Initialised to please compiler
+		for (regionNum = 0; regionNum < NUM_REGIONS; regionNum++) {
+			region = &memoryRegions[regionNum];
+			if (region->contains(addr)) {
+				writeMemoryRegion(region, addr, region->getClampedLen(addr, len), sysex);
+				break;
+			}
+		}
+		if (regionNum == NUM_REGIONS) {
+			printDebug("Sysex write to unrecognised address %06x, len %d", MT32EMU_SYSEXMEMADDR(addr), len);
+			break;
+		}
+		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) {
+	int regionNum;
+	const MemoryRegion *region = NULL;
+	for (regionNum = 0; regionNum < NUM_REGIONS; regionNum++) {
+		region = &memoryRegions[regionNum];
+		if (region->contains(addr)) {
+			readMemoryRegion(region, addr, len, data);
+			break;
+		}
+	}
+}
+
+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;
+
+	switch(region->type) {
+	case MR_PatchTemp:
+		for (m = 0; m < len; m++) 
+			data[m] = ((Bit8u *)&mt32ram.patchSettings[first])[off + m];
+		break;
+	case MR_RhythmTemp:
+		for (m = 0; m < len; m++) 
+			data[m] = ((Bit8u *)&mt32ram.rhythmSettings[first])[off + m];
+		break;
+	case MR_TimbreTemp:
+		for (m = 0; m < len; m++) 
+			data[m] = ((Bit8u *)&mt32ram.timbreSettings[first])[off + m];
+		break;
+	case MR_Patches:
+		for (m = 0; m < len; m++) 
+			data[m] = ((Bit8u *)&mt32ram.patches[first])[off + m];
+		break;
+	case MR_Timbres:
+		for (m = 0; m < len; m++) 
+			data[m] = ((Bit8u *)&mt32ram.timbres[first])[off + m];
+		break;
+	case MR_System:
+		for (m = 0; m < len; m++) 
+			data[m] = ((Bit8u *)&mt32ram.system)[m + off];
+		break;
+	default:
+		for (m = 0; m < len; m += 2) { 
+			data[m] = 0xff;
+			if (m + 1 < len) {
+				data[m+1] = (Bit8u)region->type;
+			}
+		}
+		// TODO: Don't care about the others ATM
+		break;
+	}
+
+}
+
+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:
+		for (unsigned int m = 0; m < len; m++) {
+			((Bit8u *)&mt32ram.patchSettings[first])[off + m] = data[m];
 		}
-		int firstPart = off / sizeof(MemParams::PatchTemp);
-		off %= sizeof(MemParams::PatchTemp);
-		for (unsigned int m = 0; m < len; m++)
-			((Bit8u *)&mt32ram.patchSettings[firstPart])[off + m] = sysex[m];
 		//printDebug("Patch temp: Patch %d, offset %x, len %d", off/16, off % 16, len);
 
-		int lastPart = firstPart + NUMTOUCHED(off + len, MemParams::PatchTemp) - 1;
-		for (int i = firstPart; i <= lastPart; i++) {
+		for (unsigned int i = first; i <= last; i++) {
 			int absTimbreNum = mt32ram.patchSettings[i].patch.timbreGroup * 64 + mt32ram.patchSettings[i].patch.timbreNum;
 			char timbreName[11];
 			memcpy(timbreName, mt32ram.timbres[absTimbreNum].timbre.common.name, 10);
 			timbreName[10] = 0;
-			printDebug("WRITE-PARTPATCH (%d-%d@%d..%d): %d; timbre=%d (%s)", firstPart, lastPart, off, off + len, i, absTimbreNum, timbreName);
+			printDebug("WRITE-PARTPATCH (%d-%d@%d..%d): %d; timbre=%d (%s), outlevel=%d", first, last, off, off + len, i, absTimbreNum, timbreName, mt32ram.patchSettings[i].outlevel);
 			if (parts[i] != NULL) {
-				if (i == firstPart && off > 2) {
-					printDebug(" (Not updating timbre, since those values weren't touched)");
-				} else {
-					// Not sure whether we should do this at all, really.
-					parts[i]->setTimbre(&mt32ram.timbres[parts[i]->getAbsTimbreNum()].timbre);
+				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) {
+						printDebug(" (Not updating timbre, since those values weren't touched)");
+					} else {
+						parts[i]->setTimbre(&mt32ram.timbres[parts[i]->getAbsTimbreNum()].timbre);
+					}
 				}
 				parts[i]->refresh();
 			}
 		}
-	} else if (addr >= MEMADDR(0x030110) && addr < MEMADDR(0x040000)) {
-		int off = addr - MEMADDR(0x030110);
-		if (off + len > sizeof(mt32ram.rhythmSettings)) {
-			printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
-			return;
-		}
-		int firstDrum = off / sizeof(MemParams::RhythmTemp);
-		off %= sizeof(MemParams::RhythmTemp);
+		break;
+	case MR_RhythmTemp:
 		for (unsigned int m = 0; m < len; m++)
-			((Bit8u *)&mt32ram.rhythmSettings[firstDrum])[off + m] = sysex[m];
-		int lastDrum = firstDrum + NUMTOUCHED(off + len, MemParams::RhythmTemp) - 1;
-		for (int i = firstDrum; i <= lastDrum; i++) {
+			((Bit8u *)&mt32ram.rhythmSettings[first])[off + m] = data[m];
+		for (unsigned int i = first; i <= last; i++) {
 			int timbreNum = mt32ram.rhythmSettings[i].timbre;
 			char timbreName[11];
 			if (timbreNum < 94) {
@@ -735,51 +904,36 @@
 			} else {
 				strcpy(timbreName, "[None]");
 			}
-			printDebug("WRITE-RHYTHM (%d-%d@%d..%d): %d; level=%02x, panpot=%02x, reverb=%02x, timbre=%d (%s)", firstDrum, lastDrum, off, off + len, i, mt32ram.rhythmSettings[i].outlevel, mt32ram.rhythmSettings[i].panpot, mt32ram.rhythmSettings[i].reverbSwitch, mt32ram.rhythmSettings[i].timbre, timbreName);
+			printDebug("WRITE-RHYTHM (%d-%d@%d..%d): %d; level=%02x, panpot=%02x, reverb=%02x, timbre=%d (%s)", first, last, off, off + len, i, mt32ram.rhythmSettings[i].outlevel, mt32ram.rhythmSettings[i].panpot, mt32ram.rhythmSettings[i].reverbSwitch, mt32ram.rhythmSettings[i].timbre, timbreName);
 		}
 		if (parts[8] != NULL) {
 			parts[8]->refresh();
 		}
-	} else if (addr >= MEMADDR(0x040000) && addr < MEMADDR(0x050000)) {
-		int off = addr - MEMADDR(0x040000);
-		if (off + len > sizeof(mt32ram.timbreSettings)) {
-			printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
-			return;
-		}
-		int firstPart = off / sizeof(TimbreParam);
-		off %= sizeof(TimbreParam);
+		break;
+	case MR_TimbreTemp:
 		for (unsigned int m = 0; m < len; m++)
-			((Bit8u *)&mt32ram.timbreSettings[firstPart])[off + m] = sysex[m];
-		int lastPart = firstPart + NUMTOUCHED(off + len, TimbreParam) - 1;
-		for (int i = firstPart; i <= lastPart; i++) {
+			((Bit8u *)&mt32ram.timbreSettings[first])[off + m] = data[m];
+		for (unsigned int i = first; i <= last; i++) {
 			char instrumentName[11];
 			memcpy(instrumentName, mt32ram.timbreSettings[i].common.name, 10);
 			instrumentName[10] = 0;
-			printDebug("WRITE-PARTTIMBRE (%d-%d@%d..%d): timbre=%d (%s)", firstPart, lastPart, off, off + len, i, instrumentName);
+			printDebug("WRITE-PARTTIMBRE (%d-%d@%d..%d): timbre=%d (%s)", first, last, off, off + len, i, instrumentName);
 			if (parts[i] != NULL) {
 				parts[i]->refresh();
 			}
 		}
-	}
-	else if (addr >= MEMADDR(0x050000) && addr < MEMADDR(0x060000)) {
-		int off = addr - MEMADDR(0x050000);
-		if (off + len > sizeof(mt32ram.patches)) {
-			printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
-			return;
-		}
-		int firstPatch = off / sizeof(PatchParam);
-		off %= sizeof(PatchParam);
+		break;
+	case MR_Patches:
 		for (unsigned int m = 0; m < len; m++)
-			((Bit8u *)&mt32ram.patches[firstPatch])[off + m] = sysex[m];
-		int lastPatch = firstPatch + NUMTOUCHED(off + len, PatchParam) - 1;
-		for (int i = firstPatch; i <= lastPatch; i++) {
+			((Bit8u *)&mt32ram.patches[first])[off + m] = data[m];
+		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", firstPatch, lastPatch, off, off + len, i, patchAbsTimbreNum, instrumentName, n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]);
+			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]);
 			// FIXME:KG: The below is definitely dodgy. We just guess that this is the patch that the part was using
 			// based on a timbre match (but many patches could have the same timbre!)
 			// If this refresh is really correct, we should store the patch number in use by each part.
@@ -795,25 +949,18 @@
 			}
 			*/
 		}
-	} else if (addr >= MEMADDR(0x080000) && addr < MEMADDR(0x090000)) {
+		break;
+	case MR_Timbres:
 		// Timbres
-		int off = addr - MEMADDR(0x080000);
-		if (off + len > sizeof(MemParams::PaddedTimbre) * 64) {
-			// You can only write to one group at a time
-			printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
-			return;
-		}
-		unsigned int firstTimbre = off / sizeof (MemParams::PaddedTimbre);
-		off %= sizeof (MemParams::PaddedTimbre);
-		firstTimbre += 128;
+		first += 128;
+		last += 128;
 		for (unsigned int m = 0; m < len; m++)
-			((Bit8u *)&mt32ram.timbres[firstTimbre])[off + m] = sysex[m];
-		unsigned int lastTimbre = firstTimbre + NUMTOUCHED(len + off, MemParams::PaddedTimbre) - 1;
-		for (unsigned int i = firstTimbre; i <= lastTimbre; i++) {
+			((Bit8u *)&mt32ram.timbres[first])[off + m] = data[m];
+		for (unsigned int i = first; i <= last; i++) {
 			char instrumentName[11];
 			memcpy(instrumentName, mt32ram.timbres[i].timbre.common.name, 10);
 			instrumentName[10] = 0;
-			printDebug("WRITE-TIMBRE (%d-%d@%d..%d): %d; name=\"%s\"", firstTimbre, lastTimbre, off, off + len, i, instrumentName);
+			printDebug("WRITE-TIMBRE (%d-%d@%d..%d): %d; name=\"%s\"", first, last, off, off + len, i, instrumentName);
 			// 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++) {
@@ -822,31 +969,25 @@
 				}
 			}
 		}
-	} else if (addr >= MEMADDR(0x100000) && addr < MEMADDR(0x200000)) {
-		int off = addr - MEMADDR(0x100000);
-		if (off + len > sizeof(mt32ram.system)) {
-			printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
-			return;
-		}
+		break;
+	case MR_System:
 		for (unsigned int m = 0; m < len; m++)
-			((Bit8u *)&mt32ram.system)[m + off] = sysex[m];
+			((Bit8u *)&mt32ram.system)[m + off] = data[m];
 
 		report(ReportType_devReconfig, NULL);
 
 		printDebug("WRITE-SYSTEM:");
 		refreshSystem();
-	} else if (addr == MEMADDR(0x200000)) {
+		break;
+	case MR_Display:
 		char buf[MAX_SYSEX_SIZE];
-		if (len > MAX_SYSEX_SIZE - 1) {
-			printDebug("WRITE-LCD sysex length (%d) exceeded MAX_SYSEX_SIZE (%d) - 1; truncating", len, MAX_SYSEX_SIZE);
-			len = MAX_SYSEX_SIZE - 1;
-		}
-		memcpy(&buf, &sysex[0], len);
+		memcpy(&buf, &data[0], len);
 		buf[len] = 0;
 		printDebug("WRITE-LCD: %s", buf);
 		report(ReportType_lcdMessage, buf);
-	} else if (addr >= MEMADDR(0x7f0000)) {
-		printDebug("Reset");
+		break;
+	case MR_Reset:
+		printDebug("RESET");
 		report(ReportType_devReset, NULL);
 		partialManager->deactivateAll();
 		mt32ram = mt32default;
@@ -854,18 +995,17 @@
 			parts[i]->refresh();
 		}
 		isEnabled = false;
-	} else {
-		printDebug("Sysex write to unrecognised address %06x", SYSEXMEMADDR(addr));
+		break;
 	}
 }
 
 bool Synth::refreshSystem() {
-	memset(chantable,-1,sizeof(chantable));
+	memset(chantable, -1, sizeof(chantable));
 
 	for (unsigned int i = 0; i < 9; i++) {
 		//LOG(LOG_MISC|LOG_ERROR,"Part %d set to MIDI channel %d",i,mt32ram.system.chanAssign[i]);
 		if (mt32ram.system.chanAssign[i] == 16 && parts[i] != NULL) {
-			parts[i]->allStop();
+			parts[i]->allSoundOff();
 		} else {
 			chantable[(int)mt32ram.system.chanAssign[i]] = (char)i;
 		}
@@ -894,8 +1034,8 @@
 	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]);
 	printDebug(" Master volume: %d", mt32ram.system.masterVol);
-	masterVolume = (Bit16u)(mt32ram.system.masterVol * 327);
-	if (!TableInitialiser::initMT32Tables(this, PCMList, (float)myProp.sampleRate, masterTune)) {
+	masterVolume = (Bit16u)(mt32ram.system.masterVol * 32767 / 100);
+	if (!tables.init(this, pcmWaves, (float)myProp.sampleRate, masterTune)) {
 		report(ReportType_errorSampleRate, NULL);
 		return false;
 	}
@@ -984,38 +1124,31 @@
 	}
 }
 
-void Synth::doRender(Bit16s * stream,Bit32u len) {
-	Bit32u m;
-
+void Synth::doRender(Bit16s *stream, Bit32u len) {
 	partialManager->ageAll();
 
 	if (myProp.useReverb) {
-		bool hasOutput = false;
 		for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
 			if (partialManager->shouldReverb(i)) {
 				if (partialManager->produceOutput(i, &tmpBuffer[0], len)) {
 					ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
-					hasOutput = true;
 				}
 			}
 		}
-		// No point in doing reverb on a mute buffer...
-		if (hasOutput) {
-			m=0;
-			for (unsigned int i = 0; i < len; i++) {
-				sndbufl[i] = (float)stream[m] / 32767.0f;
-				m++;
-				sndbufr[i] = (float)stream[m] / 32767.0f;
-				m++;
-			}
-			reverbModel->processreplace(sndbufl, sndbufr, outbufl, outbufr, len, 1);
-			m=0;
-			for (unsigned int i = 0; i < len; i++) {
-				stream[m] = (Bit16s)(outbufl[i] * 32767.0f);
-				m++;
-				stream[m] = (Bit16s)(outbufr[i] * 32767.0f);
-				m++;
-			}
+		Bit32u m = 0;
+		for (unsigned int i = 0; i < len; i++) {
+			sndbufl[i] = (float)stream[m] / 32767.0f;
+			m++;
+			sndbufr[i] = (float)stream[m] / 32767.0f;
+			m++;
+		}
+		reverbModel->processreplace(sndbufl, sndbufr, outbufl, outbufr, len, 1);
+		m=0;
+		for (unsigned int i = 0; i < len; i++) {
+			stream[m] = (Bit16s)(outbufl[i] * 32767.0f);
+			m++;
+			stream[m] = (Bit16s)(outbufr[i] * 32767.0f);
+			m++;
 		}
 		for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
 			if (!partialManager->shouldReverb(i)) {
@@ -1045,4 +1178,14 @@
 #endif
 }
 
+const Partial *Synth::getPartial(unsigned int partialNum) const {
+	return partialManager->getPartial(partialNum);
+}
+
+const Part *Synth::getPart(unsigned int partNum) const {
+	if (partNum > 8)
+		return NULL;
+	return parts[partNum];
+}
+
 }

Index: synth.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/softsynth/mt32/synth.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- synth.h	25 Dec 2004 18:34:44 -0000	1.1
+++ synth.h	20 Mar 2005 16:24:55 -0000	1.2
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2004 Various contributors
+/* Copyright (c) 2003-2005 Various contributors
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -28,10 +28,6 @@
 
 namespace MT32Emu {
 
-const int ROMSIZE = 512 * 1024;
-const int PCMSIZE = ROMSIZE / 2;
-const int GRAN = 512;
-
 class File;
 class TableInitialiser;
 class Partial;
@@ -104,28 +100,118 @@
 // callback routine, no status is reported.
 bool RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack);
 
-typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr, int revLevel);
+typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr);
+
+const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41;
+
+const Bit8u SYSEX_MDL_MT32 = 0x16;
+const Bit8u SYSEX_MDL_D50 = 0x14;
+
+const Bit8u SYSEX_CMD_RQ1 = 0x11; // Request data #1
+const Bit8u SYSEX_CMD_DT1 = 0x12; // Data set 1
+const Bit8u SYSEX_CMD_WSD = 0x40; // Want to send data
+const Bit8u SYSEX_CMD_RQD = 0x41; // Request data
+const Bit8u SYSEX_CMD_DAT = 0x42; // Data set
+const Bit8u SYSEX_CMD_ACK = 0x43; // Acknowledge
+const Bit8u SYSEX_CMD_EOD = 0x45; // End of data
+const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error
+const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection
+
+const unsigned int CONTROL_ROM_SIZE = 64 * 1024;
+
+struct ControlROMPCMStruct
+{
+	Bit8u pos;
+	Bit8u len;
+	Bit8u pitchLSB;
+	Bit8u pitchMSB;
+};
+
+struct ControlROMMap {
+	Bit16u idPos;
+	Bit16u idLen;
+	const char *idBytes;
+	Bit16u pcmTable;
+	Bit16u pcmCount;
+	Bit16u timbreAMap;
+	Bit16u timbreAOffset;
+	Bit16u timbreBMap;
+	Bit16u timbreBOffset;
+	Bit16u timbreRMap;
+	Bit16u timbreRCount;
+	Bit16u rhythmSettings;
+	Bit16u rhythmSettingsCount;
+	Bit16u reserveSettings;
+	Bit16u panSettings;
+	Bit16u programSettings;
+};
+
+enum MemoryRegionType {
+	MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset
+};
+
+class MemoryRegion {
+public:
+	MemoryRegionType type;
+	Bit32u startAddr, entrySize, entries;
+
+	int lastTouched(Bit32u addr, Bit32u len) const {
+		return (offset(addr) + len - 1) / entrySize;
+	}
+	int firstTouchedOffset(Bit32u addr) const {
+		return offset(addr) % entrySize;
+	}
+	int firstTouched(Bit32u addr) const {
+		return offset(addr) / entrySize;
+	}
+	Bit32u regionEnd() const {
+		return startAddr + entrySize * entries;
+	}
+	bool contains(Bit32u addr) const {
+		return addr >= startAddr && addr < regionEnd();
+	}
+	int offset(Bit32u addr) const {
+		return addr - startAddr;
+	}
+	Bit32u getClampedLen(Bit32u addr, Bit32u len) const {
+		if (addr + len > regionEnd())
+			return regionEnd() - addr;
+		return len;
+	}
+	Bit32u next(Bit32u addr, Bit32u len) const {
+		if (addr + len > regionEnd()) {
+			return regionEnd() - addr;
+		}
+		return 0;
+	}
+};
+
 
 class Synth {
 friend class Part;
 friend class RhythmPart;
 friend class Partial;
-friend class TableInitialiser;
+friend class Tables;
 private:
 	bool isEnabled;
 
 	iir_filter_type iirFilter;
 
-	PCMWaveEntry PCMList[128];
+	PCMWaveEntry *pcmWaves; // Array
+
+	const ControlROMMap *controlROMMap;
+	Bit8u controlROMData[CONTROL_ROM_SIZE];
+	Bit16s *pcmROMData;
+	int pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM
 
-	Bit8u controlROMData[64 * 1024];
-	Bit16s romfile[PCMSIZE + GRAN];
 	Bit8s chantable[32];
 
 	#if MT32EMU_MONITOR_PARTIALS == 1
 	static Bit32s samplepos = 0;
 	#endif
 
+	Tables tables;
+
 	MemParams mt32ram, mt32default;
 
 	revmodel *reverbModel;
@@ -149,19 +235,23 @@
 	bool loadPreset(File *file);
 	void initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel);
 	void doRender(Bit16s * stream, Bit32u len);
-	void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
-	void playSysexWithoutHeader(unsigned char channel, const Bit8u *sysex, Bit32u len);
+
+	void playAddressedSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
+	void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
+	void writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data);
+	void readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data);
 
 	bool loadControlROM(const char *filename);
 	bool loadPCMROM(const char *filename);
 	bool dumpTimbre(File *file, const TimbreParam *timbre, Bit32u addr);
 	int dumpTimbres(const char *filename, int start, int len);
 
-	void initPCMList();
-	void initRhythmTimbres();
-	void initTimbres(Bit16u mapAddress, int startTimbre);
-	void initRhythmTimbre(int drumNum, const Bit8u *mem);
+	bool initPCMList(Bit16u mapAddress, Bit16u count);
+	bool initRhythmTimbres(Bit16u mapAddress, Bit16u count);
+	bool initTimbres(Bit16u mapAddress, Bit16u offset, int startTimbre);
+	bool initRhythmTimbre(int drumNum, const Bit8u *mem, unsigned int memLen);
 	bool refreshSystem();
+
 protected:
 	int report(ReportType type, const void *reportData);
 	File *openFile(const char *filename, File::OpenMode mode);
@@ -183,16 +273,26 @@
 
 	// Sends a 4-byte MIDI message to the MT-32 for immediate playback
 	void playMsg(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 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);
 
 	// This callback routine is used to have the MT-32 generate samples to the specified
 	// output stream.  The length is in whole samples, not bytes. (I.E. in 16-bit stereo,
 	// one sample is 4 bytes)
 	void render(Bit16s * stream, Bit32u len);
+
+	const Partial *getPartial(unsigned int partialNum) const;
+
+	void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
+
+	// partNum should be 0..7 for Part 1..8, or 8 for Rhythm
+	const Part *getPart(unsigned int partNum) const;
 };
 
 }

Index: tables.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/softsynth/mt32/tables.cpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- tables.cpp	25 Dec 2004 18:34:44 -0000	1.1
+++ tables.cpp	20 Mar 2005 16:24:55 -0000	1.2
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2004 Various contributors
+/* Copyright (c) 2003-2005 Various contributors
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -30,8 +30,8 @@
 namespace MT32Emu {
 
 //Amplitude time velocity follow exponential coefficients
-const double tvcatconst[5] = {0.0, 0.002791309, 0.005942882, 0.012652792, 0.026938637};
-const double tvcatmult[5] = {1.0, 1.072662811, 1.169129367, 1.288579123, 1.229630539};
+static const double tvcatconst[5] = {0.0, 0.002791309, 0.005942882, 0.012652792, 0.026938637};
+static const double tvcatmult[5] = {1.0, 1.072662811, 1.169129367, 1.288579123, 1.229630539};
 
 // These are division constants for the TVF depth key follow
 static const Bit32u depexp[5] = {3000, 950, 485, 255, 138};
@@ -40,36 +40,6 @@
 static const double tkcatconst[5] = {0.0, 0.005853144, 0.011148054, 0.019086143, 0.043333215};
 static const double tkcatmult[5] = {1.0, 1.058245688, 1.048488989, 1.016049301, 1.097538067};
 
-static float initialisedSampleRate = 0.0f;
-static float initialisedMasterTune = 0.0f;
-
-Bit16s smallnoise[MAX_SAMPLE_OUTPUT];
-
-// Some optimization stuff
-Bit32s keytable[217];
-Bit16s sintable[65536];
-Bit32u lfotable[101];
-Bit32s penvtable[16][101];
-Bit32s filveltable[128][101];
-Bit32s veltkeytable[5][128];
-Bit32s pulsetable[101];
-Bit32s pulseoffset[101];
-Bit32s ampbiastable[13][128];
-Bit32s fbiastable[15][128];
-float filtcoeff[FILTERGRAN][31][8];
-Bit32s finetable[201];
-Bit32u lfoptable[101][101];
-Bit32s ampveltable[128][64];
-Bit32s pwveltable[15][128];
-Bit32s envtimetable[101];
-Bit32s decaytimetable[101];
-Bit32s lasttimetable[101];
-Bit32s voltable[128];
-float ResonFactor[31];
-float ResonInv[31];
-
-NoteLookup noteLookups[NUM_NOTES];
-
 // Begin filter stuff
 
 // Pre-warp the coefficients of a numerator or denominator.
@@ -169,68 +139,66 @@
 	icoeff[0] = (float)k;
 }
 
-static void initFiltCoeff(float samplerate) {
+void Tables::initFiltCoeff(float samplerate) {
 	for (int j = 0; j < FILTERGRAN; j++) {
 		for (int res = 0; res < 31; res++) {
-			float tres = ResonFactor[res];
-			initFilter((float)samplerate, (((float)(j+1.0)/FILTERGRAN)) * ((float)samplerate/2), filtcoeff[j][res], tres);
+			float tres = resonanceFactor[res];
+			initFilter((float)samplerate, (((float)(j+1.0)/FILTERGRAN)) * ((float)samplerate/2), filtCoeff[j][res], tres);
 		}
 	}
 }
 
-static void initEnvelopes(float samplerate) {
+void Tables::initEnvelopes(float samplerate) {
 	for (int lf = 0; lf <= 100; lf++) {
 		float elf = (float)lf;
 
 		// General envelope
-		float logtime = elf * 0.088362939f;
-		envtimetable[lf] = (int)((exp(logtime)/312.12) * (float)samplerate);
-
-		// Decay envelope -- shorter for some reason
-		// This is also the timing for the envelope right before the
-		// amp and filter envelope sustains
-
-		lasttimetable[lf] = decaytimetable[lf] = (int)((exp(logtime)/(312.12*2)) * (float)samplerate);
-		//lasttimetable[lf] = (int)((exp(logtime)/(312.12*6)) * (float)samplerate);
+		// This formula fits observation of the CM-32L by +/- 0.03s or so for the second time value in the filter,
+		// when all other times were 0 and all levels were 100. Note that variations occur depending on the level
+		// delta of the section, which we're not fully emulating.
+		float seconds = powf(2.0f, (elf / 8.0f) + 7.0f) / 32768.0f;
+		int samples = (int)(seconds * samplerate);
+		envTime[lf] = samples;
 
-		float mv = (float)lf / 100.0f;
-		float pt = mv - 0.5f;
-		if (pt < 0)
-			pt = 0;
+		// Cap on envelope times depending on the level delta
+		if(elf == 0) {
+			envDeltaMaxTime[lf] = 63;
+		} else {
+			float cap = 11 * log(elf) + 64;
+			if(cap > 100.0f) {
+				cap = 100.0f;
+			}
+			envDeltaMaxTime[lf] = (int)cap;
+		}
+		
 
-		pulsetable[lf] = (int)(pt * 215.04f) + 128;
+		// This (approximately) represents the time durations when the target level is 0.
+		// Not sure why this is a special case, but it's seen to be from the real thing.
+		seconds = powf(2, (elf / 8.0f) + 6) / 32768.0f;
+		envDecayTime[lf]  = (int)(seconds * samplerate);
 
 		// I am certain of this:  Verified by hand LFO log
-		lfotable[lf] = (Bit32u)(((float)samplerate) / (powf(1.088883372f,(float)lf) * 0.021236044f));
-
-		//LOG(LOG_ERROR|LOG_MISC,"lf %d = lfo %d pulsetable %d", lf, lfotable[lf], pulsetable[lf]);
+		lfoPeriod[lf] = (Bit32u)(((float)samplerate) / (powf(1.088883372f, (float)lf) * 0.021236044f));
 	}
 }
 
-void TableInitialiser::initMT32ConstantTables(Synth *synth) {
-	if (initialisedSampleRate > 0.0f) {
-		return;
-	}
+void Tables::initMT32ConstantTables(Synth *synth) {
 	int lf;
 	synth->printDebug("Initialising Pitch Tables");
 	for (lf = -108; lf <= 108; lf++) {
-		keytable[lf + 108] = (int)(256 * powf(2.0f, (float)(lf / 24.0f)));
+		tvfKeyfollowMult[lf + 108] = (int)(256 * powf(2.0f, (float)(lf / 24.0f)));
 		//synth->printDebug("KT %d = %d", f, keytable[f+108]);
 	}
 
-	int res;
-	float fres;
-	for (res = 0; res < 31; res++) {
-		fres = (float)res / 30.0f;
-		ResonFactor[res] = (powf(2.0f, logf(powf(fres, 16.0f))) * 2.5f) + 1.0f;
-		ResonInv[res] = 1 / ResonFactor[res];
+	for (int res = 0; res < 31; res++) {
+		resonanceFactor[res] = powf((float)res / 30.0f, 5.0f) + 1.0f;
 	}
 
 	int period = 65536;
 
 	for (int ang = 0; ang < period; ang++) {
 		int halfang = (period / 2);
-		int angval  = ang % halfang;
+		int angval = ang % halfang;
 		float tval = (((float)angval / (float)halfang) - 0.5f) * 2;
 		if (ang >= halfang)
 			tval = -tval;
@@ -242,14 +210,14 @@
 	for (velt = 0; velt < 128; velt++) {
 		for (dep = 0; dep < 5; dep++) {
 			if (dep > 0) {
-				float ff = (float)(exp(3.5f*tvcatconst[dep] * (59.0f-(float)velt)) * tvcatmult[dep]);
+				float ff = (float)(exp(3.5f * tvcatconst[dep] * (59.0f - (float)velt)) * tvcatmult[dep]);
 				tempdep = 256.0f * ff;
-				veltkeytable[dep][velt] = (int)tempdep;
+				envTimeVelfollowMult[dep][velt] = (int)tempdep;
 				//if ((velt % 16) == 0) {
 				//	synth->printDebug("Key %d, depth %d, factor %d", velt, dep, (int)tempdep);
 				//}
 			} else
-				veltkeytable[dep][velt] = 256;
+				envTimeVelfollowMult[dep][velt] = 256;
 		}
 
 		for (dep = -7; dep < 8; dep++) {
@@ -257,7 +225,7 @@
 			fldep = powf(fldep,2.5f);
 			if (dep < 0)
 				fldep = fldep * -1.0f;
-			pwveltable[dep+7][velt] = Bit32s((fldep * (float)velt * 100) / 128.0);
+			pwVelfollowAdd[dep+7][velt] = Bit32s((fldep * (float)velt * 100) / 128.0);
 		}
 	}
 
@@ -269,46 +237,54 @@
 			float fbase;
 
 			if (velt > 64)
-				filveltable[velt][dep] = (int)(flogdep * 256.0);
+				synth->tables.tvfVelfollowMult[velt][dep] = (int)(flogdep * 256.0);
 			else {
 				//lff = 1 - (pow(((128.0 - (float)lf) / 64.0),.25) * ((float)velt / 96));
 				fbase = 1 - (powf(((float)dep / 100.0f),.25f) * ((float)(64-velt) / 96.0f));
-				filveltable[velt][dep] = (int)(fbase * 256.0);
+				synth->tables.tvfVelfollowMult[velt][dep] = (int)(fbase * 256.0);
 			}
 			//synth->printDebug("Filvel dep %d velt %d = %x", dep, velt, filveltable[velt][dep]);
 		}
 	}
 
-	for (lf = 0; lf <= 200; lf++) {
-		//FIXME:KG: I'm fairly sure this is wrong... lf=100 should yield no fine-tuning (4096)?
-		finetable[lf] = (int)((powf(2.0f, (((float)lf / 200.0f) - 1.0f) / 12.0f)) * 4096.0f);
-
-		// FIXME:KG: This now gives a range of -1 .. 1 semitone. Should be correct, but check
-		//finetable[lf] = (int)((powf(2.0f, (((float)lf / 100.0f) - 1.0f) / 12.0f)) * 4096.0f);
-	}
-
-	float lff;
 	for (lf = 0; lf < 128; lf++) {
-		for (velt = 0; velt < 64; velt++) {
-			lff = 1 - (powf(((128.0f - (float)lf) / 64.0f), 0.25f) * ((float)velt / 96));
-			ampveltable[lf][velt] = (int)(lff * 256.0);
-			//synth->printDebug("Ampveltable: %d, %d = %d", lf, velt, ampveltable[lf][velt]);
+		float veloFract = lf / 127.0f;
+		for (int velsens = 0; velsens <= 100; velsens++) {
+			float sensFract = (velsens - 50) / 50.0f;
+			if (velsens < 50) {
+				tvaVelfollowMult[lf][velsens] = FIXEDPOINT_MAKE(1.0f / powf(2.0f, veloFract * -sensFract * 127.0f / 20.0f), 8);
+			} else {
+				tvaVelfollowMult[lf][velsens] = FIXEDPOINT_MAKE(1.0f / powf(2.0f, (1.0f - veloFract) * sensFract * 127.0f / 20.0f), 8);
+			}
 		}
 	}
 
-	for (lf = 0; lf < 128; lf++) {
-		// Converts MIDI velocity to volume.
-		voltable[lf] = FIXEDPOINT_MAKE(powf((float)lf / 127.0f, FLOAT_LN), 7);
+	for (lf = 0; lf <= 100; lf++) {
+		// Converts the 0-100 range used by the MT-32 to volume multiplier
+		volumeMult[lf] = FIXEDPOINT_MAKE(powf((float)lf / 100.0f, FLOAT_LN), 7);
 	}
+
+	for (lf = 0; lf <= 100; lf++) {
+		float mv = lf / 100.0f;
+		float pt = mv - 0.5f;
+		if (pt < 0)
+			pt = 0;
+
+		// Original (CC version)
+		//pwFactor[lf] = (int)(pt * 210.04f) + 128;
+
+		// Approximation from sample comparison
+		pwFactor[lf] = (int)(pt * 179.0f) + 128;
+	}
+
 	for (unsigned int i = 0; i < MAX_SAMPLE_OUTPUT; i++) {
 		int myRand;
 		myRand = rand();
-		int origRand = myRand;
-		//myRand = ((myRand - 16383) * WGAMP) >> 16;
+		//myRand = ((myRand - 16383) * 7168) >> 16;
 		// This one is slower but works with all values of RAND_MAX
-		myRand = (int)((origRand - RAND_MAX / 2) / (float)RAND_MAX * (WGAMP / 2));
+		myRand = (int)((myRand - RAND_MAX / 2) / (float)RAND_MAX * (7168 / 2));
 		//FIXME:KG: Original ultimately set the lowest two bits to 0, for no obvious reason
-		smallnoise[i] = (Bit16s)myRand;
+		noiseBuf[i] = (Bit16s)myRand;
 	}
 
 	float tdist;
@@ -360,10 +336,10 @@
 					finalval = 4096.0f * powf(2, lfp);
 				pval = (int)finalval;
 
-				penvtable[lf][depat] = pval;
+				pitchEnvVal[lf][depat] = pval;
 				//synth->printDebug("lf %d depat %d pval %d tlf %f lfp %f", lf,depat,pval, tlf, lfp);
 			} else {
-				penvtable[lf][depat] = 4096;
+				pitchEnvVal[lf][depat] = 4096;
 				//synth->printDebug("lf %d depat %d pval 4096", lf, depat);
 			}
 		}
@@ -379,7 +355,7 @@
 
 			pval = (int)finalval;
 
-			lfoptable[lf][depat] = pval;
+			lfoShift[lf][depat] = pval;
 
 			//synth->printDebug("lf %d depat %d pval %x", lf,depat,pval);
 		}
@@ -391,15 +367,27 @@
 			if (lf == 0) {
 				amplog = 0;
 				dval = 1;
-				ampbiastable[lf][distval] = 256;
+				tvaBiasMult[lf][distval] = 256;
 			} else {
+				/*
 				amplog = powf(1.431817011f, (float)lf) / FLOAT_PI;
 				dval = ((128.0f - (float)distval) / 128.0f);
 				amplog = expf(amplog);
 				dval = powf(amplog, dval) / amplog;
-				ampbiastable[lf][distval] = (int)(dval * 256.0);
+				tvaBiasMult[lf][distval] = (int)(dval * 256.0);
+				*/
+				// Lets assume for a second it's linear
+
+				// Distance of full volume reduction
+				amplog = (float)(12.0f / (float)lf) * 24.0f;
+				if(distval > amplog) {
+					tvaBiasMult[lf][distval] = 0;
+				} else {
+					dval = (amplog - (float)distval) / amplog;
+					tvaBiasMult[lf][distval] = (int)(dval * 256.0f);
+				}
 			}
-			//synth->printDebug("Ampbias lf %d distval %d = %f (%x) %f", lf, distval, dval, ampbiastable[lf][distval],amplog);
+			//synth->printDebug("Ampbias lf %d distval %d = %f (%x) %f", lf, distval, dval, tvaBiasMult[lf][distval],amplog);
 		}
 	}
 
@@ -410,7 +398,7 @@
 			if (lf == 7) {
 				amplog = 0;
 				dval = 1;
-				fbiastable[lf][distval] = 256;
+				tvfBiasMult[lf][distval] = 256;
 			} else {
 				//amplog = pow(1.431817011, filval) / FLOAT_PI;
 				amplog = powf(1.531817011f, filval) / FLOAT_PI;
@@ -418,30 +406,30 @@
 				amplog = expf(amplog);
 				dval = powf(amplog,dval)/amplog;
 				if (lf < 8) {
-					fbiastable[lf][distval] = (int)(dval * 256.0f);
+					tvfBiasMult[lf][distval] = (int)(dval * 256.0f);
 				} else {
 					dval = powf(dval, 0.3333333f);
 					if (dval < 0.01f)
 						dval = 0.01f;
 					dval = 1 / dval;
-					fbiastable[lf][distval] = (int)(dval * 256.0f);
+					tvfBiasMult[lf][distval] = (int)(dval * 256.0f);
 				}
 			}
-			//synth->printDebug("Fbias lf %d distval %d = %f (%x) %f", lf, distval, dval, fbiastable[lf][distval],amplog);
+			//synth->printDebug("Fbias lf %d distval %d = %f (%x) %f", lf, distval, dval, tvfBiasMult[lf][distval],amplog);
 		}
 	}
 }
 
 // Per-note table initialisation follows
 
-static void initSaw(NoteLookup *noteLookup, Bit32s div) {
+static void initSaw(NoteLookup *noteLookup, Bit32s div2) {
+	int tmpdiv = div2 << 16;
 	for (int rsaw = 0; rsaw <= 100; rsaw++) {
 		float fsaw;
 		if (rsaw < 50)
 			fsaw = 50.0f;
 		else
 			fsaw = (float)rsaw;
-		int tmpdiv = div << 17;
 
 		//(66 - (((A8 - 50) / 50) ^ 0.63) * 50) / 132
 		float sawfact = (66.0f - (powf((fsaw - 50.0f) / 50.0f, 0.63f) * 50.0f)) / 132.0f;
@@ -450,11 +438,11 @@
 	}
 }
 
-static void initDep(NoteLookup *noteLookup, float f) {
+static void initDep(KeyLookup *keyLookup, float f) {
 	for (int dep = 0; dep < 5; dep++) {
 		if (dep == 0) {
-			noteLookup->fildepTable[dep] = 256;
-			noteLookup->timekeyTable[dep] = 256;
+			keyLookup->envDepthMult[dep] = 256;
+			keyLookup->envTimeMult[dep] = 256;
 		} else {
 			float depfac = 3000.0f;
 			float ff, tempdep;
@@ -462,20 +450,32 @@
 
 			ff = (f - (float)MIDDLEC) / depfac;
 			tempdep = powf(2, ff) * 256.0f;
-			noteLookup->fildepTable[dep] = (int)tempdep;
+			keyLookup->envDepthMult[dep] = (int)tempdep;
 
 			ff = (float)(exp(tkcatconst[dep] * ((float)MIDDLEC - f)) * tkcatmult[dep]);
-			noteLookup->timekeyTable[dep] = (int)(ff * 256.0f);
+			keyLookup->envTimeMult[dep] = (int)(ff * 256.0f);
 		}
 	}
 	//synth->printDebug("F %f d1 %x d2 %x d3 %x d4 %x d5 %x", f, noteLookup->fildepTable[0], noteLookup->fildepTable[1], noteLookup->fildepTable[2], noteLookup->fildepTable[3], noteLookup->fildepTable[4]);
 }
 
-File *TableInitialiser::initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div, File *file) {
-	int iDiv = (int)div;
-	noteLookup->waveformSize[0] = iDiv << 2;
-	noteLookup->waveformSize[1] = iDiv << 2;
-	noteLookup->waveformSize[2] = iDiv << 3;
+Bit16s Tables::clampWF(Synth *synth, const char *n, float ampVal, double input) {
+	Bit32s x = (Bit32s)(input * ampVal);
+	if (x < -ampVal - 1) {
+		synth->printDebug("%s==%d<-WGAMP-1!", n, x);
+		x = (Bit32s)(-ampVal - 1);
+	} else if (x > ampVal) {
+		synth->printDebug("%s==%d>WGAMP!", n, x);
+		x = (Bit32s)ampVal;
+	}
+	return (Bit16s)x;
+}
+
+File *Tables::initWave(Synth *synth, NoteLookup *noteLookup, float ampVal, float div2, File *file) {
+	int iDiv2 = (int)div2;
+	noteLookup->waveformSize[0] = iDiv2 << 1;
+	noteLookup->waveformSize[1] = iDiv2 << 1;
+	noteLookup->waveformSize[2] = iDiv2 << 2;
 	for (int i = 0; i < 3; i++) {
 		if (noteLookup->waveforms[i] == NULL) {
 			noteLookup->waveforms[i] = new Bit16s[noteLookup->waveformSize[i]];
@@ -495,27 +495,27 @@
 		}
 	}
 	if (file == NULL) {
-		double sd = DOUBLE_PI / (div * 2.0);
+		double sd = DOUBLE_PI / div2;
 
-		for (int fa = 0; fa < (iDiv << 2); fa++) {
+		for (int fa = 0; fa < (iDiv2 << 1); fa++) {
+			// sa ranges from 0 to 2PI
 			double sa = fa * sd;
 
-#if 0
-			//FIXME:KG: Credit Timo Strunk (bastardo on #scummvm) for help with this!
-			double saw = 0.5 * DOUBLE_PI - sa / 2;
-#else
+			// Calculate a sample for the bandlimited sawtooth wave
 			double saw = 0.0;
-			for (int sinus = 1; sinus < div; sinus++) {
-				double fsinus = (double)sinus;
-				saw += sin(fsinus * sa) / fsinus;
+			int sincs = iDiv2 >> 1;
+			double sinus = 1.0;
+			for (int sincNum = 1; sincNum <= sincs; sincNum++) {
+				saw += sin(sinus * sa) / sinus;
+				sinus++;
 			}
-#endif
 
 			// This works pretty well
-			noteLookup->waveforms[0][fa] = (Bit16s)(saw * -ampsize / 2);
-			noteLookup->waveforms[1][fa] = (Bit16s)(cos(sa / 2.0) * -ampsize);
-			noteLookup->waveforms[2][fa * 2] = (Bit16s)(cos(sa - DOUBLE_PI) * -ampsize);
-			noteLookup->waveforms[2][fa * 2 + 1] = (Bit16s)(cos((sa + (sd / 2)) - DOUBLE_PI) * -ampsize);
+			// Multiplied by 0.84 so that the spikes caused by bandlimiting don't overdrive the amplitude
+			noteLookup->waveforms[0][fa] = clampWF(synth, "saw", ampVal, -saw / (0.5 * DOUBLE_PI) * 0.84);
+			noteLookup->waveforms[1][fa] = clampWF(synth, "cos", ampVal, -cos(sa / 2.0));
+			noteLookup->waveforms[2][fa * 2] = clampWF(synth, "cosoff_0", ampVal, -cos(sa - DOUBLE_PI));
+			noteLookup->waveforms[2][fa * 2 + 1] = clampWF(synth, "cosoff_1", ampVal, -cos((sa + (sd / 2)) - DOUBLE_PI));
 		}
 	}
 	return file;
@@ -547,11 +547,12 @@
 		float cfmult = (float)cf;
 
 		for (int tf = 0;tf <= 100; tf++) {
-			float tfadd = (float)(tf - 0);
-			if (tfadd < 0)
-				tfadd = 0;
+			float tfadd = (float)tf;
 
-			float freqsum = expf((cfmult + tfadd) / 30.0f) / 4.0f;
+			//float freqsum = expf((cfmult + tfadd) / 30.0f) / 4.0f;
+			//float freqsum = 0.15f * expf(0.45f * ((cfmult + tfadd) / 10.0f));
+
+			float freqsum = powf(2.0f, ((cfmult + tfadd) - 40.0f) / 16.0f);
 
 			noteLookup->nfiltTable[cf][tf] = (int)((freq * freqsum) / (rate / 2) * FILTERGRAN);
 			if (noteLookup->nfiltTable[cf][tf] >= ((FILTERGRAN * 15) / 16))
@@ -560,26 +561,25 @@
 	}
 }
 
-File *TableInitialiser::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float masterTune, PCMWaveEntry pcmWaves[128], File *file) {
-	float ampsize = WGAMP;
+File *Tables::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float masterTune, PCMWaveEntry *pcmWaves, File *file) {
 	float freq = (float)(masterTune * pow(2.0, ((double)note - MIDDLEA) / 12.0));
-	float div = rate / freq;
-	noteLookup->div = (int)div;
+	float div2 = rate * 2.0f / freq;
+	noteLookup->div2 = (int)div2;
 
-	if (noteLookup->div == 0)
-		noteLookup->div = 1;
+	if (noteLookup->div2 == 0)
+		noteLookup->div2 = 1;
 
-	initSaw(noteLookup, noteLookup->div);
-	initDep(noteLookup, note);
+	initSaw(noteLookup, noteLookup->div2);
 
 	//synth->printDebug("Note %f; freq=%f, div=%f", note, freq, rate / freq);
-	file = initWave(synth, noteLookup, ampsize, div, file);
+	file = initWave(synth, noteLookup, (const float)WGAMP, div2, file);
 
 	// Create the pitch tables
-
+	if (noteLookup->wavTable == NULL)
+		noteLookup->wavTable = new Bit32u[synth->controlROMMap->pcmCount];
 	double rateMult = 32000.0 / rate;
 	double tuner = freq * 65536.0f;
-	for (int pc = 0; pc < 128; pc++) {
+	for (int pc = 0; pc < synth->controlROMMap->pcmCount; pc++) {
 		noteLookup->wavTable[pc] = (int)(tuner / pcmWaves[pc].tune * rateMult);
 	}
 
@@ -588,13 +588,13 @@
 	return file;
 }
 
-bool TableInitialiser::initNotes(Synth *synth, PCMWaveEntry pcmWaves[128], float rate, float masterTune) {
+bool Tables::initNotes(Synth *synth, PCMWaveEntry *pcmWaves, float rate, float masterTune) {
 	const char *NoteNames[12] = {
 		"C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B "
 	};
 	char filename[64];
 	int intRate = (int)rate;
-	char version[4] = {0, 0, 0, 3};
+	char version[4] = {0, 0, 0, 5};
 	sprintf(filename, "waveformcache-%d-%.2f.raw", intRate, masterTune);
 
 	File *file = NULL;
@@ -649,7 +649,7 @@
 	bool abort = false;
 	synth->report(ReportType_progressInit, &progress);
 	for (int f = LOWEST_NOTE; f <= HIGHEST_NOTE; f++) {
-		synth->printDebug("Initialising note %s%d", NoteNames[f % 12], (f / 12) - 1);
+		synth->printDebug("Initialising note %s%d", NoteNames[f % 12], (f / 12) - 2);
 		NoteLookup *noteLookup = &noteLookups[f - LOWEST_NOTE];
 		file = initNote(synth, noteLookup, (float)f, rate, masterTune, pcmWaves, file);
 		progress = (f - LOWEST_NOTE + 1) / (float)NUM_NOTES;
@@ -690,7 +690,7 @@
 	return !abort;
 }
 
-void TableInitialiser::freeNotes() {
+void Tables::freeNotes() {
 	for (int t = 0; t < 3; t++) {
 		for (int m = 0; m < NUM_NOTES; m++) {
 			if (noteLookups[m].waveforms[t] != NULL) {
@@ -698,12 +698,22 @@
 				noteLookups[m].waveforms[t] = NULL;
 				noteLookups[m].waveformSize[t] = 0;
 			}
+			if (noteLookups[m].wavTable != NULL) {
+				delete[] noteLookups[m].wavTable;
+				noteLookups[m].wavTable = NULL;
+			}
 		}
 	}
 	initialisedMasterTune = 0.0f;
 }
 
-bool TableInitialiser::initMT32Tables(Synth *synth, PCMWaveEntry pcmWaves[128], float sampleRate, float masterTune) {
+Tables::Tables() {
+	initialisedSampleRate = 0.0f;
+	initialisedMasterTune = 0.0f;
+	memset(&noteLookups, 0, sizeof(noteLookups));
+}
+
+bool Tables::init(Synth *synth, PCMWaveEntry *pcmWaves, float sampleRate, float masterTune) {
 	if (sampleRate <= 0.0f) {
 		synth->printDebug("Bad sampleRate (%d <= 0.0f)", sampleRate);
 		return false;
@@ -714,6 +724,9 @@
 	if (initialisedSampleRate != sampleRate) {
 		initFiltCoeff(sampleRate);
 		initEnvelopes(sampleRate);
+		for (int key = 12; key <= 108; key++) {
+			initDep(&keyLookups[key - 12], (float)key);
+		}
 	}
 	if (initialisedSampleRate != sampleRate || initialisedMasterTune != masterTune) {
 		freeNotes();

Index: tables.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/softsynth/mt32/tables.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- tables.h	25 Dec 2004 18:34:44 -0000	1.1
+++ tables.h	20 Mar 2005 16:24:55 -0000	1.2
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2004 Various contributors
+/* Copyright (c) 2003-2005 Various contributors
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -33,67 +33,82 @@
 // Filter settings
 const int FILTERGRAN = 512;
 
+// Amplitude of waveform generator
+// FIXME: This value is the amplitude possible whilst avoiding
+// overdriven values immediately after filtering when playing
+// back SQ3MT.MID. Needs to be checked.
+const int WGAMP = 12382;
+
 const int MIDDLEC = 60;
 const int MIDDLEA = 69; // By this I mean "A above middle C"
 
-//FIXME:KG: may only need to do 12 to 108
-//12..108 is the range allowed by note on commands, but the key can be modified by pitch keyfollow
-//and adjustment for timbre pitch, so the results can be outside that range. Do move it (by octave) into
-// the 12..108 range, or keep it in 0..127 range, or something else altogether?
+// FIXME:KG: may only need to do 12 to 108
+// 12..108 is the range allowed by note on commands, but the key can be modified by pitch keyfollow
+// and adjustment for timbre pitch, so the results can be outside that range.
+// Should we move it (by octave) into the 12..108 range, or keep it in 0..127 range,
+// or something else altogether?
 const int LOWEST_NOTE = 12;
 const int HIGHEST_NOTE = 127;
 const int NUM_NOTES = HIGHEST_NOTE - LOWEST_NOTE + 1; // Number of slots for note LUT
 
-// Amplitude of waveform generator
-const int WGAMP = 7168; // 8192?
-
 class Synth;
 
-extern Bit16s smallnoise[MAX_SAMPLE_OUTPUT];
-
-// Some optimization stuff
-extern Bit32s keytable[217];
-extern Bit16s sintable[65536];
-extern Bit32u lfotable[101];
-extern Bit32s penvtable[16][101];
-extern Bit32s filveltable[128][101];
-extern Bit32s veltkeytable[5][128];
-extern Bit32s pulsetable[101];
-extern Bit32s ampbiastable[13][128];
-extern Bit32s fbiastable[15][128];
-extern float filtcoeff[FILTERGRAN][31][8];
-extern Bit32s finetable[201];
-extern Bit32u lfoptable[101][101];
-extern Bit32s ampveltable[128][64];
-extern Bit32s pwveltable[15][128];
-extern Bit32s envtimetable[101];
-extern Bit32s decaytimetable[101];
-extern Bit32s lasttimetable[101];
-extern Bit32s voltable[128];
-extern float ResonInv[31];
-
 struct NoteLookup {
-	Bit32u div;
-	Bit32u wavTable[128];
+	Bit32u div2;
+	Bit32u *wavTable;
 	Bit32s sawTable[101];
-	Bit32s fildepTable[5];
-	Bit32s timekeyTable[5];
 	int filtTable[2][201];
 	int nfiltTable[101][101];
 	Bit16s *waveforms[3];
 	Bit32u waveformSize[3];
 };
 
-extern NoteLookup noteLookups[NUM_NOTES];
+struct KeyLookup {
+	Bit32s envTimeMult[5]; // For envelope time adjustment for key pressed
+	Bit32s envDepthMult[5];
+};
 
-class TableInitialiser {
-	static void initMT32ConstantTables(Synth *synth);
-	static File *initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div, File *file);
-	static bool initNotes(Synth *synth, PCMWaveEntry pcmWaves[128], float rate, float tuning);
+class Tables {
+	float initialisedSampleRate;
+	float initialisedMasterTune;
+	void initMT32ConstantTables(Synth *synth);
+	static Bit16s clampWF(Synth *synth, const char *n, float ampVal, double input);
+	static File *initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div2, File *file);
+	bool initNotes(Synth *synth, PCMWaveEntry pcmWaves[128], float rate, float tuning);
+	void initEnvelopes(float sampleRate);
+	void initFiltCoeff(float samplerate);
 public:
-	static bool initMT32Tables(Synth *synth, PCMWaveEntry pcmWaves[128], float sampleRate, float masterTune);
-	static File *initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWaveEntry pcmWaves[128], File *file);
-	static void freeNotes();
+	// Constant LUTs
+	Bit32s tvfKeyfollowMult[217];
+	Bit32s tvfVelfollowMult[128][101];
+	Bit32s tvfBiasMult[15][128];
+	Bit32u tvaVelfollowMult[128][101];
+	Bit32s tvaBiasMult[13][128];
+	Bit16s noiseBuf[MAX_SAMPLE_OUTPUT];
+	Bit16s sintable[65536];
+	Bit32s pitchEnvVal[16][101];
+	Bit32s envTimeVelfollowMult[5][128];
+	Bit32s pwVelfollowAdd[15][128];
+	float resonanceFactor[31];
+	Bit32u lfoShift[101][101];
+	Bit32s pwFactor[101];
+	Bit32s volumeMult[101];
+
+	// LUTs varying with sample rate
+	Bit32u envTime[101];
+	Bit32u envDeltaMaxTime[101];
+	Bit32u envDecayTime[101];
+	Bit32u lfoPeriod[101];
+	float filtCoeff[FILTERGRAN][31][8];
+
+	// Various LUTs for each note and key
+	NoteLookup noteLookups[NUM_NOTES];
+	KeyLookup keyLookups[97];
+
+	Tables();
+	bool init(Synth *synth, PCMWaveEntry pcmWaves[128], float sampleRate, float masterTune);
+	File *initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWaveEntry pcmWaves[128], File *file);
+	void freeNotes();
 };
 
 }





More information about the Scummvm-git-logs mailing list