diff options
Diffstat (limited to 'backends/midi/mt32')
| -rw-r--r-- | backends/midi/mt32/mt32_file.h | 2 | ||||
| -rw-r--r-- | backends/midi/mt32/mt32emu.h | 4 | ||||
| -rw-r--r-- | backends/midi/mt32/part.cpp | 505 | ||||
| -rw-r--r-- | backends/midi/mt32/part.h | 66 | ||||
| -rw-r--r-- | backends/midi/mt32/partial.cpp | 287 | ||||
| -rw-r--r-- | backends/midi/mt32/partial.h | 35 | ||||
| -rw-r--r-- | backends/midi/mt32/partialManager.cpp | 49 | ||||
| -rw-r--r-- | backends/midi/mt32/partialManager.h | 20 | ||||
| -rw-r--r-- | backends/midi/mt32/structures.h | 179 | ||||
| -rw-r--r-- | backends/midi/mt32/synth.cpp | 800 | ||||
| -rw-r--r-- | backends/midi/mt32/synth.h | 84 | ||||
| -rw-r--r-- | backends/midi/mt32/tables.cpp | 126 | ||||
| -rw-r--r-- | backends/midi/mt32/tables.h | 28 | 
13 files changed, 975 insertions, 1210 deletions
| diff --git a/backends/midi/mt32/mt32_file.h b/backends/midi/mt32/mt32_file.h index f5888db5e3..7ebb6449b6 100644 --- a/backends/midi/mt32/mt32_file.h +++ b/backends/midi/mt32/mt32_file.h @@ -41,7 +41,7 @@ public:  	virtual bool readBit32u(Bit32u *in);  	virtual size_t write(const void *out, size_t size) = 0;  	virtual bool writeBit8u(Bit8u out) = 0; -	// Note: May write some a single byte to the file before failing +	// Note: May write a single byte to the file before failing  	virtual bool writeBit16u(Bit16u out);  	// Note: May write some (<4) bytes to the file before failing  	virtual bool writeBit32u(Bit32u out); diff --git a/backends/midi/mt32/mt32emu.h b/backends/midi/mt32/mt32emu.h index 48c3d903fd..9fffa721f5 100644 --- a/backends/midi/mt32/mt32emu.h +++ b/backends/midi/mt32/mt32emu.h @@ -27,10 +27,6 @@  #define MT32EMU_MONITOR_INSTRUMENTS 1  // Shows number of partials MT-32 is playing, and on which parts  #define MT32EMU_MONITOR_PARTIALS 0 -// Dump drum patches to syx file for viewing -#define MT32EMU_DUMP_DRUMS 0 -// Output benchmarks for the filter implementations -#define MT32EMU_BENCHMARK_FILTERS 0  // Determines how the waveform cache file is handled (must be regenerated after sampling rate change)  #define MT32EMU_WAVECACHEMODE 0 // Load existing cache if possible, otherwise generate and save cache  //#define MT32EMU_WAVECACHEMODE 1 // Load existing cache if possible, otherwise generage but don't save cache diff --git a/backends/midi/mt32/part.cpp b/backends/midi/mt32/part.cpp index 6fe783e1b6..c240874143 100644 --- a/backends/midi/mt32/part.cpp +++ b/backends/midi/mt32/part.cpp @@ -34,16 +34,19 @@ static const Bit8u PartialMixStruct[13] = {  	0, 1, 0, 1, 1, 0,  	1, 3, 3, 2, 2, 2, 2 }; -// This caches the timbres/settings in use by the rhythm part -static PatchCache drumCache[94][4]; -static StereoVolume drumPan[64]; +static const float floatKeyfollow[17] = { +	-1.0f, -1.0f/2.0f, -1.0f/4.0f, 0.0f, +	1.0f/8.0f, 1.0f/4.0f, 3.0f/8.0f, 1.0f/2.0f, 5.0f/8.0f, 3.0f/4.0f, 7.0f/8.0f, 1.0f, +	5.0f/4.0f, 3.0f/2.0f, 2.0f, +	1.0009765625f, 1.0048828125f +};  //FIXME:KG: Put this dpoly stuff somewhere better -bool dpoly::isActive() { +bool dpoly::isActive() const {  	return partials[0] != NULL || partials[1] != NULL || partials[2] != NULL || partials[3] != NULL;  } -Bit64s dpoly::getAge() { +Bit64s dpoly::getAge() const {  	for (int i = 0; i < 4; i++) {  		if (partials[i] != NULL) {  			return partials[i]->age; @@ -52,36 +55,34 @@ Bit64s dpoly::getAge() {  	return 0;  } -Part::Part(Synth *useSynth, int usePartNum) { +RhythmPart::RhythmPart(Synth *useSynth, unsigned int usePartNum): Part(useSynth, usePartNum) { +	strcpy(name, "Rhythm"); +	rhythmTemp = &synth->mt32ram.rhythmSettings[0]; +	refresh(); +} + +Part::Part(Synth *useSynth, unsigned int usePartNum) {  	this->synth = useSynth;  	this->partNum = usePartNum; -	isRhythm = (usePartNum == 8); +	patchCache[0].dirty = true;  	holdpedal = false; -	if (isRhythm) { -		strcpy(name, "Rhythm"); +	if (usePartNum == 8) { +		// Nasty hack for rhythm  		patchTemp = NULL;  		timbreTemp = NULL; -		rhythmTemp = &synth->mt32ram.params.rhythmSettings[0];  	} else {  		sprintf(name, "Part %d", partNum + 1); -		patchTemp = &synth->mt32ram.params.patchSettings[partNum]; -		timbreTemp = &synth->mt32ram.params.timbreSettings[partNum]; -		rhythmTemp = NULL; +		patchTemp = &synth->mt32ram.patchSettings[partNum]; +		timbreTemp = &synth->mt32ram.timbreSettings[partNum];  	}  	currentInstr[0] = 0;  	currentInstr[10] = 0; -	volume = 102; +	volume = voltable[102]; //FIXME:KG: Original was just volume=102; I assume this is intended  	volumesetting.leftvol = 32767;  	volumesetting.rightvol = 32767;  	bend = 0.0f;  	memset(polyTable,0,sizeof(polyTable));  	memset(patchCache, 0, sizeof(patchCache)); - -	if (isRhythm) { -		init = true; -		refreshDrumCache(); -	} -	init = false;  }  void Part::setHoldPedal(bool pedalval) { @@ -90,16 +91,17 @@ void Part::setHoldPedal(bool pedalval) {  	holdpedal = pedalval;  } -void Part::setBend(int midiBend) { -	if (isRhythm) { -		synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, midiBend); -		return; -	} -	// FIXME:KG: Slightly uneven increments, but I wanted min -1.0, centre 0.0 and max 1.0 +void RhythmPart::setBend(unsigned int midiBend) { +	synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, midiBend); +	return; +} + +void Part::setBend(unsigned int midiBend) { +	// FIXME:KG: Slightly unbalanced increments, but I wanted min -1.0, centre 0.0 and max 1.0  	if (midiBend <= 0x2000) { -		bend = (midiBend - 0x2000) / (float)0x2000; +		bend = ((signed int)midiBend - 0x2000) / (float)0x2000;  	} else { -		bend = (midiBend - 0x2000) / (float)0x1FFF; +		bend = ((signed int)midiBend - 0x2000) / (float)0x1FFF;  	}  	// Loop through all partials to update their bend  	for (int i = 0; i < MT32EMU_MAX_POLY; i++) { @@ -111,15 +113,15 @@ void Part::setBend(int midiBend) {  	}  } -void Part::setModulation(int vol) { -	if (isRhythm) { -		synth->printDebug("%s: Setting modulation (%d) not supported on rhythm", name, vol); -		return; -	} +void RhythmPart::setModulation(unsigned int midiModulation) { +	synth->printDebug("%s: Setting modulation (%d) not supported on rhythm", name, midiModulation); +} + +void Part::setModulation(unsigned int midiModulation) {  	// Just a bloody guess, as always, before I get things figured out  	for (int t = 0; t < 4; t++) {  		if (patchCache[t].playPartial) { -			int newrate = (patchCache[t].modsense * vol) >> 7; +			int newrate = (patchCache[t].modsense * midiModulation) >> 7;  			//patchCache[t].lfoperiod = lfotable[newrate];  			patchCache[t].lfodepth = newrate;  			//FIXME:KG: timbreTemp->partial[t].lfo.depth = @@ -127,52 +129,78 @@ void Part::setModulation(int vol) {  	}  } -void Part::refreshDrumCache() { -	if (!isRhythm) { -		synth->printDebug("ERROR: RefreshDrumCache() called on non-rhythm part"); -	} -	// Cache drum patches -	for (int m = 0; m < 64; m++) { -		int drumTimbre = rhythmTemp[m].timbre; -		if (drumTimbre >= 94) +void RhythmPart::refresh() { +	// (Re-)cache all the mapped timbres ahead of time +	for (unsigned int drumNum = 0; drumNum < 64; drumNum++) { +		int drumTimbreNum = rhythmTemp[drumNum].timbre; +		if (drumTimbreNum >= 94)  			continue; -		setPatch(drumTimbre + 128); // This is to cache all the mapped drum timbres ahead of time -		Bit16s pan = rhythmTemp[m].panpot; // They use R-L 0-14... +		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[m].leftvol = 32767; -			drumPan[m].rightvol = pan * 4681; +			drumPan[drumNum].leftvol = 32767; +			drumPan[drumNum].rightvol = pan * 4681;  		} else { -			drumPan[m].rightvol = 32767; -			drumPan[m].leftvol = (14 - pan) * 4681; +			drumPan[drumNum].rightvol = 32767; +			drumPan[drumNum].leftvol = (14 - pan) * 4681;  		} +		PatchCache *cache = drumCache[drumNum]; +		backupCacheToPartials(cache); +		for (int t = 0; t < 4; t++) { +			// Common parameters, stored redundantly +			cache[t].dirty = true; +			cache[t].pitchShift = 0.0f; +			cache[t].benderRange = 0.0f; +			cache[t].pansetptr = &drumPan[drumNum]; +			cache[t].reverb = rhythmTemp[drumNum].reverbSwitch > 0; +		} +	} +} + +void Part::refresh() { +	backupCacheToPartials(patchCache); +	for (int t = 0; t < 4; t++) { +		// Common parameters, stored redundantly +		patchCache[t].dirty = true; +		patchCache[t].pitchShift = (patchTemp->patch.keyShift - 24) + (patchTemp->patch.fineTune - 50) / 100.0f; +		patchCache[t].benderRange = patchTemp->patch.benderRange; +		patchCache[t].pansetptr = &volumesetting; +		patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0; +	} +	memcpy(currentInstr, timbreTemp->common.name, 10); +} + +void RhythmPart::refreshTimbre(unsigned int absTimbreNum) { +	for (int m = 0; m < 64; m++) { +		if (rhythmTemp[m].timbre == absTimbreNum - 128) +			drumCache[m][0].dirty = true; +	} +} + +void Part::refreshTimbre(unsigned int absTimbreNum) { +	if (getAbsTimbreNum() == absTimbreNum) { +		memcpy(currentInstr, timbreTemp->common.name, 10); +		patchCache[0].dirty = true;  	}  }  int Part::fixBiaslevel(int srcpnt, int *dir) { -	int noteat = srcpnt & 63; +	int noteat = srcpnt & 0x3F;  	int outnote; -	*dir = 1;  	if (srcpnt < 64)  		*dir = 0; +	else +		*dir = 1;  	outnote = 33 + noteat;  	//synth->printDebug("Bias note %d, dir %d", outnote, *dir);  	return outnote;  } -int Part::fixKeyfollow(int srckey, int *dir) { +int Part::fixKeyfollow(int srckey) {  	if (srckey>=0 && srckey<=16) { -		//int keyfix[17] = { 256, 128, 64, 0, 32, 64, 96, 128, 128+32, 192, 192+32, 256, 256+64, 256+128, 512, 259, 269 }; -		int keyfix[17] = { 256*16, 128*16, 64*16, 0, 32*16, 64*16, 96*16, 128*16, (128+32)*16, 192*16, (192+32)*16, 256*16, (256+64)*16, (256+128)*16, (512)*16, 4100, 4116}; - -		if (srckey<3) -			*dir = -1; -		else if (srckey==3) -			*dir = 0; -		else -			*dir = 1; - +		int keyfix[17] = { -256*16, -128*16, -64*16, 0, 32*16, 64*16, 96*16, 128*16, (128+32)*16, 192*16, (192+32)*16, 256*16, (256+64)*16, (256+128)*16, (512)*16, 4100, 4116};  		return keyfix[srckey];  	} else {  		//LOG(LOG_ERROR|LOG_MISC,"Missed key: %d", srckey); @@ -180,10 +208,6 @@ int Part::fixKeyfollow(int srckey, int *dir) {  	}  } -void Part::refreshPatch() { -	setPatch(-1); -} -  void Part::abortPoly(dpoly *poly) {  	if (!poly->isPlaying) {  		return; @@ -197,7 +221,7 @@ void Part::abortPoly(dpoly *poly) {  	poly->isPlaying = false;  } -void Part::setPatch(PatchParam *patch) { +void Part::setPatch(const PatchParam *patch) {  	patchTemp->patch = *patch;  } @@ -205,187 +229,165 @@ void Part::setTimbre(TimbreParam *timbre) {  	*timbreTemp = *timbre;  } -unsigned int Part::getAbsTimbreNum() { -	if (isRhythm) { -		synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm"); -		return 0; -	} +unsigned int RhythmPart::getAbsTimbreNum() const { +	synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm"); +	return 0; +} + +unsigned int Part::getAbsTimbreNum() const {  	return (patchTemp->patch.timbreGroup * 64) + patchTemp->patch.timbreNum;  } -void Part::setPatch(int patchNum) { -	int absTimbreNum = -1; // Initialised to please compiler -	const TimbreParam *timbre; -	if (isRhythm) { -		// "patchNum" is treated as "timbreNum" for rhythm part -		if (patchNum < 128) { -			synth->printDebug("%s: Patch #%d is not valid for rhythm (must be >= 128)", name, patchNum); -			return; -		} -		absTimbreNum = patchNum; -		timbre = &synth->mt32ram.params.timbres[absTimbreNum].timbre; -	} else { -		if (patchNum >= 0) { -			setPatch(&synth->mt32ram.params.patches[patchNum]); -		} -		if (patchNum >= 0) { -			setTimbre(&synth->mt32ram.params.timbres[getAbsTimbreNum()].timbre); -		} -		timbre = timbreTemp; +void RhythmPart::setProgram(unsigned int patchNum) { +	synth->printDebug("%s: Attempt to set program (%d) on rhythm is invalid", name, patchNum); +} + +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); -		} -#else -		// check if any partials are still playing with the old patch cache -		// if so then duplicate the cached data from the part to the partial so that -		// we can change the part's cache without affecting the partial. -		// We delay this until now to avoid a copy operation with every note played -		for (int m = 0; m < MT32EMU_MAX_POLY; m++) { -			for (int i = 0; i < 4; i++) { -				Partial *partial = polyTable[m].partials[i]; -				if (partial != NULL && partial->patchCache == &patchCache[i]) { -					// copy cache data -					partial->cachebackup = patchCache[i]; -					// update pointers -					partial->patchCache = &partial->cachebackup; -				} +	// 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? +} + +void Part::backupCacheToPartials(PatchCache cache[4]) { +	// check if any partials are still playing with the old patch cache +	// if so then duplicate the cached data from the part to the partial so that +	// we can change the part's cache without affecting the partial. +	// We delay this until now to avoid a copy operation with every note played +	for (int m = 0; m < MT32EMU_MAX_POLY; m++) { +		for (int i = 0; i < 4; i++) { +			Partial *partial = polyTable[m].partials[i]; +			if (partial != NULL && partial->patchCache == &cache[i]) { +				partial->cachebackup = cache[i]; +				partial->patchCache = &partial->cachebackup;  			}  		} -#endif  	} +} -	memcpy(currentInstr, timbre->common.name, 10); - +void Part::cacheTimbre(PatchCache cache[4], const TimbreParam *timbre) { +	backupCacheToPartials(cache);  	int partialCount = 0;  	for (int t = 0; t < 4; t++) {  		if (((timbre->common.pmute >> t) & 0x1) == 1) { -			patchCache[t].playPartial = true; +			cache[t].playPartial = true;  			partialCount++;  		} else { -			patchCache[t].playPartial = false; +			cache[t].playPartial = false;  			continue;  		}  		// Calculate and cache common parameters -		patchCache[t].pcm = timbre->partial[t].wg.pcmwave; -		patchCache[t].useBender = (timbre->partial[t].wg.bender == 1); +		cache[t].pcm = timbre->partial[t].wg.pcmwave; +		cache[t].useBender = (timbre->partial[t].wg.bender == 1);  		switch (t) {  		case 0: -			patchCache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x2) ? true : false; -			patchCache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12]; -			patchCache[t].structurePosition = 0; -			patchCache[t].structurePair = 1; +			cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x2) ? true : false; +			cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12]; +			cache[t].structurePosition = 0; +			cache[t].structurePair = 1;  			break;  		case 1: -			patchCache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x1) ? true : false; -			patchCache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12]; -			patchCache[t].structurePosition = 1; -			patchCache[t].structurePair = 0; +			cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x1) ? true : false; +			cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12]; +			cache[t].structurePosition = 1; +			cache[t].structurePair = 0;  			break;  		case 2: -			patchCache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x2) ? true : false; -			patchCache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34]; -			patchCache[t].structurePosition = 0; -			patchCache[t].structurePair = 3; +			cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x2) ? true : false; +			cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34]; +			cache[t].structurePosition = 0; +			cache[t].structurePair = 3;  			break;  		case 3: -			patchCache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x1) ? true : false; -			patchCache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34]; -			patchCache[t].structurePosition = 1; -			patchCache[t].structurePair = 2; +			cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x1) ? true : false; +			cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34]; +			cache[t].structurePosition = 1; +			cache[t].structurePair = 2;  			break;  		default:  			break;  		} -		patchCache[t].waveform = timbre->partial[t].wg.waveform; -		patchCache[t].pulsewidth = timbre->partial[t].wg.pulsewid; -		patchCache[t].pwsens = timbre->partial[t].wg.pwvelo; -		patchCache[t].pitchkeyfollow = fixKeyfollow(timbre->partial[t].wg.keyfollow, &patchCache[t].pitchkeydir); - -		// Calculate and cache pitch stuff -		patchCache[t].pitchshift = timbre->partial[t].wg.coarse; -		Bit32s pFine, fShift; -		pFine = (Bit32s)timbre->partial[t].wg.fine; -		if (isRhythm) { -			patchCache[t].pitchshift += 24; -			fShift = pFine + 50; +		cache[t].waveform = timbre->partial[t].wg.waveform; +		cache[t].pulsewidth = timbre->partial[t].wg.pulsewid; +		cache[t].pwsens = timbre->partial[t].wg.pwvelo; +		if (timbre->partial[t].wg.keyfollow > 16) { +			synth->printDebug("Bad keyfollow value in timbre!"); +			cache[t].pitchKeyfollow = 1.0f;  		} else { -			patchCache[t].pitchshift += patchTemp->patch.keyShift; -			fShift = pFine + (Bit32s)patchTemp->patch.fineTune; +			cache[t].pitchKeyfollow = floatKeyfollow[timbre->partial[t].wg.keyfollow];  		} -		patchCache[t].fineshift = finetable[fShift]; -		patchCache[t].pitchEnv = timbre->partial[t].env; -		patchCache[t].pitchEnv.sensitivity = (char)((float)patchCache[t].pitchEnv.sensitivity*1.27); -		patchCache[t].pitchsustain = patchCache[t].pitchEnv.level[3]; +		cache[t].pitch = timbre->partial[t].wg.coarse + (timbre->partial[t].wg.fine - 50) / 100.0f + 24.0f; +		cache[t].pitchEnv = timbre->partial[t].env; +		cache[t].pitchEnv.sensitivity = (char)((float)cache[t].pitchEnv.sensitivity * 1.27f); +		cache[t].pitchsustain = cache[t].pitchEnv.level[3];  		// Calculate and cache TVA envelope stuff -		patchCache[t].ampEnv = timbre->partial[t].tva; +		cache[t].ampEnv = timbre->partial[t].tva;  		for (int i = 0; i < 4; i++) -			patchCache[t].ampEnv.envlevel[i] = (char)((float)patchCache[t].ampEnv.envlevel[i] * 1.27f); -		patchCache[t].ampEnv.level = (char)((float)patchCache[t].ampEnv.level * 1.27f); +			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; -		patchCache[t].ampEnv.velosens = (char)velo; +		cache[t].ampEnv.velosens = (char)velo;  		if (tvelo<0.5f) -			patchCache[t].ampenvdir = 1; +			cache[t].ampenvdir = 1;  		else -			patchCache[t].ampenvdir = 0; +			cache[t].ampenvdir = 0; -		patchCache[t].ampbias[0] = fixBiaslevel(patchCache[t].ampEnv.biaspoint1, &patchCache[t].ampdir[0]); -		patchCache[t].ampblevel[0] = 12 - patchCache[t].ampEnv.biaslevel1; -		patchCache[t].ampbias[1] = fixBiaslevel(patchCache[t].ampEnv.biaspoint2, &patchCache[t].ampdir[1]); -		patchCache[t].ampblevel[1] = 12 - patchCache[t].ampEnv.biaslevel2; -		patchCache[t].ampdepth = patchCache[t].ampEnv.envvkf * patchCache[t].ampEnv.envvkf; -		patchCache[t].ampsustain = patchCache[t].ampEnv.envlevel[3]; -		patchCache[t].amplevel = patchCache[t].ampEnv.level; +		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 -		patchCache[t].filtEnv = timbre->partial[t].tvf; -		patchCache[t].tvfdepth = patchCache[t].filtEnv.envdkf; -		patchCache[t].filtkeyfollow  = fixKeyfollow(patchCache[t].filtEnv.keyfollow, &patchCache[t].keydir); -		patchCache[t].filtEnv.envdepth = (char)((float)patchCache[t].filtEnv.envdepth * 1.27); -		patchCache[t].tvfbias = fixBiaslevel(patchCache[t].filtEnv.biaspoint, &patchCache[t].tvfdir); -		patchCache[t].tvfblevel = patchCache[t].filtEnv.biaslevel; -		patchCache[t].filtsustain  = patchCache[t].filtEnv.envlevel[3]; +		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); +		cache[t].tvfblevel = cache[t].filtEnv.biaslevel; +		cache[t].filtsustain  = cache[t].filtEnv.envlevel[3];  		// Calculate and cache LFO stuff -		patchCache[t].lfodepth = timbre->partial[t].lfo.depth; -		patchCache[t].lfoperiod = lfotable[(int)timbre->partial[t].lfo.rate]; -		patchCache[t].lforate = timbre->partial[t].lfo.rate; -		patchCache[t].modsense = timbre->partial[t].lfo.modsense; +		cache[t].lfodepth = timbre->partial[t].lfo.depth; +		cache[t].lfoperiod = lfotable[(int)timbre->partial[t].lfo.rate]; +		cache[t].lforate = timbre->partial[t].lfo.rate; +		cache[t].modsense = timbre->partial[t].lfo.modsense;  	}  	for (int t = 0; t < 4; t++) {  		// Common parameters, stored redundantly -		patchCache[t].partialCount = partialCount; -		patchCache[t].sustain = (timbre->common.nosustain == 0); -		if (isRhythm) { -			patchCache[t].benderRange = 0; -		} else { -			patchCache[t].benderRange = patchTemp->patch.benderRange; -		} +		cache[t].dirty = false; +		cache[t].partialCount = partialCount; +		cache[t].sustain = (timbre->common.nosustain == 0);  	} -	//synth->printDebug("Res 1: %d 2: %d 3: %d 4: %d", patchCache[0].waveform, patchCache[1].waveform, patchCache[2].waveform, patchCache[3].waveform); +	//synth->printDebug("Res 1: %d 2: %d 3: %d 4: %d", cache[0].waveform, cache[1].waveform, cache[2].waveform, cache[3].waveform); -	if (isRhythm) -		memcpy(drumCache[absTimbreNum - 128], patchCache, sizeof(patchCache)); -	else -		allStop();  #if MT32EMU_MONITOR_INSTRUMENTS == 1 -	synth->printDebug("%s: Recache, param %d (timbre: %s)", name, patchNum, currentInstr); +	synth->printDebug("%s (%s): Recached timbre", name, currentInstr);  	for (int i = 0; i < 4; i++) { -		synth->printDebug(" %d: play=%s, pcm=%s (%d), wave=%d", i, patchCache[i].playPartial ? "YES" : "NO", patchCache[i].PCMPartial ? "YES" : "NO", timbre->partial[i].wg.pcmwave, timbre->partial[i].wg.waveform); +		synth->printDebug(" %d: play=%s, pcm=%s (%d), wave=%d", i, cache[i].playPartial ? "YES" : "NO", cache[i].PCMPartial ? "YES" : "NO", timbre->partial[i].wg.pcmwave, timbre->partial[i].wg.waveform);  	}  #endif  } -char *Part::getName() { +const char *Part::getName() const {  	return name;  } @@ -393,72 +395,88 @@ void Part::setVolume(int vol) {  	volume = voltable[vol];  } -void Part::setPan(int pan) { +void RhythmPart::setPan(unsigned int midiPan) +{  	// FIXME:KG: This is unchangeable for drums (they always use drumPan), is that correct? +	synth->printDebug("%s: Setting pan (%d) not supported on rhythm", name, midiPan); +} + +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) -	if (pan < 64) { +	if (midiPan < 64) {  		volumesetting.leftvol = 32767; -		volumesetting.rightvol = (Bit16s)(pan * 512); -	} else if (pan == 64) { +		volumesetting.rightvol = (Bit16s)(midiPan * 512); +	} else if (midiPan == 64) {  		volumesetting.leftvol = 32767;  		volumesetting.rightvol = 32767;  	} else {  		volumesetting.rightvol = 32767; -		volumesetting.leftvol = (Bit16s)((127 - pan) * 520); +		volumesetting.leftvol = (Bit16s)((127 - midiPan) * 520);  	}  	//synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot);  } -void Part::playNote(PartialManager *partialManager, unsigned int key, int vel) { -	int drumNum = -1; // Initialised to please compiler -	int drumTimbre = -1; // As above -	int freqNum; +void RhythmPart::playNote(unsigned int key, int vel) { +	if (key < 24 || key > 87) { +		synth->printDebug("%s: Attempted to play invalid key %d", name, key); +		return; +	} +	int drumNum = key - 24; +	int drumTimbreNum = rhythmTemp[drumNum].timbre; +	if (drumTimbreNum >= 94) { +		synth->printDebug("%s: Attempted to play unmapped key %d", name, key); +		return; +	} +	int absTimbreNum = drumTimbreNum + 128; +	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); +#endif +	if (drumCache[drumNum][0].dirty) { +		cacheTimbre(drumCache[drumNum], timbre); +	} +	playPoly(drumCache[drumNum], key, MIDDLEC, vel); +} -	if (isRhythm) { -		if (key < 24 || key > 87) { -			synth->printDebug("%s: Attempted to play invalid key %d", name, key); -			return; -		} -		drumNum = key - 24; -		drumTimbre = rhythmTemp[drumNum].timbre; -		if (drumTimbre >= 94) { -			synth->printDebug("%s: Attempted to play unmapped key %d", name, key); -			return; +void Part::playNote(unsigned int key, int vel) { +	int freqNum = key; +	if (freqNum < 12) { +		synth->printDebug("%s (%s): Attempted to play invalid key %d < 12; moving up by octave", name, currentInstr, key); +		freqNum += 12; +	} else if (freqNum > 108) { +		synth->printDebug("%s (%s): Attempted to play invalid key %d > 108; moving down by octave", name, currentInstr, key); +		while (freqNum > 108) { +			freqNum -= 12;  		} -		memcpy(patchCache, drumCache[drumTimbre], sizeof(patchCache)); -		memcpy(¤tInstr, synth->mt32ram.params.timbres[128 + drumTimbre].timbre.common.name, 10); -		freqNum = MIDDLEC; -	} else { -		if (key < 12) { -			synth->printDebug("%s (%s): Attempted to play invalid key %d < 12; moving up by octave", name, currentInstr, key); -			key += 12; -		} else if (key > 108) { -			synth->printDebug("%s (%s): Attempted to play invalid key %d > 108; moving down by octave", name, currentInstr, key); -			while (key > 108) { -				key -= 12; -			} -		} -		freqNum = key;  	}  	// POLY1 mode, Single Assign  	// Haven't found any software that uses any of the other poly modes  	// FIXME:KG: Should this also apply to rhythm? -	if (!isRhythm) { -		for (unsigned int i = 0; i < MT32EMU_MAX_POLY; i++) { -			if (polyTable[i].isActive() && (polyTable[i].key == key)) { -				//AbortPoly(&polyTable[i]); -				stopNote(key); -				break; -			} +	for (unsigned int i = 0; i < MT32EMU_MAX_POLY; i++) { +		if (polyTable[i].isActive() && (polyTable[i].key == key)) { +			//AbortPoly(&polyTable[i]); +			stopNote(key); +			break;  		}  	} +#if MT32EMU_MONITOR_INSTRUMENTS == 1 +	synth->printDebug("%s (%s): starting poly - Vel %d Key %d Vol %d", name, currentInstr, vel, key, volume); +#endif +	if (patchCache[0].dirty) { +		cacheTimbre(patchCache, timbreTemp); +	} +	playPoly(patchCache, key, freqNum, vel); +} -	unsigned int needPartials = patchCache[0].partialCount; +void Part::playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel) { +	unsigned int needPartials = cache[0].partialCount; +	unsigned int freePartials = synth->partialManager->getFreePartialCount(); -	if (needPartials > partialManager->GetFreePartialCount()) { -		if (!partialManager->FreePartials(needPartials, partNum)) { -			synth->printDebug("%s (%s): Insufficient free partials to play key %d (vel=%d)", name, currentInstr, key, vel); +	if (freePartials < needPartials) { +		if (!synth->partialManager->freePartials(needPartials - freePartials, partNum)) { +			synth->printDebug("%s (%s): Insufficient free partials to play key %d (vel=%d); needed=%d, free=%d", name, currentInstr, key, vel, needPartials, synth->partialManager->getFreePartialCount());  			return;  		}  	} @@ -485,8 +503,8 @@ void Part::playNote(PartialManager *partialManager, unsigned int key, int vel) {  	bool allnull = true;  	for (int x = 0; x < 4; x++) { -		if (patchCache[x].playPartial) { -			tpoly->partials[x] = partialManager->AllocPartial(partNum); +		if (cache[x].playPartial) { +			tpoly->partials[x] = synth->partialManager->allocPartial(partNum);  			allnull = false;  		} else {  			tpoly->partials[x] = NULL; @@ -496,27 +514,12 @@ void Part::playNote(PartialManager *partialManager, unsigned int key, int vel) {  	if (allnull)  		synth->printDebug("%s (%s): No partials to play for this instrument", name, this->currentInstr); -	if (isRhythm) { -		tpoly->pansetptr = &drumPan[drumNum]; -		tpoly->reverb = rhythmTemp[drumNum].reverbSwitch > 0; -	} else { -		tpoly->pansetptr = &volumesetting; -		tpoly->reverb = patchTemp->patch.reverbSwitch > 0; -	} -	tpoly->sustain = patchCache[0].sustain; +	tpoly->sustain = cache[0].sustain;  	tpoly->volumeptr = &volume; -#if MT32EMU_MONITOR_INSTRUMENTS == 1 -	if (isRhythm) { -		synth->printDebug("%s (%s): starting poly %d (drum %d, timbre %d) - Vel %d Key %d Vol %d", name, currentInstr, m, drumNum, drumTimbre, vel, key, volume); -	} else { -		synth->printDebug("%s (%s): starting poly %d - Vel %d Key %d Vol %d", name, currentInstr, m, vel, key, volume); -	} -#endif -  	for (int x = 0; x < 4; x++) {  		if (tpoly->partials[x] != NULL) { -			tpoly->partials[x]->startPartial(tpoly, &patchCache[x], tpoly->partials[patchCache[x].structurePair]); +			tpoly->partials[x]->startPartial(tpoly, &cache[x], tpoly->partials[cache[x].structurePair]);  			tpoly->partials[x]->setBend(bend);  		}  	} diff --git a/backends/midi/mt32/part.h b/backends/midi/mt32/part.h index e9b8e923e3..1214ec52f9 100644 --- a/backends/midi/mt32/part.h +++ b/backends/midi/mt32/part.h @@ -29,20 +29,12 @@ class Synth;  class Part {  private: -	Synth *synth; // Only used for sending debug output -  	// Pointers to the areas of the MT-32's memory dedicated to this part (for parts 1-8)  	MemParams::PatchTemp *patchTemp;  	TimbreParam *timbreTemp; -	//... and for rhythm -	MemParams::RhythmTemp *rhythmTemp; - -	bool isRhythm; -	bool init; -	int partNum; -	char name[8]; // "Part 1".."Part 8", "Rhythm" -	char currentInstr[11]; +	// 0=Part 1, .. 7=Part 8, 8=Rhythm +	unsigned int partNum;  	bool holdpedal; @@ -51,33 +43,61 @@ private:  	PatchCache patchCache[4];  	float bend; // -1.0 .. +1.0  -	Bit32s volume;  	dpoly polyTable[MT32EMU_MAX_POLY];  	void abortPoly(dpoly *poly); -	static int fixKeyfollow(int srckey, int *dir); +	static int fixKeyfollow(int srckey);  	static int fixBiaslevel(int srcpnt, int *dir); +	void setPatch(const PatchParam *patch); + +protected: +	Synth *synth; +	char name[8]; // "Part 1".."Part 8", "Rhythm" +	char currentInstr[11]; +	Bit32u volume; +	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); +	const char *getName() const; +  public: -	Part(Synth *synth, int usePartNum); -	char *getName(); -	void playNote(PartialManager *partialManager, unsigned int key, int vel); +	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 setPan(int vol); -	void setBend(int vol); -	void setModulation(int vol); -	void setPatch(int patchnum); +	virtual void setPan(unsigned int midiPan); +	virtual void setBend(unsigned int midiBend); +	virtual void setModulation(unsigned int midiModulation); +	virtual void setProgram(unsigned int patchNum);  	void setHoldPedal(bool pedalval);  	void stopPedalHold(); -	void refreshPatch(); -	void refreshDrumCache(); -	void setPatch(PatchParam *patch); +	virtual void refresh(); +	virtual void refreshTimbre(unsigned int absTimbreNum);  	void setTimbre(TimbreParam *timbre); -	unsigned int getAbsTimbreNum(); +	virtual unsigned int getAbsTimbreNum() const; +}; + +class RhythmPart: public Part { +	// Pointer to the area of the MT-32's memory dedicated to rhythm +	const MemParams::RhythmTemp *rhythmTemp; + +	// This caches the timbres/settings in use by the rhythm part +	PatchCache drumCache[64][4]; +	StereoVolume drumPan[64]; +public: +	RhythmPart(Synth *synth, unsigned int usePartNum); +	void refreshTimbre(unsigned int timbreNum); +	void refresh(); +	void playNote(unsigned int key, int vel); +	unsigned int getAbsTimbreNum() const; +	void setPan(unsigned int midiPan); +	void setBend(unsigned int midiBend); +	void setModulation(unsigned int midiModulation); +	void setProgram(unsigned int patchNum);  };  } diff --git a/backends/midi/mt32/partial.cpp b/backends/midi/mt32/partial.cpp index f785f22d67..97158ed396 100644 --- a/backends/midi/mt32/partial.cpp +++ b/backends/midi/mt32/partial.cpp @@ -25,6 +25,11 @@  #include "mt32emu.h" +#define FIXEDPOINT_UDIV(x, y, point) (((x) << (point)) / ((y))) +#define FIXEDPOINT_SDIV(x, y, point) (((x) * (1 << point)) / ((y))) +#define FIXEDPOINT_UMULT(x, y, point) (((x) * (y)) >> point) +#define FIXEDPOINT_SMULT(x, y, point) (((x) * (y)) / (1 << point)) +  using namespace MT32Emu;  Partial::Partial(Synth *useSynth) { @@ -32,9 +37,20 @@ Partial::Partial(Synth *useSynth) {  	ownerPart = -1;  	poly = NULL;  	pair = NULL; +#if MT32EMU_ACCURATENOTES == 1 +	for (int i = 0; i < 3; i++) { +		noteLookupStorage.waveforms[i] = new Bit16s[65536]; +	} +	noteLookup = ¬eLookupStorage; +#endif  }  Partial::~Partial() { +#if MT32EMU_ACCURATENOTES == 1 +	for (int i = 0; i < 3; i++) { +		delete[] noteLookupStorage.waveforms[i]; +	} +#endif  }  int Partial::getOwnerPart() { @@ -68,55 +84,49 @@ void Partial::deactivate() {  void Partial::initKeyFollow(int key) {  	// Setup partial keyfollow  	// Note follow relative to middle C -	int keyfollow; -	int realfol = (key * 2 - MIDDLEC * 2) / 2; -	int antirealfol = (MIDDLEC * 2 - key * 2) / 2; +  	// Calculate keyfollow for pitch -	switch(patchCache->pitchkeydir) { -	case -1: -		keyfollow = (antirealfol * patchCache->pitchkeyfollow) >> 12; -		break; -	case 0: -		keyfollow =  0; -		break; -	case 1: -		keyfollow = (realfol * patchCache->pitchkeyfollow) >> 12; -		break; -	default: -		keyfollow = 0; // Please the compiler -	} -	if ((patchCache->pitchkeyfollow>4096) && (patchCache->pitchkeyfollow<4200)) { -		// Be sure to round up on keys below MIDDLEC -		if (realfol < 0) -			keyfollow++; +#if 1 +	float rel = key == -1 ? 0.0f : (key - MIDDLEC); +	float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch + patchCache->pitchShift; +	//FIXME:KG: Does it truncate the keyfollowed pitch to a semitone (towards MIDDLEC)? +	//int newKey = (int)(rel * patchCache->pitchKeyfollow); +	//float newPitch = newKey + patchCache->pitch + patchCache->pitchShift; +#else +	float rel = key == -1 ? 0.0f : (key + patchCache->pitchShift - MIDDLEC); +	float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch; +#endif +#if MT32EMU_ACCURATENOTES == 1 +	noteVal = newPitch; +	synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f", key, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, newPitch); +#else +	float newPitchInt; +	float newPitchFract = modff(newPitch, &newPitchInt); +	synth->printDebug("Really: newPitch=%f, newPitchInt=%f, newPitchFract=%f", newPitch, newPitchInt, newPitchFract); +	if (newPitchFract > 0.5f) { +		newPitchInt += 1.0f; +		newPitchFract -= 1.0f;  	} -	noteVal = (keyfollow + patchCache->pitchshift); -	if (noteVal > 108) -		noteVal = 108; -	if (noteVal < 12) -		noteVal = 12; - +	noteVal = (int)newPitchInt; +	fineShift = (int)(powf(2.0f, newPitchFract / 12.0f) * 4096.0f); +	synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f, noteVal=%d, fineShift=%d", key, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, newPitch, noteVal, fineShift); +#endif +	// FIXME:KG: Raise/lower by octaves until in the supported range. +	while (noteVal > HIGHEST_NOTE) // FIXME:KG: see tables.cpp: >108? +		noteVal -= 12; +	while (noteVal < LOWEST_NOTE) // FIXME:KG: see tables.cpp: <12? +		noteVal += 12;  	// Calculate keyfollow for filter -	switch(patchCache->keydir) { -	case -1: -		keyfollow = (antirealfol * patchCache->filtkeyfollow) >> 12; -		break; -	case 0: -		keyfollow = key; -		break; -	case 1: -		keyfollow = (realfol * patchCache->filtkeyfollow) >> 12; -		break; -	} +	int keyfollow = ((key - MIDDLEC) * patchCache->filtkeyfollow) / 4096;  	if (keyfollow > 108)  		keyfollow = 108;  	if (keyfollow < -108)  		keyfollow = -108;  	filtVal = keytable[keyfollow + 108]; -	realVal = keytable[realfol + 108]; +	realVal = keytable[(key - MIDDLEC) + 108];  } -void Partial::startPartial(dpoly *usePoly, PatchCache *useCache, Partial *pairPartial) { +void Partial::startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial) {  	if (usePoly == NULL || useCache == NULL) {  		synth->printDebug("*** Error: Starting partial for owner %d, usePoly=%s, useCache=%s", ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", useCache == NULL ? "*** NULL ***" : "OK");  		return; @@ -128,11 +138,11 @@ void Partial::startPartial(dpoly *usePoly, PatchCache *useCache, Partial *pairPa  	play = true;  	initKeyFollow(poly->freqnum); // Initialises noteVal, filtVal and realVal -	noteLookup = ¬eLookups[noteVal]; - -	// FIXME:KG: I think most places should refer to noteVal/noteLookup instead of these. -	keyVal = poly->freqnum; -	keyLookup = ¬eLookups[poly->freqnum]; +#if MT32EMU_ACCURATENOTES == 0 +	noteLookup = ¬eLookups[noteVal - LOWEST_NOTE]; +#else +	TableInitialiser::initNote(synth, ¬eLookupStorage, noteVal, (float)synth->myProp.sampleRate, synth->masterTune, synth->PCMList, NULL); +#endif  	lfoPos = 0;  	pulsewidth = patchCache->pulsewidth + pwveltable[patchCache->pwsens][poly->vel]; @@ -154,8 +164,8 @@ void Partial::startPartial(dpoly *usePoly, PatchCache *useCache, Partial *pairPa  		envs[e].counter = 0;  		envs[e].count = 0;  	} -	ampEnvCache = 0; -	pitchEnvCache = 0; +	ampEnvVal = 0; +	pitchEnvVal = 0;  	pitchSustain = false;  	loopPos = 0;  	partialOff.pcmoffset = partialOff.pcmplace = 0; @@ -181,22 +191,16 @@ Bit16s *Partial::generateSamples(long length) {  	Bit16s *partialBuf = &myBuffer[0];  	while (length--) { -		Bit32s envval, ampval; -		Bit32s ptemp = 0; -		if (envs[EnvelopeType_amp].sustaining) -			ampval = ampEnvCache; -		else { +		Bit32s envval; +		Bit32s sample = 0; +		if (!envs[EnvelopeType_amp].sustaining) {  			if (envs[EnvelopeType_amp].count <= 0) { -				ampval = getAmpEnvelope(); +				Bit32u ampval = getAmpEnvelope();  				if (!play) {  					deactivate();  					break;  				} -				if (ampval < 0) { -					//TODO: check what is going on here -					synth->printDebug("ampval<0! ampval=%ld, active=%d", ampval, isActive()); -					ampval = 0; -				} else if (ampval > 127) { +				if (ampval > 127) {  					ampval = 127;  				} @@ -208,15 +212,12 @@ Bit16s *Partial::generateSamples(long length) {  					tmpvel = poly->vel;  				ampval = (ampval * ampveltable[tmpvel][(int)patchCache->ampEnv.velosens]) >> 8;  				//if (envs[EnvelopeType_amp].sustaining) -				ampEnvCache = ampval; -			} else -				ampval = ampEnvCache; +				ampEnvVal = ampval; +			}  			--envs[EnvelopeType_amp].count;  		} -		// Calculate Pitch envelope  		int lfoat = 0x1000; -		int pdep;  		if (pitchSustain) {  			// Calculate LFO position  			// LFO does not kick in completely until pitch envelope sustains @@ -224,17 +225,15 @@ Bit16s *Partial::generateSamples(long length) {  				lfoPos++;  				if (lfoPos >= patchCache->lfoperiod)  					lfoPos = 0; -				int lfoatm = (lfoPos << 16) / patchCache->lfoperiod; +				int lfoatm = FIXEDPOINT_UDIV(lfoPos, patchCache->lfoperiod, 16);  				int lfoatr = sintable[lfoatm];  				lfoat = lfoptable[patchCache->lfodepth][lfoatr];  			} -			pdep = pitchEnvCache;  		} else { +			// Calculate Pitch envelope  			envval = getPitchEnvelope();  			int pd = patchCache->pitchEnv.depth; -			pdep = penvtable[pd][envval]; -			if (pitchSustain) -				pitchEnvCache = pdep; +			pitchEnvVal = penvtable[pd][envval];  		}  		int delta; @@ -242,36 +241,23 @@ Bit16s *Partial::generateSamples(long length) {  		PCMWaveEntry *pcmWave = NULL; // Initialise to please compiler  		int pcmAddr = 0; // Initialise to please compiler -		// Get waveform - either PCM or synthesized sawtooth or square +		// Wrap positions or end if necessary  		if (patchCache->PCMPartial) {  			// PCM partial  			int len;  			pcmWave = &synth->PCMList[patchCache->pcm]; -			if (pcmWave->aggSound == -1) { -				delta = noteLookup->wavTable[pcmWave->pcmnum]; -				pcmAddr = pcmWave->addr; -				len = pcmWave->len; -				if (partialOff.pcmplace >= len) { -					if (pcmWave->loop) { -						partialOff.pcmplace = partialOff.pcmoffset = 0; -						// FIXME:KG: Use this?: partialOff.pcmplace %= len; -					} else { -						play = false; -						deactivate(); -						break; -					} -				} -			} else { -				int tmppcm = LoopPatterns[pcmWave->aggSound][loopPos]; -				delta = noteLookup->loopTable[pcmWave->aggSound][loopPos]; -				pcmAddr = synth->PCM[tmppcm].addr; -				len = synth->PCM[tmppcm].len; -				if (partialOff.pcmplace >= len) { -					loopPos++; -					if (LoopPatterns[pcmWave->aggSound][loopPos] == -1) -						loopPos = 0; -					partialOff.pcmplace = partialOff.pcmoffset = 0; +			delta = noteLookup->wavTable[patchCache->pcm]; +			pcmAddr = pcmWave->addr; +			len = pcmWave->len; +			if (partialOff.pcmplace >= len) { +				if (pcmWave->loop) { +					//partialOff.pcmplace = partialOff.pcmoffset = 0; +					partialOff.pcmplace %= len; +				} else { +					play = false; +					deactivate(); +					break;  				}  			}  		} else { @@ -282,14 +268,18 @@ Bit16s *Partial::generateSamples(long length) {  		// Build delta for position of next sample  		// Fix delta code -		Bit64s tdelta = (Bit64s)delta; -		tdelta = (tdelta * patchCache->fineshift) >> 12; -		tdelta = (tdelta * pdep) >> 12; +		Bit64u tdelta = (Bit64u)delta; +#if MT32EMU_ACCURATENOTES == 0 +		tdelta = (tdelta * fineShift) >> 12; +#endif +		tdelta = (tdelta * pitchEnvVal) >> 12;  		tdelta = (tdelta * lfoat) >> 12;  		tdelta = (tdelta * bendShift) >> 12;  		delta = (int)tdelta; +		Bit32u volume = *poly->volumeptr; -		if (ampval > 0) { +		// Get waveform - either PCM or synthesized sawtooth or square +		if (ampEnvVal > 0) {  			if (patchCache->PCMPartial) {  				// Render PCM sample  				int ra, rb, dist; @@ -301,7 +291,7 @@ Bit16s *Partial::generateSamples(long length) {  					//FIXME:KG: Deal with condition that taddr + 1 is past PCM length  					rb = synth->romfile[taddr + 1];  					dist = rb - ra; -					ptemp = (ra + ((dist * (Bit32s)(partialOff.pcmoffset >> 8)) >> 8)); +					sample = (ra + ((dist * (Bit32s)(partialOff.pcmoffset >> 8)) >> 8));  				} else {  					// Sound decimation  					// The right way to do it is to use a lowpass filter on the waveform before selecting @@ -311,11 +301,11 @@ Bit16s *Partial::generateSamples(long length) {  					ra = 0;  					for (int ix = 0; ix < idelta; ix++)  						ra += synth->romfile[taddr++]; -					ptemp = ra / idelta; +					sample = ra / idelta;  				}  			} else {  				// Render synthesised sample -				int div = noteLookup->div; +				Bit32u div = noteLookup->div;  				int wf = patchCache->waveform;  				int toff = partialOff.pcmplace;  				int minorplace = partialOff.pcmoffset >> 14; @@ -328,26 +318,23 @@ Bit16s *Partial::generateSamples(long length) {  					// Square waveform.  Made by combining two pregenerated bandlimited  					// sawtooth waveforms  					// Pulse width is not yet correct -					int pa, pb; -					int divmark = div << 8; -  					if (div == 0) {  						synth->printDebug("ERROR: div=0 generating square wave, this should never happen!");  						div = 1;  					} -					int ofsA = toff % div; -					int ofsB = toff + ((divmark * pulsetable[pulsewidth]) >> 16); +					Bit32u ofsA = toff % div; +					Bit32u ofsB = toff + FIXEDPOINT_UMULT(div, pulsetable[pulsewidth], 8);  					ofsB = ofsB % div; -					pa = noteLookup->waveforms[0][(ofsA << 2) + minorplace]; -					pb = noteLookup->waveforms[0][(ofsB << 2) + minorplace]; -					ptemp = (pa - pb) * 4; +					Bit16s pa = noteLookup->waveforms[0][(ofsA << 2) + minorplace]; +					Bit16s pb = noteLookup->waveforms[0][(ofsB << 2) + minorplace]; +					sample = (pa - pb) * 4;  					// Non-bandlimited squarewave  					/*  					ofs = ((div << 1) * pulsetable[patchCache->pulsewidth]) >> 8;  					if (toff < ofs) -						ptemp = 1 * WGAMP; +						sample = 1 * WGAMP;  					else -						ptemp = -1 * WGAMP; +						sample = -1 * WGAMP;  					*/  				} else {  					// Sawtooth.  Made by combining the full cosine and half cosine according @@ -355,33 +342,32 @@ Bit16s *Partial::generateSamples(long length) {  					// square wave and multiplies it by a full cosine  					int waveoff = (toff << 2) + minorplace;  					if (toff < noteLookup->sawTable[pulsewidth]) -						ptemp = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]]; +						sample = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]];  					else -						ptemp = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]]; -					ptemp = ptemp * 4; +						sample = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]]; +					sample = sample * 4;  					// This is the correct way  					// Seems slow to me (though bandlimited) -- doesn't seem to  					// sound any better though  					/* -					int divmark = noteLookup->div << 8;  					//int pw = (patchCache->pulsewidth * pulsemod[filtval]) >> 8; -					int ofs = toff % div; +					Bit32u ofs = toff % div; -					int ofs3 = toff + ((divmark * pulsetable[patchCache->pulsewidth]) >> 16); +					Bit32u ofs3 = toff + FIXEDPOINT_UMULT(div, pulsetable[patchCache->pulsewidth], 8);  					ofs3 = ofs3 % div;  					pa = noteLookup->waveforms[0][ofs];  					pb = noteLookup->waveforms[0][ofs3]; -					ptemp = ((pa - pb) * noteLookup->waveforms[2][toff]) / WGAMP; -					ptemp = ptemp *4; +					sample = ((pa - pb) * noteLookup->waveforms[2][toff]) / WGAMP; +					sample = sample *4;  					*/  				}  				//Very exact filter  				if (filtval > ((FILTERGRAN * 15) / 16))  					filtval = ((FILTERGRAN * 15) / 16); -				ptemp = (Bit32s)floor((usefilter)((float)ptemp, &history[0], filtcoeff[filtval][(int)patchCache->filtEnv.resonance], patchCache->filtEnv.resonance)); +				sample = (Bit32s)floor((synth->iirFilter)((float)sample, &history[0], filtcoeff[filtval][(int)patchCache->filtEnv.resonance], patchCache->filtEnv.resonance));  			}  		} @@ -392,14 +378,13 @@ Bit16s *Partial::generateSamples(long length) {  		partialOff.pcmoffset = (Bit16u)(absOff & 0xFFFF);  		// Put volume envelope over generated sample -		ptemp = (ptemp * ampval) >> 9; -		ptemp = (ptemp * *poly->volumeptr) >> 7; - +		sample = FIXEDPOINT_SMULT(sample, ampEnvVal, 9); +		sample = FIXEDPOINT_SMULT(sample, volume, 7);  		envs[EnvelopeType_amp].envpos++;  		envs[EnvelopeType_pitch].envpos++;  		envs[EnvelopeType_filt].envpos++; -		*partialBuf++ = (Bit16s)ptemp; +		*partialBuf++ = (Bit16s)sample;  	}  	// We may have deactivated and broken out of the loop before the end of the buffer,  	// if so then fill the remainder with 0s. @@ -414,9 +399,10 @@ void Partial::setBend(float factor) {  		return;  	}  	// NOTE:KG: We can't do this smoothly with lookup tables, unless we use several MB. +	// FIXME:KG: Bend should be influenced by pitch key-follow too, according to docs.  	float bendSemitones = factor * patchCache->benderRange; // -24 .. 24  	float mult = powf(2.0f, bendSemitones / 12.0f); -	synth->printDebug("setBend(): semitones=%f, mult=%f, factor=%f, benderRange=%d\n", bendSemitones, mult, factor, patchCache->benderRange); +	synth->printDebug("setBend(): factor=%f, benderRange=%f, semitones=%f, mult=%f\n", factor, patchCache->benderRange, bendSemitones, mult);  	bendShift = (int)(mult * 4096.0f);  } @@ -601,8 +587,8 @@ bool Partial::produceOutput(Bit16s *partialBuf, long length) {  		return false;  	Bit16s leftvol, rightvol; -	leftvol = poly->pansetptr->leftvol; -	rightvol = poly->pansetptr->rightvol; +	leftvol = patchCache->pansetptr->leftvol; +	rightvol = patchCache->pansetptr->rightvol;  #if MT32EMU_USE_MMX >= 2  	// FIXME:KG: This appears to introduce crackle @@ -624,7 +610,7 @@ Bit32s Partial::getFiltEnvelope() {  	int cutoff,depth,keyfollow, realfollow; -	envstatus *tStat  = &envs[EnvelopeType_filt]; +	EnvelopeStatus *tStat  = &envs[EnvelopeType_filt];  	keyfollow = filtVal;  	realfollow = realVal; @@ -651,7 +637,7 @@ Bit32s Partial::getFiltEnvelope() {  				if (tStat->envstat == 3) {  					tStat->envsize = lasttimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]];  				} else { -					tStat->envsize = (envtimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]] * keyLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8; +					tStat->envsize = (envtimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8;  				}  				tStat->envsize++; @@ -678,16 +664,17 @@ Bit32s Partial::getFiltEnvelope() {  	int dist;  	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 (keyVal < bias) { -				dist = bias - keyVal; +			if (noteVal < bias) { +				dist = bias - noteVal;  				cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8;  			}  		} else {  			// > Bias -			if (keyVal > bias) { -				dist = keyVal - bias; +			if (noteVal > bias) { +				dist = noteVal - bias;  				cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8;  			} @@ -695,7 +682,7 @@ Bit32s Partial::getFiltEnvelope() {  		//synth->printDebug("Cutoff after %d", cutoff);  	} -	depth = (depth * keyLookup->fildepTable[patchCache->tvfdepth]) >> 8; +	depth = (depth * noteLookup->fildepTable[patchCache->tvfdepth]) >> 8;  	reshigh = (reshigh * depth) >> 7;  	Bit32s tmp; @@ -714,7 +701,7 @@ Bit32s Partial::getFiltEnvelope() {  		reshigh = 100;  	else if (reshigh<0)  		reshigh = 0; -	tmp = keyLookup->nfiltTable[cutoff][reshigh]; +	tmp = noteLookup->nfiltTable[cutoff][reshigh];  	//tmp *= keyfollow;  	//tmp /= realfollow; @@ -725,13 +712,13 @@ Bit32s Partial::getFiltEnvelope() {  bool Partial::shouldReverb() {  	if (!isActive())  		return false; -	return poly->reverb; +	return patchCache->reverb;  } -Bit32s Partial::getAmpEnvelope() { -	Bit32s tc; +Bit32u Partial::getAmpEnvelope() { +	Bit32u tc; -	envstatus *tStat = &envs[EnvelopeType_amp]; +	EnvelopeStatus *tStat = &envs[EnvelopeType_amp];  	if (!play)  		return 0; @@ -780,7 +767,7 @@ Bit32s Partial::getAmpEnvelope() {  			default:  				//Spot for timekey follow  				//Only used in subsquent envelope parameters, including the decay -				tStat->envsize = (envtimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]] * keyLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8; +				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);  				break; @@ -809,21 +796,19 @@ PastCalc:  	//Bias level crap stuff now -	int dist, bias; -  	for (int i = 0; i < 2; i++) {  		if (patchCache->ampblevel[i]!=0) { -			bias = patchCache->ampbias[i]; +			int bias = patchCache->ampbias[i];  			if (patchCache->ampdir[i]==0) {  				// < Bias -				if (keyVal < bias) { -					dist = bias - keyVal; +				if (noteVal < bias) { +					int dist = bias - noteVal;  					tc = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8;  				}  			} else {  				// > Bias -				if (keyVal > bias) { -					dist = keyVal - bias; +				if (noteVal > bias) { +					int dist = noteVal - bias;  					tc = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8;  				}  			} @@ -833,7 +818,7 @@ PastCalc:  }  Bit32s Partial::getPitchEnvelope() { -	envstatus *tStat = &envs[EnvelopeType_pitch]; +	EnvelopeStatus *tStat = &envs[EnvelopeType_pitch];  	Bit32s tc;  	pitchSustain = false; @@ -856,7 +841,7 @@ Bit32s Partial::getPitchEnvelope() {  				tStat->envstat++;  				tStat->envbase = patchCache->pitchEnv.level[tStat->envstat]; -				tStat->envsize = (envtimetable[(int)patchCache->pitchEnv.time[tStat->envstat]] * keyLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8; +				tStat->envsize = (envtimetable[(int)patchCache->pitchEnv.time[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8;  				tStat->envpos = 0;  				tStat->envsize++; @@ -878,7 +863,7 @@ void Partial::startDecayAll() {  }  void Partial::startDecay(EnvelopeType envnum, Bit32s startval) { -	envstatus *tStat  = &envs[envnum]; +	EnvelopeStatus *tStat  = &envs[envnum];  	tStat->sustaining = false;  	tStat->decaying = true; @@ -887,15 +872,15 @@ void Partial::startDecay(EnvelopeType envnum, Bit32s startval) {  	switch(envnum) {  	case EnvelopeType_amp: -		tStat->envsize = (decaytimetable[(int)patchCache->ampEnv.envtime[4]] * keyLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8; +		tStat->envsize = (decaytimetable[(int)patchCache->ampEnv.envtime[4]] * noteLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8;  		tStat->envdist = -startval;  		break;  	case EnvelopeType_filt: -		tStat->envsize = (decaytimetable[(int)patchCache->filtEnv.envtime[4]] * keyLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8; +		tStat->envsize = (decaytimetable[(int)patchCache->filtEnv.envtime[4]] * noteLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8;  		tStat->envdist = -startval;  		break;  	case EnvelopeType_pitch: -		tStat->envsize = (decaytimetable[(int)patchCache->pitchEnv.time[3]] * keyLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8 ; +		tStat->envsize = (decaytimetable[(int)patchCache->pitchEnv.time[3]] * noteLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8 ;  		tStat->envdist = patchCache->pitchEnv.level[4] - startval;  		break;  	default: diff --git a/backends/midi/mt32/partial.h b/backends/midi/mt32/partial.h index 2760b8fa2a..52b07bfeb1 100644 --- a/backends/midi/mt32/partial.h +++ b/backends/midi/mt32/partial.h @@ -33,7 +33,7 @@ enum EnvelopeType {  	EnvelopeType_pitch = 2  }; -struct envstatus { +struct EnvelopeStatus {  	Bit32s envpos;  	Bit32s envstat;  	Bit32s envbase; @@ -51,7 +51,7 @@ struct envstatus {  // Class definition of MT-32 partials.  32 in all.  class Partial {  private: -	Synth *synth; // Only used for sending debug output +	Synth *synth;  	int ownerPart; // -1 if unassigned  	int mixType; @@ -63,25 +63,28 @@ private:  	bool play;  	// Keyfollowed note value +#if MT32EMU_ACCURATENOTES == 1 +	NoteLookup noteLookupStorage; +	float noteVal; +#else  	int noteVal; -	NoteLookup *noteLookup; // Lookup stuff for this noteVal - -	int keyVal; -	NoteLookup *keyLookup; +	int fineShift; +#endif +	const NoteLookup *noteLookup; // LUTs for this noteVal  	// Keyfollowed filter values  	int realVal;  	int filtVal; -	envstatus envs[3]; +	EnvelopeStatus envs[3];  	int pulsewidth;  	Bit32u lfoPos;  	soundaddr partialOff; -	Bit32u ampEnvCache; -	Bit32u pitchEnvCache; +	Bit32u ampEnvVal; +	Bit32u pitchEnvVal;  	float history[32]; @@ -93,19 +96,19 @@ private:  	int bendShift; -	Bit16s *mixBuffers(Bit16s * buf1, Bit16s * buf2, int len); -	Bit16s *mixBuffersRingMix(Bit16s * buf1, Bit16s * buf2, int len); -	Bit16s *mixBuffersRing(Bit16s * buf1, Bit16s * buf2, int len); -	void mixBuffersStereo(Bit16s * buf1, Bit16s * buf2, Bit16s * outBuf, int len); +	Bit16s *mixBuffers(Bit16s *buf1, Bit16s *buf2, int len); +	Bit16s *mixBuffersRingMix(Bit16s *buf1, Bit16s *buf2, int len); +	Bit16s *mixBuffersRing(Bit16s *buf1, Bit16s *buf2, int len); +	void mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len);  	Bit32s getFiltEnvelope(); -	Bit32s getAmpEnvelope(); +	Bit32u getAmpEnvelope();  	Bit32s getPitchEnvelope();  	void initKeyFollow(int freqNum);  public: -	PatchCache *patchCache; +	const PatchCache *patchCache;  	PatchCache cachebackup;  	Partial *pair; @@ -119,7 +122,7 @@ public:  	bool isActive();  	void activate(int part);  	void deactivate(void); -	void startPartial(dpoly *usePoly, PatchCache *useCache, Partial *pairPartial); +	void startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial);  	void startDecay(EnvelopeType envnum, Bit32s startval);  	void startDecayAll();  	void setBend(float factor); diff --git a/backends/midi/mt32/partialManager.cpp b/backends/midi/mt32/partialManager.cpp index 4c9d264128..01fb57b026 100644 --- a/backends/midi/mt32/partialManager.cpp +++ b/backends/midi/mt32/partialManager.cpp @@ -36,7 +36,7 @@ PartialManager::~PartialManager(void) {  		delete partialTable[i];  } -void PartialManager::GetPerPartPartialUsage(int usage[9]) { +void PartialManager::getPerPartPartialUsage(int usage[9]) {  	memset(usage, 0, 9 * sizeof (int));  	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {  		if (partialTable[i]->isActive()) @@ -44,12 +44,12 @@ void PartialManager::GetPerPartPartialUsage(int usage[9]) {  	}  } -void PartialManager::ClearAlreadyOutputed() { +void PartialManager::clearAlreadyOutputed() {  	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)  		partialTable[i]->alreadyOutputed = false;  } -void PartialManager::AgeAll() { +void PartialManager::ageAll() {  	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)  		partialTable[i]->age++;  } @@ -58,28 +58,28 @@ bool PartialManager::shouldReverb(int i) {  	return partialTable[i]->shouldReverb();  } -bool PartialManager::ProduceOutput(int i, Bit16s *buffer, Bit32u bufferLength) { +bool PartialManager::produceOutput(int i, Bit16s *buffer, Bit32u bufferLength) {  	return partialTable[i]->produceOutput(buffer, bufferLength);  } -void PartialManager::DeactivateAll() { +void PartialManager::deactivateAll() {  	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {  		partialTable[i]->deactivate();  	}  } -unsigned int PartialManager::SetReserve(char *rset) { +unsigned int PartialManager::setReserve(Bit8u *rset) {  	unsigned int pr = 0;  	for (int x = 0; x < 9; x++) {  		for (int y = 0; y < rset[x]; y++) { -			PartialReserveTable[pr] = x; +			partialReserveTable[pr] = x;  			pr++;  		}  	}  	return pr;  } -Partial * PartialManager::AllocPartial(int partNum) { +Partial *PartialManager::allocPartial(int partNum) {  	Partial *outPartial = NULL;  	// Use the first inactive partial reserved for the specified part (if there are any) @@ -87,7 +87,7 @@ Partial * PartialManager::AllocPartial(int partNum) {  	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {  		if (!partialTable[i]->isActive()) {  			outPartial = partialTable[i]; -			if (PartialReserveTable[i] == partNum) +			if (partialReserveTable[i] == partNum)  				break;  		}  	} @@ -98,7 +98,7 @@ Partial * PartialManager::AllocPartial(int partNum) {  	return outPartial;  } -unsigned int PartialManager::GetFreePartialCount(void) { +unsigned int PartialManager::getFreePartialCount(void) {  	int count = 0;  	memset(partialPart, 0, sizeof(partialPart));  	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { @@ -111,9 +111,9 @@ unsigned int PartialManager::GetFreePartialCount(void) {  }  /* -bool PartialManager::FreePartials(unsigned int needed, int partNum) { +bool PartialManager::freePartials(unsigned int needed, int partNum) {  	int i; -	int myPartPrior = (int)mt32ram.params.system.reserveSettings[partNum]; +	int myPartPrior = (int)mt32ram.system.reserveSettings[partNum];  	if (myPartPrior<partialPart[partNum]) {  		//This can have more parts, must kill off those with less priority  		int most, mostPart; @@ -125,7 +125,7 @@ bool PartialManager::FreePartials(unsigned int needed, int partNum) {  			int diff;  			for (i=0;i<9;i++) { -				diff = partialPart[i] - (int)mt32ram.params.system.reserveSettings[i]; +				diff = partialPart[i] - (int)mt32ram.system.reserveSettings[i];  				if (diff>0) {  					if (diff>most) { @@ -143,7 +143,7 @@ bool PartialManager::FreePartials(unsigned int needed, int partNum) {  			bool found;  			int oldest;  			int oldnum; -			while (partialPart[selectPart] > (int)mt32ram.params.system.reserveSettings[selectPart]) { +			while (partialPart[selectPart] > (int)mt32ram.system.reserveSettings[selectPart]) {  				oldest = -1;  				oldnum = -1;  				found = false; @@ -198,7 +198,7 @@ bool PartialManager::FreePartials(unsigned int needed, int partNum) {  }  */ -bool PartialManager::FreePartials(unsigned int needed, int partNum) { +bool PartialManager::freePartials(unsigned int needed, int partNum) {  	if (needed == 0) {  		return true;  	} @@ -206,7 +206,7 @@ bool PartialManager::FreePartials(unsigned int needed, int partNum) {  	// Kill those that are already decaying first  	/*  	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { -		if (PartialReserveTable[i] == partNum) { +		if (partialReserveTable[i] == partNum) {  			if (partialTable[i]->ownerPart != partNum) {  				if (partialTable[i]->partCache->envs[AMPENV].decaying) {  					partialTable[i]->isActive = false; @@ -223,10 +223,10 @@ bool PartialManager::FreePartials(unsigned int needed, int partNum) {  		int priornum = -1;  		for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { -			if (PartialReserveTable[i] == partNum && partialTable[i]->isActive() && partialTable[i]->getOwnerPart() != partNum) { +			if (partialReserveTable[i] == partNum && partialTable[i]->isActive() && partialTable[i]->getOwnerPart() != partNum) {  				/* -				if (mt32ram.params.system.reserveSettings[partialTable[i]->ownerPart] < prior) { -					prior = mt32ram.params.system.reserveSettings[partialTable[i]->ownerPart]; +				if (mt32ram.system.reserveSettings[partialTable[i]->ownerPart] < prior) { +					prior = mt32ram.system.reserveSettings[partialTable[i]->ownerPart];  					priornum = i;  				}*/  				if (partialTable[i]->age > prior) { @@ -244,17 +244,14 @@ bool PartialManager::FreePartials(unsigned int needed, int partNum) {  	}  	// Kill off the oldest partials within this part -  	while (needed > 0) {  		Bit64s oldest = -1;  		Bit64s oldlist = -1;  		for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { -			if (partialTable[i]->isActive()) { -				if (partialTable[i]->getOwnerPart() == partNum) { -					if (partialTable[i]->age > oldest) { -						oldest = partialTable[i]->age; -						oldlist = i; -					} +			if (partialTable[i]->getOwnerPart() == partNum && partialTable[i]->isActive()) { +				if (partialTable[i]->age > oldest) { +					oldest = partialTable[i]->age; +					oldlist = i;  				}  			}  		} diff --git a/backends/midi/mt32/partialManager.h b/backends/midi/mt32/partialManager.h index 7902a3cdc5..ee145d4c9f 100644 --- a/backends/midi/mt32/partialManager.h +++ b/backends/midi/mt32/partialManager.h @@ -31,22 +31,22 @@ private:  	Synth *synth; // Only used for sending debug output  	Partial *partialTable[MT32EMU_MAX_PARTIALS]; -	Bit32s PartialReserveTable[MT32EMU_MAX_PARTIALS]; +	Bit32s partialReserveTable[MT32EMU_MAX_PARTIALS];  	Bit32s partialPart[9]; // The count of partials played per part  public:  	PartialManager(Synth *synth);  	~PartialManager(); -	Partial *AllocPartial(int partNum); -	unsigned int GetFreePartialCount(void); -	bool FreePartials(unsigned int needed, int partNum); -	unsigned int SetReserve(char *rset); -	void DeactivateAll(); -	void AgeAll(); -	bool ProduceOutput(int i, Bit16s *buffer, Bit32u bufferLength); +	Partial *allocPartial(int partNum); +	unsigned int getFreePartialCount(void); +	bool freePartials(unsigned int needed, int partNum); +	unsigned int setReserve(Bit8u *rset); +	void deactivateAll(); +	void ageAll(); +	bool produceOutput(int i, Bit16s *buffer, Bit32u bufferLength);  	bool shouldReverb(int i); -	void ClearAlreadyOutputed(); -	void GetPerPartPartialUsage(int usage[9]); +	void clearAlreadyOutputed(); +	void getPerPartPartialUsage(int usage[9]);  };  } diff --git a/backends/midi/mt32/structures.h b/backends/midi/mt32/structures.h index d64f0a44f2..047f69f8bd 100644 --- a/backends/midi/mt32/structures.h +++ b/backends/midi/mt32/structures.h @@ -56,92 +56,92 @@ typedef   signed char      Bit8s;  struct TimbreParam {  	struct commonParam {  		char name[10]; -		char pstruct12;  // 1&2  0-12 (1-13) -		char pstruct34;  // #3&4  0-12 (1-13) -		char pmute;  // 0-15 (0000-1111) -		char nosustain; // 0-1(Normal, No sustain) +		Bit8u pstruct12;  // 1&2  0-12 (1-13) +		Bit8u pstruct34;  // #3&4  0-12 (1-13) +		Bit8u pmute;  // 0-15 (0000-1111) +		Bit8u nosustain; // 0-1(Normal, No sustain)  	} MT32EMU_ALIGN_PACKED common;  	struct partialParam {  		struct wgParam { -			char coarse;  // 0-96 (C1,C#1-C9) -			char fine;  // 0-100 (-50 to +50 (cents?)) -			char 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) -			char bender;  // 0,1 (ON/OFF) -			char waveform; //  0-1 (SQU/SAW) -			char pcmwave; // 0-127 (1-128) -			char pulsewid; // 0-100 -			char pwvelo; // 0-14 (-7 - +7) +			Bit8u coarse;  // 0-96 (C1,C#1-C9) +			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 pcmwave; // 0-127 (1-128) +			Bit8u pulsewid; // 0-100 +			Bit8u pwvelo; // 0-14 (-7 - +7)  		} MT32EMU_ALIGN_PACKED wg;  		struct envParam { -			char depth; // 0-10 -			char sensitivity; // 1-100 -			char timekeyfollow; // 0-4 -			char time[4]; // 1-100 -			char level[5]; // 1-100 (-50 - +50) +			Bit8u depth; // 0-10 +			Bit8u sensitivity; // 1-100 +			Bit8u timekeyfollow; // 0-4 +			Bit8u time[4]; // 1-100 +			Bit8u level[5]; // 1-100 (-50 - +50)  		} MT32EMU_ALIGN_PACKED env;  		struct lfoParam { -			char rate; // 0-100 -			char depth; // 0-100 -			char modsense; // 0-100 +			Bit8u rate; // 0-100 +			Bit8u depth; // 0-100 +			Bit8u modsense; // 0-100  		} MT32EMU_ALIGN_PACKED lfo;  		struct tvfParam { -			char cutoff; // 0-100 -			char resonance; // 0-30 -			char keyfollow; // 0-16 (-1,-1/2,1/4,0,1,1/8,1/4,3/8,1/2,5/8,3/2,7/8,1,5/4,3/2,2,s1,s2) -			char biaspoint; // 0-127 (<1A-<7C >1A-7C) -			char biaslevel; // 0-14 (-7 - +7) -			char envdepth; // 0-100 -			char envsense; // 0-100 -			char envdkf; // DEPTH KEY FOLL0W 0-4 -			char envtkf; // TIME KEY FOLLOW 0-4 -			char envtime[5]; // 1-100 -			char envlevel[4]; // 1-100 +			Bit8u cutoff; // 0-100 +			Bit8u resonance; // 0-30 +			Bit8u keyfollow; // 0-16 (-1,-1/2,1/4,0,1,1/8,1/4,3/8,1/2,5/8,3/2,7/8,1,5/4,3/2,2,s1,s2) +			Bit8u biaspoint; // 0-127 (<1A-<7C >1A-7C) +			Bit8u biaslevel; // 0-14 (-7 - +7) +			Bit8u envdepth; // 0-100 +			Bit8u envsense; // 0-100 +			Bit8u envdkf; // DEPTH KEY FOLL0W 0-4 +			Bit8u envtkf; // TIME KEY FOLLOW 0-4 +			Bit8u envtime[5]; // 1-100 +			Bit8u envlevel[4]; // 1-100  		} MT32EMU_ALIGN_PACKED tvf;  		struct tvaParam { -			char level; // 0-100 -			char velosens; // 0-100 -			char biaspoint1; // 0-127 (<1A-<7C >1A-7C) -			char biaslevel1; // 0-12 (-12 - 0) -			char biaspoint2; // 0-127 (<1A-<7C >1A-7C) -			char biaslevel2; // 0-12 (-12 - 0) -			char envtkf; // TIME KEY FOLLOW 0-4 -			char envvkf; // VELOS KEY FOLL0W 0-4 -			char envtime[5]; // 1-100 -			char envlevel[4]; // 1-100 +			Bit8u level; // 0-100 +			Bit8u velosens; // 0-100 +			Bit8u biaspoint1; // 0-127 (<1A-<7C >1A-7C) +			Bit8u biaslevel1; // 0-12 (-12 - 0) +			Bit8u biaspoint2; // 0-127 (<1A-<7C >1A-7C) +			Bit8u biaslevel2; // 0-12 (-12 - 0) +			Bit8u envtkf; // TIME KEY FOLLOW 0-4 +			Bit8u envvkf; // VELOS KEY FOLL0W 0-4 +			Bit8u envtime[5]; // 1-100 +			Bit8u envlevel[4]; // 1-100  		} MT32EMU_ALIGN_PACKED tva;  	} MT32EMU_ALIGN_PACKED partial[4];  } MT32EMU_ALIGN_PACKED;  struct PatchParam { -	char timbreGroup; // TIMBRE GROUP  0-3 (group A, group B, Memory, Rhythm) -	char timbreNum; // TIMBRE NUMBER 0-63 -	char keyShift; // KEY SHIFT 0-48 (-24 - +24 semitones) -	char fineTune; // FINE TUNE 0-100 (-50 - +50 cents) -	char benderRange; // BENDER RANGE 0-24 -	char assignMode;  // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4) -	char reverbSwitch;  // REVERB SWITCH 0-1 (OFF,ON) -	char dummy; // (DUMMY) +	Bit8u timbreGroup; // TIMBRE GROUP  0-3 (group A, group B, Memory, Rhythm) +	Bit8u timbreNum; // TIMBRE NUMBER 0-63 +	Bit8u keyShift; // KEY SHIFT 0-48 (-24 - +24 semitones) +	Bit8u fineTune; // FINE TUNE 0-100 (-50 - +50 cents) +	Bit8u benderRange; // BENDER RANGE 0-24 +	Bit8u assignMode;  // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4) +	Bit8u reverbSwitch;  // REVERB SWITCH 0-1 (OFF,ON) +	Bit8u dummy; // (DUMMY)  } MT32EMU_ALIGN_PACKED;  struct MemParams {  	struct PatchTemp {  		PatchParam patch; -		char outlevel; // OUTPUT LEVEL 0-100 -		char panpot; // PANPOT 0-14 (R-L) -		char dummyv[6]; +		Bit8u outlevel; // OUTPUT LEVEL 0-100 +		Bit8u panpot; // PANPOT 0-14 (R-L) +		Bit8u dummyv[6];  	} MT32EMU_ALIGN_PACKED patchSettings[8];  	struct RhythmTemp { -		char timbre; // TIMBRE  0-94 (M1-M64,R1-30,OFF) -		char outlevel; // OUTPUT LEVEL 0-100 -		char panpot; // PANPOT 0-14 (R-L) -		char reverbSwitch;  // REVERB SWITCH 0-1 (OFF,ON) -	} MT32EMU_ALIGN_PACKED rhythmSettings[64]; +		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...  	TimbreParam MT32EMU_ALIGN_PACKED timbreSettings[8]; @@ -149,58 +149,43 @@ struct MemParams {  	struct PaddedTimbre {  		TimbreParam timbre; -		char padding[10]; +		Bit8u padding[10];  	} MT32EMU_ALIGN_PACKED timbres[64 + 64 + 64 + 30]; // Group A, Group B, Memory, Rhythm  	struct SystemArea { -		char masterTune; // MASTER TUNE 0-127 432.1-457.6Hz -		char reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay) -		char reverbTime; // REVERB TIME 0-7 (1-8) -		char reverbLevel; // REVERB LEVEL 0-7 (1-8) -		char reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32 -		char chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF) -		char masterVol; // MASTER VOLUME 0-100 +		Bit8u masterTune; // MASTER TUNE 0-127 432.1-457.6Hz +		Bit8u reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay) +		Bit8u reverbTime; // REVERB TIME 0-7 (1-8) +		Bit8u reverbLevel; // REVERB LEVEL 0-7 (1-8) +		Bit8u reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32 +		Bit8u chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF) +		Bit8u masterVol; // MASTER VOLUME 0-100  	} MT32EMU_ALIGN_PACKED system;  };  struct MemBanks { -	char pTemp[8][sizeof(MemParams::PatchTemp)]; -	char rTemp[64][sizeof(MemParams::RhythmTemp)]; -	char tTemp[8][sizeof(TimbreParam)]; -	char patchBank[128][sizeof(PatchParam)]; -	char timbreBank[64 + 64 + 64 + 30][sizeof(MemParams::PaddedTimbre)]; -	char systemBank[sizeof(MemParams::SystemArea)]; +	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  }; -union MT32RAMFormat { -	MemParams params; -	MemBanks banks; -} MT32EMU_ALIGN_PACKED; -  #if defined(_MSC_VER) || defined (__MINGW32__)  #pragma pack(pop)  #else  #pragma pack()  #endif -struct PCMWave { -	char name[16]; -	Bit32u addr; -	Bit32u len; -	bool loop; -	float tune; -	Bit32s ampval; -}; -  struct PCMWaveEntry {  	Bit32u addr;  	Bit32u len; -	Bit32u pcmnum; +	double tune;  	bool loop; -	Bit32s aggSound; // This variable is for the last 9 PCM samples, which are actually loop combinations  };  struct soundaddr { @@ -221,17 +206,15 @@ struct PatchCache {  	char waveform;  	int pulsewidth;  	int pwsens; -	int pitchshift; -	int fineshift; + +	float pitch;  	int lfodepth;  	int lforate;  	Bit32u lfoperiod;  	int modsense; -	int keydir; -	int pitchkeyfollow; -	int pitchkeydir; +	float pitchKeyfollow;  	int filtkeyfollow; @@ -264,8 +247,12 @@ struct PatchCache {  	int structurePair;  	// The following fields are actually common to all partials in the timbre +	bool dirty;  	Bit32u partialCount;  	bool sustain; +	float pitchShift; +	bool reverb; +	const StereoVolume *pansetptr;  };  class Partial; // Forward reference for class defined in partial.h @@ -277,19 +264,17 @@ struct dpoly {  	int freqnum;  	int vel; -	bool reverb;  	bool isDecay; -	const Bit32s *volumeptr; -	const StereoVolume *pansetptr; +	const Bit32u *volumeptr;  	Partial *partials[4];  	bool pedalhold; // This marks keys that have been released on the keyboard, but are being held by the pedal  	bool sustain; -	bool isActive(); -	Bit64s getAge(); +	bool isActive() const; +	Bit64s getAge() const;  };  } diff --git a/backends/midi/mt32/synth.cpp b/backends/midi/mt32/synth.cpp index 9361c3bda9..28d09f4d4d 100644 --- a/backends/midi/mt32/synth.cpp +++ b/backends/midi/mt32/synth.cpp @@ -26,20 +26,10 @@  #include "mt32emu.h" -#if MT32EMU_BENCHMARK_FILTERS > 0 -#include <time.h> -#endif -  namespace MT32Emu {  const int MAX_SYSEX_SIZE = 512; -iir_filter_type usefilter; - -static const Bit8u InitPatches[8] = { -	68, 48, 95, 78, 41, 3, 110, 122 -}; -  // Maps MIDI channel numbers to MT-32 parts (not to be confused with "partials")  // This is the default (FIXME: the mapping from 11->9 is undocumented, is this correct?):  static const Bit8s InitChanTable[16] = { @@ -51,23 +41,6 @@ static const Bit8s InitChanTable[16] = {  //	0, 1, 2, 3, 4, 5, 6, 7, -1, 8, -1, -1, -1, -1, -1, -1  //}; -static int axtoi(char *str) { -	int result = 0; -	while (*str) { -		char ch = *str++; -		if (ch >= '0' && ch <= '9') -			ch -= '0'; -		else if (ch >= 'a' && ch <= 'f') -			ch = ch + 10 - 'a'; -		else if (ch >= 'A' && ch <= 'F') -			ch = ch + 10 - 'A'; -		else -			break; -		result = (result << 4) | ch; -	} -	return result; -} -  float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLevel) {  	float *hist2_ptr;  	float output,new_hist; @@ -103,7 +76,7 @@ float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLe  	return(output);  } -Bit8u Synth::calcSysexChecksum(Bit8u *data, Bit32u len, Bit8u checksum) { +Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) {  	for (unsigned int i = 0; i < len; i++) {  		checksum = checksum + data[i];  	} @@ -125,10 +98,11 @@ Synth::~Synth() {  	close(); // Make sure we're closed and everything is freed  } -void Synth::report(ReportType type, const void *data) { +int Synth::report(ReportType type, const void *data) {  	if (myProp.report != NULL) { -		myProp.report(myProp.userData, type, data); +		return myProp.report(myProp.userData, type, data);  	} +	return 0;  }  void Synth::printDebug(const char *fmt, ...) { @@ -143,15 +117,12 @@ void Synth::printDebug(const char *fmt, ...) {  	va_end(ap);  } -void Synth::initReverb(char newRevMode, char newRevTime) { +void Synth::initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel) {  	// FIXME:KG: I don't think it's necessary to recreate the reverbModel... Just set the parameters  	if (reverbModel != NULL)  		delete reverbModel;  	reverbModel = new revmodel(); -	curRevTime = newRevTime; -	curRevMode = newRevMode; -  	switch(newRevMode) {  	case 0:  		reverbModel->setroomsize(.1f); @@ -175,8 +146,8 @@ void Synth::initReverb(char newRevMode, char newRevTime) {  		break;  	}  	reverbModel->setdry(1); -	reverbModel->setwet((float)mt32ram.params.system.reverbLevel / 8.0f); -	reverbModel->setwidth((float)curRevTime / 8.0f); +	reverbModel->setwet((float)newRevLevel / 8.0f); +	reverbModel->setwidth((float)newRevTime / 8.0f);  }  File *Synth::openFile(const char *filename, File::OpenMode mode) { @@ -228,7 +199,7 @@ bool Synth::loadPreset(const char *filename) {  		if (inSys) {  			syslen++;  			if (c == 0xF7) { -				playSysex(&sysexBuf[0],syslen); +				playSysex(&sysexBuf[0], syslen);  				inSys = false;  				syslen = 0;  			} else if (syslen == MAX_SYSEX_SIZE) { @@ -245,231 +216,22 @@ bool Synth::loadPreset(const char *filename) {  	return rc;  } -bool Synth::loadDrums(const char *filename) { -	File *file = openFile(filename, File::OpenMode_read); -	if (file == NULL) { -		return false; -	} -	int drumnum = 0; -	for (;;) { -		//Read common area -		TimbreParam *timbre = &mt32ram.params.timbres[drumnum + 192].timbre; -		if (file->read(&timbre->common, 14) != 14) -			break; -		char drumname[11]; -		strncpy(drumname, timbre->common.name, 10); -		drumname[10] = 0; -		bool breakout = false; -		for (int t=0;t<4;t++) { -			if (((timbre->common.pmute >> t) & 0x1) == 0x1) { -				if (file->read(&timbre->partial[t], 58) != 58) { -					breakout = true; -					break; -				} -				//printDebug("Loaded drum #%d (%s) - t %d", drumnum, drumname, t); -			} -		} -		if (breakout) { -			break; -		} -		//printDebug("Loaded drum #%d (%s)", drumnum, drumname); -		drumnum++; -	} -	closeFile(file); -	return true; -} - -void Synth::dumpDrums(const char *filename) { -	File *file = openFile(filename, File::OpenMode_write); -	if (file == NULL) -		return; -	char dumbtext[10]; -	memset(dumbtext,0,10); -	for (int drumnum=0;drumnum<30;drumnum++) { -		// Sysex header -		if (!file->writeBit8u(0xf0)) -			break; -		if (!file->writeBit8u(0x41)) -			break; -		if (!file->writeBit8u(0x10)) -			break; -		if (!file->writeBit8u(0x16)) -			break; -		if (!file->writeBit8u(0x12)) -			break; - -		int useaddr = drumnum * 256; -		char lsb = (char)(useaddr & 0x7f); -		char isb = (char)((useaddr >> 7) & 0x7f); -		char msb = (char)(((useaddr >> 14) & 0x7f) | 0x08); -		//Address -		if (!file->writeBit8u(msb)) -			break; -		if (!file->writeBit8u(isb)) -			break; -		if (!file->writeBit8u(lsb)) -			break; - -		TimbreParam *timbre = &mt32ram.params.timbres[192 + drumnum].timbre; -		//Data -		if (file->write(&timbre->common,0xE) != 0xE) -			break; -		if (file->write(&timbre->partial[0],0x3A) != 0x3A) -			break; -		if (file->write(&timbre->partial[1],0x3A) != 0x3A) -			break; -		if (file->write(&timbre->partial[2],0x3A) != 0x3A) -			break; -		if (file->write(&timbre->partial[3],0x3A) != 0x3A) -			break; -		//Checksum -		unsigned char *dat = (unsigned char *)timbre; -		unsigned char checksum = calcSysexChecksum(dat, 246, msb + isb + lsb); -		if (!file->writeBit8u(checksum)) -			break; - -		//End of sysex -		if (!file->writeBit8u(0xf7)) -			break; -	} -	closeFile(file); -} - -bool Synth::loadPCMToROMMap(const char *filename) { -	File *file = openFile(filename, File::OpenMode_read); // Original specified text mode - +bool Synth::loadControlROM(const char *filename) { +	File *file = openFile(filename, File::OpenMode_read); // ROM File  	if (file == NULL) {  		return false;  	} +	bool rc = (file->read(controlROMData, sizeof(controlROMData)) == sizeof(controlROMData)); -	Bit32u PCMReassign[54]; -	for (int i = 0; i < 54; i++) { -		PCMReassign[i] = i; -		PCM[i].tune = 220.0f; -		PCM[i].ampval = 256; -	} -	//PCM[53].ampval = 128; - -	char tbuf[512]; -	char *cp; -	if (!file->readLine(tbuf,sizeof(tbuf))) { -		return false; -	} -	Bit32u patchstart = 0; //axtoi(tbuf); -	Bit32u patchend = 0; -	Bit32u patchcount = 0; -	bool rc = true; -	for (;;) { -		if (!file->readLine(tbuf,sizeof(tbuf))) { -			if (!file->isEOF()) { -				rc = false; -			} -			break; -		} -		cp = strtok(tbuf," \n\r"); -		PCM[patchcount].loop = false; -		if (cp != NULL) { -			patchend = axtoi(cp); -			cp = strtok(NULL," \n\r"); -			if (cp != NULL) { -				cp = strtok(NULL," \n\r"); -				if (cp != NULL) { -					strncpy(PCM[patchcount].name, cp, 15); -					PCM[patchcount].name[15] = 0; -					cp = strtok(NULL," \n\r"); -					if (cp !=NULL) { -						int newpcm = atoi(cp); -						PCMReassign[newpcm] = patchcount; -						cp = strtok(NULL," \n\r"); -						if (cp != NULL) { -							if (atoi(cp)==1) -								PCM[patchcount].loop = true; -							cp = strtok(NULL," \n\r"); -							if (cp != NULL) { -								PCM[patchcount].tune = (float)atoi(cp) / 100.0f; -								//printDebug("PCM %d tuning at %f", patchcount, PCM[patchcount].tune); -							} -						} -					} -				} -			} -		} -		if (patchend==0) -			break; - -		PCM[patchcount].addr =  patchstart; -		PCM[patchcount].len = patchend - patchstart; -		patchcount++; -		//printf("Patch %d %d %d %d", patchcount, patchstart, patchend, mt32ram.PCM[patchcount].len); -		patchstart = patchend; -	}  	closeFile(file); -	if (!rc) -		return rc; - -	PCM[53].len = 1950; - -	// Generate official PCM list - -	// Normal sounds -	int pat = 0; -	for (int p = 0; p < 54; p++) { -		PCMList[pat].addr = PCM[PCMReassign[p]].addr; -		PCMList[pat].len = PCM[PCMReassign[p]].len; -		PCMList[pat].loop = PCM[PCMReassign[p]].loop; -		PCMList[pat].aggSound = -1; -		PCMList[pat].pcmnum = PCMReassign[p]; -		pat++; -	} - -	// Drum specific sounds.  Not exactly sure yet how these are different -	for (int p = 0; p < 20; p++) { -		PCMList[pat] = PCMList[p]; -		pat++; -	} - -	// Looped PCM sounds.  The last remaining 9 are aggregate sounds; -	// FIXME:KG: I hope this is correct; the original was heavily broken, -	// and it was hard to determine the author's intention. -	for (int p = 0; p < 54; p++) { -		if (p < 45) { -			int pcmNum = p > 7 ? p - 1 : p; -			PCMList[pat].addr = PCM[PCMReassign[pcmNum]].addr; -			PCMList[pat].len = PCM[PCMReassign[pcmNum]].len; -			PCMList[pat].pcmnum = PCMReassign[pcmNum]; -			PCMList[pat].loop = true; -			PCMList[pat].aggSound = -1; -		} else { -			//Calculate aggregate length -			int aggsnd = p - 45; -			int tmplen = 0; -			int sndpos = 0; -			while (LoopPatterns[aggsnd][sndpos] != -1) { -				tmplen += PCM[LoopPatterns[aggsnd][sndpos]].len; -				sndpos++; -			} -			PCMList[pat].addr = 0; -			PCMList[pat].len = tmplen; -			PCMList[pat].loop = true; -			PCMList[pat].aggSound = aggsnd; -		} -		pat++; -	} - -	//for (p=0;p<128;p++) -	//	printDebug("PCM #%d addr 0x%x len %d loop %d aggSound %d pcmnum %d", p, PCMList[p].addr, PCMList[p].len, PCMList[p].loop, PCMList[p].aggSound, PCMList[p].pcmnum); -	return true; +	return rc;  } -bool Synth::loadROM(const char *filename) { +bool Synth::loadPCMROM(const char *filename) {  	File *file = openFile(filename, File::OpenMode_read); // ROM File  	if (file == NULL) {  		return false;  	} -#ifdef MT32OUT -	File *outFile = openFile("mt32out.raw", File::OpenMode_write); -	File *outFileB = openFile("mt32out2.raw", File::OpenMode_write); -#endif  	bool rc = true;  	for (int i = 0; ; i++) {  		Bit8u s; @@ -495,12 +257,12 @@ bool Synth::loadROM(const char *filename) {  		int order[16] = {0, 9, 1 ,2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8}; -		e=0; -		for (u=0;u<15;u++) { -			if (order[u]<8) -				bit = (s >> (7-order[u])) & 0x1; +		e = 0; +		for (u = 0; u < 15; u++) { +			if (order[u] < 8) +				bit = (s >> (7 - order[u])) & 0x1;  			else -				bit = (c >> (7-(order[u]-8))) & 0x1; +				bit = (c >> (7  - (order[u] - 8))) & 0x1;  			e = e | (short)(bit << (15 - u));  		} @@ -514,10 +276,6 @@ bool Synth::loadROM(const char *filename) {  		e = (int)((float)e * (x/3200));  		*/ -#ifdef MT32OUT -		outFile->writeBit8u(e & 0xff); -		outFile->writeBit8u(((e >> 8) & 0x7f)); -#endif  		// File is encoded in dB, convert to PCM  		// MINDB = -96  		// MAXDB = -15 @@ -525,107 +283,169 @@ bool Synth::loadROM(const char *filename) {  		testval = (float)((~e) & 0x7fff);  		testval = -(testval / 400.00f);  		//testval = -(testval / 341.32291666666666666666666666667); -		float vol = powf(8,(testval / 20)) * 32767.0f; +		float vol = powf(8, testval / 20) * 32767.0f; -		if (e>0) +		if (e > 0)  			vol = -vol;  		romfile[i] = (Bit16s)vol; -#ifdef MT32OUT -		outFileB->writeBit8u(romfile[i] & 0xff); -		outFileB->writeBit8u(romfile[i] >> 8); -#endif  	} -#ifdef MT32OUT -	closeFile(outFileB); -	closeFile(outFile); -#endif  	closeFile(file);  	return rc;  } +struct TempPCMStruct +{ +	Bit8u pos; +	Bit8u len; +	Bit8u pitchLSB; +	Bit8u pitchMSB; +}; + +void Synth::initPCMList() { +	TempPCMStruct *tps = (TempPCMStruct *)&controlROMData[0x3000]; +	printDebug("********************************"); +	for (int i = 0; i < 128; 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. 260.1f sounded best to me in listening tests, but needs to be confirmed. +		//double STANDARDFREQ = 261.6255653005986346778499935233; // A below Middle C of 440Hz +		double STANDARDFREQ = 260.1f; +		float rTune = (float)(STANDARDFREQ * pow(2.0, (0x5000 - rTuneOffset) / 4096.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; +	} +	printDebug("********************************"); +} + +void Synth::initRhythmTimbre(int timbreNum, const Bit8u *mem) { +	TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre; +	memcpy(&timbre->common, mem, 14); +	mem += 14; +	char drumname[11]; +	strncpy(drumname, timbre->common.name, 10); +	drumname[10] = 0; +	bool breakout = false; +	for (int t = 0; t < 4; t++) { +		if (((timbre->common.pmute >> t) & 0x1) == 0x1) { +			memcpy(&timbre->partial[t], mem, 58); +			mem += 58; +		} +	} +} + +void Synth::initRhythmTimbres() { +	TempPCMStruct *tps = (TempPCMStruct *)&controlROMData[0x3000]; +	const Bit8u *drumMap = &controlROMData[0x3200]; +	int timbreNum = 192; +	for (Bit16u i = 0x3200; i < 0x323C; i += 2) { +		Bit16u address = (controlROMData[i + 1] << 8) | controlROMData[i]; +		initRhythmTimbre(timbreNum++, &controlROMData[address]); +	} +} + +void Synth::initTimbres(Bit16u mapAddress, int startTimbre) { +	for (Bit16u i = mapAddress; i < mapAddress + 0x80; i += 2) { +		Bit16u address = (controlROMData[i + 1] << 8) | controlROMData[i]; +		address = address + (mapAddress - 0x8000); +		TimbreParam *timbre = &mt32ram.timbres[startTimbre++].timbre; +		memcpy(timbre, &controlROMData[address], sizeof(TimbreParam)); +	} +} +  bool Synth::open(SynthProperties &useProp) {  	if (isOpen)  		return false; -	// Initalise patch information -  	myProp = useProp; -	usefilter = &iir_filter_normal; - -	partialManager = new PartialManager(this); - -	// This is so that names won't be garbage during early setup debug output, but we can detect bugs +	// This is to help detect bugs  	memset(&mt32ram, '?', sizeof(mt32ram)); -	printDebug("Initialising patch banks"); -	initmode = 0; -	if (!loadPreset("Preset1.syx")) { -		report(ReportType_errorPreset1, &errno); +	printDebug("Loading Control ROM"); +	if (!loadControlROM("MT32_CONTROL.ROM")) { +		printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM"); +		report(ReportType_errorControlROM, &errno);  		return false;  	} -	initmode = 1; -	if (!loadPreset("Preset2.syx")) { -		report(ReportType_errorPreset2, &errno); -		return false; -	} -	initmode = 2; -	printDebug("Initialising Drums"); -	if (!loadDrums("drumpat.rom")) { -		report(ReportType_errorDrumpat, &errno); +	printDebug("Loading PCM ROM"); +	if (!loadPCMROM("MT32_PCM.ROM")) { +		printDebug("Init Error - Missing MT32_PCM.ROM"); +		report(ReportType_errorPCMROM, &errno);  		return false;  	} -#if MT32EMU_DUMP_DRUMS == 1 -	strcpy(&pathBuf[0], baseDir); -	dumpDrums(strcat(&pathBuf[0],"drumsys.syx")); -#endif +	partialManager = new PartialManager(this); -	printDebug("Initialising PCM-to-ROM map"); -	if (!loadPCMToROMMap("patchlog.cfg")) { -		printDebug("Init Error - Missing patchlog.cfg"); -		report(ReportType_errorPatchlog, &errno); -		return false; -	} +	printDebug("Initialising PCM List"); +	initPCMList(); -	printDebug("Initialising ROM"); -	if (!loadROM("MT32_PCM.ROM")) { -		printDebug("Init Error - Missing MT32_PCM.ROM"); -		report(ReportType_errorMT32ROM, &errno); -		return false; -	} -	memcpy(chantable, InitChanTable, sizeof (chantable)); -	for (unsigned char i = 0; i < 128; i++) { -		mt32ram.params.patches[i].timbreGroup = i >> 6; -		mt32ram.params.patches[i].timbreNum = i & 63; +	printDebug("Initialising Timbre Bank A"); +	initTimbres(0x8000, 0); + +	printDebug("Initialising Timbre Bank B"); +	initTimbres(0xC000, 64); + +	printDebug("Initialising Timbre Bank R"); +	initRhythmTimbres(); + +	printDebug("Initialising Rhythm Temp"); +	memcpy(mt32ram.rhythmSettings, &controlROMData[0x741C], 344); + +	printDebug("Initialising Patches"); +	for (Bit8u i = 0; i < 128; i++) { +		PatchParam *patch = &mt32ram.patches[i]; +		patch->timbreGroup = i / 64; +		patch->timbreNum = i % 64; +		patch->keyShift = 24; +		patch->fineTune = 50; +		patch->benderRange = 12; +		patch->assignMode = 0; +		patch->reverbSwitch = 1; +		patch->dummy = 0;  	} -	if (!TableInitialiser::initMT32Tables(this, PCM, (float)myProp.sampleRate)) { -		report(ReportType_errorSampleRate, NULL); -		return false; +	printDebug("Initialising System"); +	//FIXME: Confirm that these are all correct +	// The MT-32 manual claims that "Standard pitch" is 442.0. +	// 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.reverbTime = 3; +	memcpy(mt32ram.system.reserveSettings, &controlROMData[0x57E5], 9); +	for (Bit8u i = 0; i < 9; i++) { +		mt32ram.system.chanAssign[i] = i + 1;  	} -	if (myProp.useDefaultReverb) -		initReverb(0, 5); -	else -		initReverb(myProp.reverbType, myProp.reverbTime); +	mt32ram.system.masterVol = 100; +	if (!refreshSystem()) +		return false; -	for (int i = 0; i < 9; i++) { +	for (int i = 0; i < 8; i++) { +		mt32ram.patchSettings[i].outlevel = 80; +		mt32ram.patchSettings[i].panpot = controlROMData[0x5800 + i]; +		memset(mt32ram.patchSettings[i].dummyv, 0, sizeof(mt32ram.patchSettings[i].dummyv));  		parts[i] = new Part(this, i); - -		if (i<8) { -			// The patch is already set by the presets, now set the timbre it wants -			parts[i]->setTimbre(&mt32ram.params.timbres[parts[i]->getAbsTimbreNum()].timbre); -			// And refresh the part's cache -			parts[i]->refreshPatch(); -		} +		parts[i]->setProgram(controlROMData[0x57EE + i]);  	} +	parts[8] = new RhythmPart(this, 8);  	// For resetting mt32 mid-execution  	mt32default = mt32ram; +	iirFilter = &iir_filter_normal; +  #ifdef MT32EMU_HAVE_X86  	bool availableSSE = DetectSIMD();  	bool available3DNow = Detect3DNow(); @@ -637,52 +457,19 @@ bool Synth::open(SynthProperties &useProp) {  	if (available3DNow) {  		printDebug("Detected and using SIMD (AMD 3DNow) extensions"); -		usefilter = &iir_filter_3dnow; +		iirFilter = &iir_filter_3dnow;  		report(ReportType_using3DNow, NULL);  	} else if (availableSSE) {  		printDebug("Detected and using SIMD (Intel SSE) extensions"); -		usefilter = &iir_filter_sse; +		iirFilter = &iir_filter_sse;  		report(ReportType_usingSSE, NULL);  	}  #endif -#if MT32EMU_BENCHMARK_FILTERS > 1 -	// Benchmark 3DNow, Floating point, and SSE filters -	clock_t start, end; -	float histval[50]; - -	for (int bench = 0; bench < 3; bench++) { -		start = clock(); -		for (int benchcnt=0;benchcnt<2000000;benchcnt++) { -			switch (bench) { -				case 0: -					iir_filter_normal(0,&histval[0],filtcoeff[0][0],0); -					break; -				case 1: -					if (!availableSSE) { -						printDebug("Skipping SSE benchmark, SSE not available"); -						continue; -					} -			        iir_filter_sse(0,&histval[0],filtcoeff[0][0],0); -					break; -				case 2: -					if (!available3DNow) { -						printDebug("Skipping 3DNow benchmark, 3DNow not available"); -						continue; -					} -			        iir_filter_3dnow(0,&histval[0],filtcoeff[0][0],0); -					break; -			} -		} -		end = clock(); -		printDebug("Bench %ld completed in %ld milliseconds", bench, (end - start) * 1000 / CLOCKS_PER_SEC); -	} -#endif - -	isOpen=true; -	isEnabled=false; +	isOpen = true; +	isEnabled = false; -	printDebug("**************** Initialisation complete ****************"); +	printDebug("*** Initialisation complete ***");  	return true;  } @@ -690,15 +477,7 @@ void Synth::close(void) {  	if (!isOpen)  		return; -	for (int t = 0; t < 3; t++) { -		for (int m = 0; m < NUM_NOTES; m++) { -			if (noteLookups[m].waveforms[t] != NULL) { -				delete[] noteLookups[m].waveforms[t]; -				noteLookups[m].waveforms[t] = NULL; -				noteLookups[m].waveformSize[t] = 0; -			} -		} -	} +	TableInitialiser::freeNotes();  	if (partialManager != NULL) {  		delete partialManager;  		partialManager = NULL; @@ -752,7 +531,7 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char  			// MIDI defines note-on with velocity 0 as being the same as note-off with velocity 40  			parts[part]->stopNote(note);  		} else { -			parts[part]->playNote(partialManager, note, velocity); +			parts[part]->playNote(note, velocity);  		}  		break;  	case 0xB: // Control change @@ -796,11 +575,7 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char  		break;  	case 0xC: // Program change  		//printDebug("Program change %01x", note); -		if (part < 8) { -			parts[part]->setPatch(note); -		} else { -			printDebug("Program change attempted on rhythm part"); -		} +		parts[part]->setProgram(note);  		break;  	case 0xE: // Pitch bender  		bend = (velocity << 7) | (note); @@ -815,7 +590,7 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char  	//midiOutShortMsg(m_out, msg);  } -void Synth::playSysex(Bit8u * sysex,Bit32u len) { +void Synth::playSysex(const Bit8u * sysex,Bit32u len) {  	if (len < 3) {  		printDebug("playSysex: Message is too short for sysex (%d bytes)", len);  	} @@ -830,7 +605,7 @@ void Synth::playSysex(Bit8u * sysex,Bit32u len) {  	playSysexWithoutFraming(sysex + 1, len - 2);  } -void Synth::playSysexWithoutFraming(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; @@ -861,7 +636,7 @@ void Synth::playSysexWithoutFraming(Bit8u * sysex, Bit32u len) {  #define NUMTOUCHED(x,y) (((x) + sizeof(y) - 1) / sizeof(y)) -void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u len) { +void Synth::playSysexWithoutHeader(unsigned char device, 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); @@ -920,21 +695,21 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le  	}  	if (addr >= MEMADDR(0x030000) && addr < MEMADDR(0x030110)) {  		int off = addr - MEMADDR(0x030000); -		if (off + len > sizeof(mt32ram.banks.pTemp)) { +		if (off + len > sizeof(mt32ram.patchSettings)) {  			printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);  			return;  		}  		int firstPart = off / sizeof(MemParams::PatchTemp);  		off %= sizeof(MemParams::PatchTemp);  		for (unsigned int m = 0; m < len; m++) -			mt32ram.banks.pTemp[firstPart][off + m] = sysex[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++) { -			int absTimbreNum = mt32ram.params.patchSettings[i].patch.timbreGroup * 64 + mt32ram.params.patchSettings[i].patch.timbreNum; +			int absTimbreNum = mt32ram.patchSettings[i].patch.timbreGroup * 64 + mt32ram.patchSettings[i].patch.timbreNum;  			char timbreName[11]; -			memcpy(timbreName, mt32ram.params.timbres[absTimbreNum].timbre.common.name, 10); +			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);  			if (parts[i] != NULL) { @@ -942,83 +717,84 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le  					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.params.timbres[parts[i]->getAbsTimbreNum()].timbre); +					parts[i]->setTimbre(&mt32ram.timbres[parts[i]->getAbsTimbreNum()].timbre);  				} -				parts[i]->refreshPatch(); +				parts[i]->refresh();  			}  		}  	} else if (addr >= MEMADDR(0x030110) && addr < MEMADDR(0x040000)) {  		int off = addr - MEMADDR(0x030110); -		if (off + len > sizeof(mt32ram.banks.rTemp)) { +		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);  		for (unsigned int m = 0; m < len; m++) -			mt32ram.banks.rTemp[firstDrum][off + m] = sysex[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++) { -			int timbreNum = mt32ram.params.rhythmSettings[i].timbre; +			int timbreNum = mt32ram.rhythmSettings[i].timbre;  			char timbreName[11];  			if (timbreNum < 94) { -				memcpy(timbreName, mt32ram.params.timbres[128 + timbreNum].timbre.common.name, 10); +				memcpy(timbreName, mt32ram.timbres[128 + timbreNum].timbre.common.name, 10);  				timbreName[10] = 0;  			} 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.params.rhythmSettings[i].outlevel, mt32ram.params.rhythmSettings[i].panpot, mt32ram.params.rhythmSettings[i].reverbSwitch, mt32ram.params.rhythmSettings[i].timbre, timbreName); +			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);  		}  		if (parts[8] != NULL) { -			parts[8]->refreshDrumCache(); +			parts[8]->refresh();  		}  	} else if (addr >= MEMADDR(0x040000) && addr < MEMADDR(0x050000)) {  		int off = addr - MEMADDR(0x040000); -		if (off + len > sizeof(mt32ram.banks.tTemp)) { +		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);  		for (unsigned int m = 0; m < len; m++) -			mt32ram.banks.tTemp[firstPart][off + m] = sysex[m]; +			((Bit8u *)&mt32ram.timbreSettings[firstPart])[off + m] = sysex[m];  		int lastPart = firstPart + NUMTOUCHED(off + len, TimbreParam) - 1;  		for (int i = firstPart; i <= lastPart; i++) {  			char instrumentName[11]; -			memcpy(instrumentName, mt32ram.params.timbreSettings[i].common.name, 10); +			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);  			if (parts[i] != NULL) { -				parts[i]->refreshPatch(); +				parts[i]->refresh();  			}  		}  	}  	else if (addr >= MEMADDR(0x050000) && addr < MEMADDR(0x060000)) {  		int off = addr - MEMADDR(0x050000); -		if (off + len > sizeof(mt32ram.banks.patchBank)) { +		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);  		for (unsigned int m = 0; m < len; m++) -			mt32ram.banks.patchBank[firstPatch][off + m] = sysex[m]; +			((Bit8u *)&mt32ram.patches[firstPatch])[off + m] = sysex[m];  		int lastPatch = firstPatch + NUMTOUCHED(off + len, PatchParam) - 1;  		for (int i = firstPatch; i <= lastPatch; i++) { -			PatchParam *patch = &mt32ram.params.patches[i]; +			PatchParam *patch = &mt32ram.patches[i];  			int patchAbsTimbreNum = patch->timbreGroup * 64 + patch->timbreNum;  			char instrumentName[11]; -			memcpy(instrumentName, mt32ram.params.timbres[patchAbsTimbreNum].timbre.common.name, 10); +			memcpy(instrumentName, mt32ram.timbres[patchAbsTimbreNum].timbre.common.name, 10);  			instrumentName[10] = 0; -			printDebug("WRITE-PATCH (%d-%d@%d..%d): %d; timbre=%d (%s)", firstPatch, lastPatch, off, off + len, i, patchAbsTimbreNum, instrumentName); +			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]);  			// 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.  			/*  			for (int part = 0; part < 8; part++) {  				if (parts[part] != NULL) { -					int partPatchAbsTimbreNum = mt32ram.params.patchSettings[part].patch.timbreGroup * 64 + mt32ram.params.patchSettings[part].patch.timbreNum; -					if (partPatchAbsTimbreNum == patchAbsTimbreNum) { +					int partPatchAbsTimbreNum = mt32ram.patchSettings[part].patch.timbreGroup * 64 + mt32ram.patchSettings[part].patch.timbreNum; +					if (parts[part]->getAbsTimbreNum() == patchAbsTimbreNum) {  						parts[part]->setPatch(patch);  						parts[part]->RefreshPatch();  					} @@ -1027,7 +803,7 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le  			*/  		}  	} else if (addr >= MEMADDR(0x080000) && addr < MEMADDR(0x090000)) { -		// Timbre patches +		// Timbres  		int off = addr - MEMADDR(0x080000);  		if (off + len > sizeof(MemParams::PaddedTimbre) * 64) {  			// You can only write to one group at a time @@ -1049,75 +825,34 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le  				// Write into user timbre group  		}  		for (unsigned int m = 0; m < len; m++) -			mt32ram.banks.timbreBank[firstTimbre][off + m] = sysex[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++) {  			char instrumentName[11]; -			memcpy(instrumentName, mt32ram.params.timbres[i].timbre.common.name, 10); +			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);  			// FIXME:KG: Not sure if the stuff below should be done (for rhythm and/or parts)...  			// Does the real MT-32 automatically do this? -			if (i >= 128 && parts[8] != NULL) { -				// FIXME:KG: Only bother to re-cache when this timbre's actually in the rhythm map -				parts[8]->setPatch(i); // Re-cache this timbre -			} -			for (unsigned int part = 0; part < 8; part++) { +			for (unsigned int part = 0; part < 9; part++) {  				if (parts[part] != NULL) { -					if (parts[part]->getAbsTimbreNum() == i) { -						parts[part]->refreshPatch(); -					} +					parts[part]->refreshTimbre(i);  				}  			}  		}  	} else if (addr >= MEMADDR(0x100000) && addr < MEMADDR(0x200000)) {  		int off = addr - MEMADDR(0x100000); -		if (off + len > sizeof(mt32ram.banks.systemBank)) { +		if (off + len > sizeof(mt32ram.system)) {  			printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);  			return;  		}  		for (unsigned int m = 0; m < len; m++) -			mt32ram.banks.systemBank[m + off] = sysex[m]; +			((Bit8u *)&mt32ram.system)[m + off] = sysex[m];  		report(ReportType_devReconfig, NULL); -		printDebug("System Reconfiguration:"); -		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.params.system.chanAssign[i]); -			if (mt32ram.params.system.chanAssign[i] == 16) { -				parts[i]->allStop(); -			} else { -				chantable[(int)mt32ram.params.system.chanAssign[i]] = (char)i; -			} -		} - -		printDebug(" Master Tune: %f", ((float)mt32ram.params.system.masterTune)*0.2+432.1); -		printDebug(" Reverb: mode=%d, time=%d, level=%d", mt32ram.params.system.reverbMode, mt32ram.params.system.reverbTime, mt32ram.params.system.reverbLevel); -		report(ReportType_newReverbMode,  &mt32ram.params.system.reverbMode); -		report(ReportType_newReverbTime,  &mt32ram.params.system.reverbTime); -		report(ReportType_newReverbLevel, &mt32ram.params.system.reverbLevel); - -		if ((mt32ram.params.system.reverbMode != curRevMode) || (mt32ram.params.system.reverbTime != curRevTime)) { -			if (myProp.useDefaultReverb) { -				initReverb(mt32ram.params.system.reverbMode, mt32ram.params.system.reverbTime); -				curRevLevel = mt32ram.params.system.reverbLevel; -			} else { -				initReverb(myProp.reverbType, myProp.reverbTime); -				curRevLevel = myProp.reverbLevel; -			} -		} - -		char *rset = mt32ram.params.system.reserveSettings; -		printDebug(" Partial reserve: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]); -		int pr = partialManager->SetReserve(rset); -		if (pr != 32) -			printDebug(" (Partial Reserve Table with less than 32 partials reserved!)"); -		rset = mt32ram.params.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.params.system.masterVol); -		mastervolume = (Bit16s)((float)mt32ram.params.system.masterVol * 327.0); +		printDebug("WRITE-SYSTEM:"); +		refreshSystem();  	} else if (addr == MEMADDR(0x200000)) {  		char buf[MAX_SYSEX_SIZE];  		if (len > MAX_SYSEX_SIZE - 1) { @@ -1131,73 +866,112 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le  	} else if (addr >= MEMADDR(0x7f0000)) {  		printDebug("Reset");  		report(ReportType_devReset, NULL); -		partialManager->DeactivateAll(); +		partialManager->deactivateAll();  		mt32ram = mt32default; -		for (int i = 0; i < 8; i++) { -			parts[i]->refreshPatch(); +		for (int i = 0; i < 9; i++) { +			parts[i]->refresh();  		} -		parts[8]->refreshDrumCache();  		isEnabled = false;  	} else {  		printDebug("Sysex write to unrecognised address %06x", SYSEXMEMADDR(addr));  	}  } -int Synth::dumpSysex(char *filename) { -	File *file = openFile(filename, File::OpenMode_write); -	if (file == NULL) -		return -1; +bool Synth::refreshSystem() { +	memset(chantable,-1,sizeof(chantable)); -	int patchnum; -	for (patchnum=0;patchnum<64;patchnum++) { -		// Sysex header -		if (!file->writeBit8u(0xF0)) -			break; -		if (!file->writeBit8u(0x41)) -			break; -		if (!file->writeBit8u(0x10)) -			break; -		if (!file->writeBit8u(0x16)) -			break; -		if (!file->writeBit8u(0x12)) -			break; +	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(); +		} else { +			chantable[(int)mt32ram.system.chanAssign[i]] = (char)i; +		} +	} +	//FIXME:KG: This is just an educated guess. +	// The LAPC-I documentation claims a range of 427.5Hz-452.6Hz (similar to what we have here) +	// The MT-32 documentation claims a range of 432.1Hz-457.6Hz +	masterTune = 440.0f * powf(2.0f, (mt32ram.system.masterTune - 64.0f) / (128.0f * 12.0f)); +	printDebug(" Master Tune: %f", masterTune); +	printDebug(" Reverb: mode=%d, time=%d, level=%d", mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel); +	report(ReportType_newReverbMode,  &mt32ram.system.reverbMode); +	report(ReportType_newReverbTime,  &mt32ram.system.reverbTime); +	report(ReportType_newReverbLevel, &mt32ram.system.reverbLevel); + +	if (myProp.useDefaultReverb) { +		initReverb(mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel); +	} else { +		initReverb(myProp.reverbType, myProp.reverbTime, mt32ram.system.reverbLevel); +	} -		int useaddr = patchnum * 256; -		char lsb = (char)(useaddr & 0x7f); -		char isb = (char)((useaddr >> 7) & 0x7f); -		char msb = (char)(((useaddr >> 14) & 0x7f) | 0x08); -		//Address -		if (!file->writeBit8u(msb)) -			break; -		if (!file->writeBit8u(isb)) -			break; -		if (!file->writeBit8u(lsb)) -			break; +	Bit8u *rset = mt32ram.system.reserveSettings; +	printDebug(" Partial reserve: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]); +	int pr = partialManager->setReserve(rset); +	if (pr != 32) +		printDebug(" (Partial Reserve Table with less than 32 partials reserved!)"); +	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)) { +		report(ReportType_errorSampleRate, NULL); +		return false; +	} +	return true; +} -		//Data -		if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.common,0xE) != 0xE) -			break; -		if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.partial[0],0x3A) != 0x3A) -			break; -		if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.partial[1],0x3A) != 0x3A) -			break; -		if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.partial[2],0x3A) != 0x3A) -			break; -		if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.partial[3],0x3A) != 0x3A) -			break; -		//Checksum -		unsigned char *dat = (unsigned char *)&mt32ram.params.timbres[patchnum + 128].timbre; -		unsigned char checksum = calcSysexChecksum(dat, 246, msb + isb + lsb); +bool Synth::dumpTimbre(File *file, const TimbreParam *timbre, Bit32u address) { +	// Sysex header +	if (!file->writeBit8u(0xF0)) +		return false; +	if (!file->writeBit8u(0x41)) +		return false; +	if (!file->writeBit8u(0x10)) +		return false; +	if (!file->writeBit8u(0x16)) +		return false; +	if (!file->writeBit8u(0x12)) +		return false; -		if (!file->writeBit8u(checksum)) -			break; +	char lsb = (char)(address & 0x7f); +	char isb = (char)((address >> 7) & 0x7f); +	char msb = (char)(((address >> 14) & 0x7f) | 0x08); + +	//Address +	if (!file->writeBit8u(msb)) +		return false; +	if (!file->writeBit8u(isb)) +		return false; +	if (!file->writeBit8u(lsb)) +		return false; + +	//Data +	if (file->write(timbre, 246) != 246) +		return false; + +	//Checksum +	unsigned char checksum = calcSysexChecksum((unsigned char *)timbre, 246, msb + isb + lsb); +	if (!file->writeBit8u(checksum)) +		return false; + +	//End of sysex +	if (!file->writeBit8u(0xF7)) +		return false; +	return true; +} + +int Synth::dumpTimbres(const char *filename, int start, int len) { +	File *file = openFile(filename, File::OpenMode_write); +	if (file == NULL) +		return -1; -		//End of sysex -		if (!file->writeBit8u(0xF7)) +	for (int timbreNum = start; timbreNum < start + len; timbreNum++) { +		int useaddr = (timbreNum - start) * 256; +		TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre; +		if (!dumpTimbre(file, timbre, useaddr))  			break;  	}  	closeFile(file); -	printDebug("Wrote temp patches to %s", filename);  	return 0;  } @@ -1231,14 +1005,14 @@ void Synth::render(Bit16s *stream, Bit32u len) {  void Synth::doRender(Bit16s * stream,Bit32u len) {  	Bit32u m; -	partialManager->AgeAll(); +	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); +				if (partialManager->produceOutput(i, &tmpBuffer[0], len)) { +					ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);  					hasOutput = true;  				}  			} @@ -1263,19 +1037,19 @@ void Synth::doRender(Bit16s * stream,Bit32u len) {  		}  		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); +				if (partialManager->produceOutput(i, &tmpBuffer[0], len)) { +					ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);  				}  			}  		}  	} else {  		for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { -			if (partialManager->ProduceOutput(i, &tmpBuffer[0], len)) -				ProduceOutput1(&tmpBuffer[0], stream, len, mastervolume); +			if (partialManager->produceOutput(i, &tmpBuffer[0], len)) +				ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);  		}  	} -	partialManager->ClearAlreadyOutputed(); +	partialManager->clearAlreadyOutputed();  #if MT32EMU_MONITOR_PARTIALS == 1  	samplepos += len; diff --git a/backends/midi/mt32/synth.h b/backends/midi/mt32/synth.h index dbd77c1dfd..c93ff01803 100644 --- a/backends/midi/mt32/synth.h +++ b/backends/midi/mt32/synth.h @@ -39,27 +39,27 @@ class PartialManager;  class Part;  enum ReportType { -	// Files missing -	ReportType_errorPreset1    = 1, -	ReportType_errorPreset2    = 2, -	ReportType_errorDrumpat    = 3, -	ReportType_errorPatchlog   = 4, -	ReportType_errorMT32ROM    = 5, -	ReportType_errorSampleRate = 6, +	// Errors +	ReportType_errorControlROM = 1, +	ReportType_errorPCMROM, +	ReportType_errorSampleRate, + +	// Progress +	ReportType_progressInit,  	// HW spec -	ReportType_availableSSE    = 7, -	ReportType_available3DNow  = 8, -	ReportType_usingSSE        = 9, -	ReportType_using3DNow      = 10, +	ReportType_availableSSE, +	ReportType_available3DNow, +	ReportType_usingSSE, +	ReportType_using3DNow,  	// General info -	ReportType_lcdMessage      = 11, -	ReportType_devReset        = 12, -	ReportType_devReconfig     = 13, -	ReportType_newReverbMode   = 14, -	ReportType_newReverbTime   = 15, -	ReportType_newReverbLevel  = 16 +	ReportType_lcdMessage, +	ReportType_devReset, +	ReportType_devReconfig, +	ReportType_newReverbMode, +	ReportType_newReverbTime, +	ReportType_newReverbLevel  };  struct SynthProperties { @@ -84,7 +84,7 @@ struct SynthProperties {  	// This is used as the first argument to all callbacks  	void *userData;  	// Callback for reporting various errors and information. May be NULL -	void (*report)(void *userData, ReportType type, const void *reportData); +	int (*report)(void *userData, ReportType type, const void *reportData);  	// Callback for debug messages, in vprintf() format  	void (*printDebug)(void *userData, const char *fmt, va_list list);  	// Callback for providing an implementation of File, opened and ready for use @@ -105,19 +105,20 @@ typedef void (*recalcStatusCallback)(int percDone);  bool RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack);  typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr, int revLevel); -extern iir_filter_type usefilter;  class Synth {  friend class Part; +friend class RhythmPart;  friend class Partial;  friend class TableInitialiser;  private:  	bool isEnabled; -	PCMWave PCM[54]; +	iir_filter_type iirFilter; +  	PCMWaveEntry PCMList[128]; -	Bit32s PCMLoopTable[54]; +	Bit8u controlROMData[64 * 1024];  	Bit16s romfile[PCMSIZE + GRAN];  	Bit8s chantable[32]; @@ -125,15 +126,12 @@ private:  	static Bit32s samplepos = 0;  	#endif -	MT32RAMFormat mt32ram, mt32default; +	MemParams mt32ram, mt32default;  	revmodel *reverbModel; -	Bit16s mastervolume; - -	char curRevMode; -	char curRevTime; -	Bit32u curRevLevel; +	float masterTune; +	Bit16u masterVolume;  	unsigned char initmode;  	bool isOpen; @@ -150,25 +148,30 @@ private:  	SynthProperties myProp;  	bool loadPreset(const char *filename); -	void initReverb(char newRevMode, char newRevTime); +	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, Bit8u *sysex, Bit32u len); - -	bool loadDrums(const char *filename); -	bool loadPCMToROMMap(const char *filename); -	bool loadROM(const char *filename); -	void dumpDrums(const char *filename); -	// Save the system state to a sysex file specified by filename -	int dumpSysex(char *filename); - +	void playSysexWithoutHeader(unsigned char channel, const Bit8u *sysex, Bit32u len); + +	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 refreshSystem();  protected: -	void report(ReportType type, const void *reportData); +	int report(ReportType type, const void *reportData);  	File *openFile(const char *filename, File::OpenMode mode);  	void closeFile(File *file);  	void printDebug(const char *fmt, ...);  public: +	static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum); +  	Synth();  	~Synth(); @@ -182,11 +185,10 @@ public:  	// Sends a 4-byte MIDI message to the MT-32 for immediate playback  	void playMsg(Bit32u msg); -	static Bit8u calcSysexChecksum(Bit8u *data, Bit32u len, Bit8u checksum);  	// Sends a string of Sysex commands to the MT-32 for immediate interpretation  	// The length is in bytes -	void playSysex(Bit8u *sysex, Bit32u len); -	void playSysexWithoutFraming(Bit8u *sysex, Bit32u len); +	void playSysex(const Bit8u *sysex, Bit32u len); +	void playSysexWithoutFraming(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, diff --git a/backends/midi/mt32/tables.cpp b/backends/midi/mt32/tables.cpp index 2f710e8e0e..3d0ab5ea45 100644 --- a/backends/midi/mt32/tables.cpp +++ b/backends/midi/mt32/tables.cpp @@ -25,36 +25,14 @@  #include "mt32emu.h" +#define FIXEDPOINT_MAKE(x, point) ((Bit32u)((1 << point) * x)) +  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}; -const Bit8s LoopPatterns[9][10] = { -	{ 2,  3, 4,   5,  6,  7, -1, -1, -1, -1}, -	{ 8,  9, 10, 11, 12, 13, 14, 15, 16, -1}, -	{17, 18, 19, 20, 21, -1, -1, -1, -1, -1}, -	{22, 23, 24, 25, 26, 27, 28, 29, -1, -1}, -	{30, 31, 32, 33, 34, 35, 36, 37, -1, -1}, -	{45, 46, 47, 48, 49, 50, 51, 52, 53, -1}, -	{15, 11, 12, 13, 14, 15, 16, -1, -1, -1}, -	{30, 35, 32, 33, 34, -1, -1, -1, -1, -1}, -	{ 2,  3, -1, -1, -1, -1, -1, -1, -1, -1}, -}; - -static const Bit32s LoopPatternTuning[9][10] = { -	{0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1,      -1,      -1,      -1}, -	{0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1}, -	{0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1,      -1,      -1,      -1,      -1}, -	{0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1,      -1}, -	{0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1,      -1}, -	{0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1}, -	{0x2590B, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1,      -1,      -1}, -	{0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1,      -1,      -1,      -1,      -1}, -	{0x1294A, 0x1294A,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1}, -}; -  // These are division constants for the TVF depth key follow  static const Bit32u depexp[5] = {3000, 950, 485, 255, 138}; @@ -63,6 +41,7 @@ static const double tkcatconst[5] = {0.0, 0.005853144, 0.011148054, 0.019086143,  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]; @@ -215,7 +194,7 @@ static void initEnvelopes(float samplerate) {  		//lasttimetable[lf] = (int)((exp(logtime)/(312.12*6)) * (float)samplerate);  		float mv = (float)lf / 100.0f; -		float pt = mv-0.5f; +		float pt = mv - 0.5f;  		if (pt < 0)  			pt = 0; @@ -319,7 +298,7 @@ void TableInitialiser::initMT32ConstantTables(Synth *synth) {  	for (lf = 0; lf < 128; lf++) {  		// Converts MIDI velocity to volume. -		voltable[lf] = (int)(127.0f * powf((float)lf / 127.0f, FLOAT_LN)); +		voltable[lf] = FIXEDPOINT_MAKE(powf((float)lf / 127.0f, FLOAT_LN), 7);  	}  	for (unsigned int i = 0; i < MAX_SAMPLE_OUTPUT; i++) {  		int myRand; @@ -523,7 +502,7 @@ File *TableInitialiser::initWave(Synth *synth, NoteLookup *noteLookup, float amp  #if 0  			//FIXME:KG: Credit Timo Strunk (bastardo on #scummvm) for help with this! -			float saw = 0.5f * FLOAT_PI - sa / 2; +			double saw = 0.5 * DOUBLE_PI - sa / 2;  #else  			double saw = 0.0;  			for (int sinus = 1; sinus < div; sinus++) { @@ -581,12 +560,15 @@ static void initNFiltTable(NoteLookup *noteLookup, float freq, float rate) {  	}  } -File *TableInitialiser::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWave pcmWaves[54], File *file) { +File *TableInitialiser::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float masterTune, PCMWaveEntry pcmWaves[128], File *file) {  	float ampsize = WGAMP; -	float freq = (float)(tuning * pow(2.0, ((double)note - MIDDLEA) / 12.0)); +	float freq = (float)(masterTune * pow(2.0, ((double)note - MIDDLEA) / 12.0));  	float div = rate / freq;  	noteLookup->div = (int)div; +	if (noteLookup->div == 0) +		noteLookup->div = 1; +  	initSaw(noteLookup, noteLookup->div);  	initDep(noteLookup, note); @@ -595,18 +577,10 @@ File *TableInitialiser::initNote(Synth *synth, NoteLookup *noteLookup, float not  	// Create the pitch tables -	float rateMult = 32000.0f / rate; -	float tuner = rateMult * freq * 65536.0f; -	for (int pc = 0; pc < 54; pc++) { -		noteLookup->wavTable[pc] = (int)(tuner / pcmWaves[pc].tune); -	} -	for (int lp = 0; lp < 9; lp++) { -		for (int ln = 0; ln < 10; ln++) { -			// FIXME:KG: Surely this needs to be adjusted for the rate? -			// If not, remove rateMult * from below -			// (Note: I'm assuming the LoopPatternTuning constants were intended for 32k rate) -			noteLookup->loopTable[lp][ln] = (int)(rateMult * (float)LoopPatternTuning[lp][ln] * (freq / 220.0f)); -		} +	double rateMult = 32000.0 / rate; +	double tuner = freq * 65536.0f; +	for (int pc = 0; pc < 128; pc++) { +		noteLookup->wavTable[pc] = (int)(tuner / pcmWaves[pc].tune * rateMult);  	}  	initFiltTable(noteLookup, freq, rate); @@ -614,11 +588,14 @@ File *TableInitialiser::initNote(Synth *synth, NoteLookup *noteLookup, float not  	return file;  } -void TableInitialiser::initNotes(Synth *synth, PCMWave pcmWaves[54], float rate, float tuning) { +bool TableInitialiser::initNotes(Synth *synth, PCMWaveEntry pcmWaves[128], 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, 1}; -	sprintf(filename, "waveformcache-%d-%.1f.raw", intRate, tuning); +	char version[4] = {0, 0, 0, 2}; +	sprintf(filename, "waveformcache-%d-%.2f.raw", intRate, masterTune);  	File *file = NULL;  	char header[20]; @@ -631,11 +608,11 @@ void TableInitialiser::initNotes(Synth *synth, PCMWave pcmWaves[54], float rate,  	header[pos++] = (char)((intRate >> 16) & 0xFF);  	header[pos++] = (char)((intRate >> 8) & 0xFF);  	header[pos++] = (char)(intRate & 0xFF); -	int intTuning = (int)tuning; +	int intTuning = (int)masterTune;  	header[pos++] = (char)((intTuning >> 8) & 0xFF);  	header[pos++] = (char)(intTuning & 0xFF);  	header[pos++] = 0; -	header[pos] = (char)((tuning - intTuning) * 10); +	header[pos] = (char)((masterTune - intTuning) * 10);  #if MT32EMU_WAVECACHEMODE < 2  	bool reading = false;  	file = synth->openFile(filename, File::OpenMode_read); @@ -648,33 +625,37 @@ void TableInitialiser::initNotes(Synth *synth, PCMWave pcmWaves[54], float rate,  					if (endianCheck == 1) {  						reading = true;  					} else { -						synth->printDebug("Endian check in %s does not match expected - will generate", filename); +						synth->printDebug("Endian check in %s does not match expected", filename);  					}  				} else { -					synth->printDebug("Unable to read endian check in %s - will generate", filename); +					synth->printDebug("Unable to read endian check in %s", filename);  				}  			} else { -				synth->printDebug("Header of %s does not match expected - will generate", filename); +				synth->printDebug("Header of %s does not match expected", filename);  			}  		} else { -			synth->printDebug("Error reading 16 bytes of %s - will generate", filename); +			synth->printDebug("Error reading 16 bytes of %s", filename);  		}  		if (!reading) {  			file->close();  			file = NULL;  		}  	} else { -		synth->printDebug("Unable to open %s for reading - will generate", filename); +		synth->printDebug("Unable to open %s for reading", filename);  	}  #endif -	//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? -	for (int f = 12; f < 109; f++) { -		NoteLookup *noteLookup = ¬eLookups[f]; -		file = initNote(synth, noteLookup, (float)f, rate, tuning, pcmWaves, file); +	float progress = 0.0f; +	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); +		NoteLookup *noteLookup = ¬eLookups[f - LOWEST_NOTE]; +		file = initNote(synth, noteLookup, (float)f, rate, masterTune, pcmWaves, file); +		progress = (f - LOWEST_NOTE + 1) / (float)NUM_NOTES; +		abort = synth->report(ReportType_progressInit, &progress) != 0; +		if (abort) +			break;  	}  #if MT32EMU_WAVECACHEMODE == 0 || MT32EMU_WAVECACHEMODE == 2 @@ -682,7 +663,7 @@ void TableInitialiser::initNotes(Synth *synth, PCMWave pcmWaves[54], float rate,  		file = synth->openFile(filename, File::OpenMode_write);  		if (file != NULL) {  			if (file->write(header, 16) == 16 && file->writeBit16u(1)) { -				for (int f = 12; f < 109 && file != NULL; f++) { +				for (int f = 0; f < NUM_NOTES; f++) {  					for (int i = 0; i < 3 && file != NULL; i++) {  						int len = noteLookups[f].waveformSize[i];  						for (int j = 0; j < len; j++) { @@ -706,21 +687,42 @@ void TableInitialiser::initNotes(Synth *synth, PCMWave pcmWaves[54], float rate,  	if (file != NULL)  		synth->closeFile(file); +	return !abort; +} + +void TableInitialiser::freeNotes() { +	for (int t = 0; t < 3; t++) { +		for (int m = 0; m < NUM_NOTES; m++) { +			if (noteLookups[m].waveforms[t] != NULL) { +				delete[] noteLookups[m].waveforms[t]; +				noteLookups[m].waveforms[t] = NULL; +				noteLookups[m].waveformSize[t] = 0; +			} +		} +	} +	initialisedMasterTune = 0.0f;  } -bool TableInitialiser::initMT32Tables(Synth *synth, PCMWave pcms[54], float sampleRate) { +bool TableInitialiser::initMT32Tables(Synth *synth, PCMWaveEntry pcmWaves[128], float sampleRate, float masterTune) {  	if (sampleRate <= 0.0f) {  		synth->printDebug("Bad sampleRate (%d <= 0.0f)", sampleRate);  		return false;  	}  	if (initialisedSampleRate == 0.0f) {  		initMT32ConstantTables(synth); +	} +	if (initialisedSampleRate != sampleRate) {  		initFiltCoeff(sampleRate);  		initEnvelopes(sampleRate); +	} +	if (initialisedSampleRate != sampleRate || initialisedMasterTune != masterTune) { +		freeNotes(); +		if (!initNotes(synth, pcmWaves, sampleRate, masterTune)) { +			return false; +		}  		initialisedSampleRate = sampleRate; +		initialisedMasterTune = masterTune;  	} -	// This always needs to be done, to allocate the waveforms -	initNotes(synth, pcms, sampleRate, TUNING);  	return true;  } diff --git a/backends/midi/mt32/tables.h b/backends/midi/mt32/tables.h index 46be3f1548..0c85796f4f 100644 --- a/backends/midi/mt32/tables.h +++ b/backends/midi/mt32/tables.h @@ -36,21 +36,19 @@ const int FILTERGRAN = 512;  const int MIDDLEC = 60;  const int MIDDLEA = 69; // By this I mean "A above middle C" -// Constant tuning for now. The manual claims "Standard pitch" is 442.0. -// 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. -// FIXME:KG: Keeping it at 440.0f for now, as in original. Check with CC -const float TUNING = 440.0f; - -const int NUM_NOTES = 128; // Number of slots for note LUT (we actually only use 12..108) +//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? +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 const Bit8s LoopPatterns[9][10]; -  extern Bit16s smallnoise[MAX_SAMPLE_OUTPUT];  // Some optimization stuff @@ -75,9 +73,8 @@ extern Bit32s voltable[128];  extern float ResonInv[31];  struct NoteLookup { -	Bit32s div; -	Bit32u wavTable[54]; -	Bit32u loopTable[9][10]; +	Bit32u div; +	Bit32u wavTable[128];  	Bit32s sawTable[101];  	Bit32s fildepTable[5];  	Bit32s timekeyTable[5]; @@ -92,10 +89,11 @@ extern NoteLookup noteLookups[NUM_NOTES];  class TableInitialiser {  	static void initMT32ConstantTables(Synth *synth);  	static File *initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div, File *file); -	static void initNotes(Synth *synth, PCMWave pcms[54], float rate, float tuning); +	static bool initNotes(Synth *synth, PCMWaveEntry pcmWaves[128], float rate, float tuning);  public: -	static bool initMT32Tables(Synth *synth, PCMWave pcms[54], float sampleRate); -	static File *initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWave pcmWaves[54], File *file); +	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();  };  } | 
