diff options
| -rw-r--r-- | audio/softsynth/mt32/AReverbModel.cpp | 4 | ||||
| -rw-r--r-- | audio/softsynth/mt32/DelayReverb.cpp | 4 | ||||
| -rw-r--r-- | audio/softsynth/mt32/FreeverbModel.cpp | 4 | ||||
| -rw-r--r-- | audio/softsynth/mt32/LA32Ramp.cpp | 5 | ||||
| -rw-r--r-- | audio/softsynth/mt32/Part.cpp | 9 | ||||
| -rw-r--r-- | audio/softsynth/mt32/Partial.cpp | 89 | ||||
| -rw-r--r-- | audio/softsynth/mt32/PartialManager.cpp | 4 | ||||
| -rw-r--r-- | audio/softsynth/mt32/Poly.cpp | 3 | ||||
| -rw-r--r-- | audio/softsynth/mt32/Synth.cpp | 6 | ||||
| -rw-r--r-- | audio/softsynth/mt32/Synth.h | 1 | ||||
| -rw-r--r-- | audio/softsynth/mt32/TVA.cpp | 6 | ||||
| -rw-r--r-- | audio/softsynth/mt32/TVF.cpp | 4 | ||||
| -rw-r--r-- | audio/softsynth/mt32/TVP.cpp | 5 | ||||
| -rw-r--r-- | audio/softsynth/mt32/Tables.cpp | 32 | ||||
| -rw-r--r-- | audio/softsynth/mt32/Tables.h | 16 | ||||
| -rw-r--r-- | audio/softsynth/mt32/mmath.h | 4 | ||||
| -rw-r--r-- | audio/softsynth/mt32/mt32emu.h | 13 | 
17 files changed, 139 insertions, 70 deletions
diff --git a/audio/softsynth/mt32/AReverbModel.cpp b/audio/softsynth/mt32/AReverbModel.cpp index 4ee6c87943..151f6c2c81 100644 --- a/audio/softsynth/mt32/AReverbModel.cpp +++ b/audio/softsynth/mt32/AReverbModel.cpp @@ -18,7 +18,7 @@  #include "mt32emu.h"  #include "AReverbModel.h" -using namespace MT32Emu; +namespace MT32Emu {  // Default reverb settings for modes 0-2 @@ -235,3 +235,5 @@ void AReverbModel::process(const float *inLeft, const float *inRight, float *out  		outRight++;  	}  } + +} diff --git a/audio/softsynth/mt32/DelayReverb.cpp b/audio/softsynth/mt32/DelayReverb.cpp index 89eebf0d79..bfb066630c 100644 --- a/audio/softsynth/mt32/DelayReverb.cpp +++ b/audio/softsynth/mt32/DelayReverb.cpp @@ -20,7 +20,7 @@  #include "mt32emu.h"  #include "DelayReverb.h" -using namespace MT32Emu; +namespace MT32Emu {  // CONFIRMED: The values below are found via analysis of digital samples. Checked with all time and level combinations. @@ -148,3 +148,5 @@ bool DelayReverb::isActive() const {  	}  	return false;  } + +} diff --git a/audio/softsynth/mt32/FreeverbModel.cpp b/audio/softsynth/mt32/FreeverbModel.cpp index c11fa859d8..d9bd17e62e 100644 --- a/audio/softsynth/mt32/FreeverbModel.cpp +++ b/audio/softsynth/mt32/FreeverbModel.cpp @@ -20,7 +20,7 @@  #include "freeverb.h" -using namespace MT32Emu; +namespace MT32Emu {  FreeverbModel::FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp) {  	freeverb = NULL; @@ -76,3 +76,5 @@ bool FreeverbModel::isActive() const {  	// FIXME: Not bothering to do this properly since we'll be replacing Freeverb soon...  	return false;  } + +} diff --git a/audio/softsynth/mt32/LA32Ramp.cpp b/audio/softsynth/mt32/LA32Ramp.cpp index 9f1f01c3c2..4e4d6b4f30 100644 --- a/audio/softsynth/mt32/LA32Ramp.cpp +++ b/audio/softsynth/mt32/LA32Ramp.cpp @@ -79,11 +79,12 @@ LA32Ramp::LA32Ramp() :  void LA32Ramp::startRamp(Bit8u target, Bit8u increment) {  	// CONFIRMED: From sample analysis, this appears to be very accurate. -	// FIXME: We could use a table for this in future  	if (increment == 0) {  		largeIncrement = 0;  	} else { -		largeIncrement = (unsigned int)(EXP2F(((increment & 0x7F) + 24) / 8.0f) + 0.125f); +		// Using integer argument here, no precision loss: +		// (unsigned int)(EXP2F(((increment & 0x7F) + 24) / 8.0f) + 0.125f) +		largeIncrement = (unsigned int)(EXP2I(((increment & 0x7F) + 24) << 9) + 0.125f);  	}  	descending = (increment & 0x80) != 0;  	if (descending) { diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp index 75912f38a8..88d42dbbd4 100644 --- a/audio/softsynth/mt32/Part.cpp +++ b/audio/softsynth/mt32/Part.cpp @@ -544,9 +544,12 @@ void Part::allNotesOff() {  	// should treat the hold pedal as usual.  	for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) {  		Poly *poly = *polyIt; -		// FIXME: This has special handling of key 0 in NoteOff that Mok has not yet confirmed -		// applies to AllNotesOff. -		poly->noteOff(holdpedal); +		// FIXME: This has special handling of key 0 in NoteOff that Mok has not yet confirmed applies to AllNotesOff. +		// if (poly->canSustain() || poly->getKey() == 0) { +		// FIXME: The real devices are found to be ignoring non-sustaining polys while processing AllNotesOff. Need to be confirmed. +		if (poly->canSustain()) { +			poly->noteOff(holdpedal); +		}  	}  } diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp index 03bec560b8..a6d164f218 100644 --- a/audio/softsynth/mt32/Partial.cpp +++ b/audio/softsynth/mt32/Partial.cpp @@ -22,7 +22,7 @@  #include "mt32emu.h"  #include "mmath.h" -using namespace MT32Emu; +namespace MT32Emu {  #ifdef INACCURATE_SMOOTH_PAN  // Mok wanted an option for smoother panning, and we love Mok. @@ -133,6 +133,25 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us  	stereoVolume.leftVol = panVal / 7.0f;  	stereoVolume.rightVol = 1.0f - stereoVolume.leftVol; +	// SEMI-CONFIRMED: From sample analysis: +	// Found that timbres with 3 or 4 partials (i.e. one using two partial pairs) are mixed in two different ways. +	// Either partial pairs are added or subtracted, it depends on how the partial pairs are allocated. +	// It seems that partials are grouped into quarters and if the partial pairs are allocated in different quarters the subtraction happens. +	// Though, this matters little for the majority of timbres, it becomes crucial for timbres which contain several partials that sound very close. +	// In this case that timbre can sound totally different depending of the way it is mixed up. +	// Most easily this effect can be displayed with the help of a special timbre consisting of several identical square wave partials (3 or 4). +	// Say, it is 3-partial timbre. Just play any two notes simultaneously and the polys very probably are mixed differently. +	// Moreover, the partial allocator retains the last partial assignment it did and all the subsequent notes will sound the same as the last released one. +	// The situation is better with 4-partial timbres since then a whole quarter is assigned for each poly. However, if a 3-partial timbre broke the normal +	// whole-quarter assignment or after some partials got aborted, even 4-partial timbres can be found sounding differently. +	// This behaviour is also confirmed with two more special timbres: one with identical sawtooth partials, and one with PCM wave 02. +	// For my personal taste, this behaviour rather enriches the sounding and should be emulated. +	// Also, the current partial allocator model probably needs to be refined. +	if (debugPartialNum & 8) { +		stereoVolume.leftVol = -stereoVolume.leftVol; +		stereoVolume.rightVol = -stereoVolume.rightVol; +	} +  	if (patchCache->PCMPartial) {  		pcmNum = patchCache->pcm;  		if (synth->controlROMMap->pcmCount > 128) { @@ -149,7 +168,7 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us  	}  	// CONFIRMED: pulseWidthVal calculation is based on information from Mok -	pulseWidthVal = (poly->getVelocity() - 64) * (patchCache->srcPartial.wg.pulseWidthVeloSensitivity - 7) + synth->tables.pulseWidth100To255[patchCache->srcPartial.wg.pulseWidth]; +	pulseWidthVal = (poly->getVelocity() - 64) * (patchCache->srcPartial.wg.pulseWidthVeloSensitivity - 7) + Tables::getInstance().pulseWidth100To255[patchCache->srcPartial.wg.pulseWidth];  	if (pulseWidthVal < 0) {  		pulseWidthVal = 0;  	} else if (pulseWidthVal > 255) { @@ -175,6 +194,7 @@ float Partial::getPCMSample(unsigned int position) {  }  unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) { +	const Tables &tables = Tables::getInstance();  	if (!isActive() || alreadyOutputed) {  		return 0;  	} @@ -197,6 +217,9 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  			deactivate();  			break;  		} + +		Bit16u pitch = tvp->nextPitch(); +  		// SEMI-CONFIRMED: From sample analysis:  		// (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc.  		// This gives results within +/- 2 at the output (before any DAC bitshifting) @@ -206,10 +229,17 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  		// positive amps, so negative still needs to be explored, as well as lower levels.  		//  		// Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing. + +#if MT32EMU_ACCURATE_WG == 1  		float amp = EXP2F((32772 - ampRampVal / 2048) / -2048.0f); +		float freq = EXP2F(pitch / 4096.0f - 16.0f) * 32000.0f; +#else +		static const float ampFactor = EXP2F(32772 / -2048.0f); +		float amp = EXP2I(ampRampVal >> 10) * ampFactor; -		Bit16u pitch = tvp->nextPitch(); -		float freq = synth->tables.pitchToFreq[pitch]; +		static const float freqFactor = EXP2F(-16.0f) * 32000.0f; +		float freq = EXP2I(pitch) * freqFactor; +#endif  		if (patchCache->PCMPartial) {  			// Render PCM waveform @@ -225,8 +255,14 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  			// Linear interpolation  			float firstSample = synth->pcmROMData[pcmAddr + intPCMPosition]; -			float nextSample = getPCMSample(intPCMPosition + 1); -			sample = firstSample + (nextSample - firstSample) * (pcmPosition - intPCMPosition); +			// We observe that for partial structures with ring modulation the interpolation is not applied to the slave PCM partial. +			// It's assumed that the multiplication circuitry intended to perform the interpolation on the slave PCM partial +			// is borrowed by the ring modulation circuit (or the LA32 chip has a similar lack of resources assigned to each partial pair). +			if (pair == NULL || mixType == 0 || structurePosition == 0) { +				sample = firstSample + (getPCMSample(intPCMPosition + 1) - firstSample) * (pcmPosition - intPCMPosition); +			} else { +				sample = firstSample; +			}  			float newPCMPosition = pcmPosition + positionDelta;  			if (pcmWave->loop) { @@ -247,8 +283,8 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  			// res corresponds to a value set in an LA32 register  			Bit8u res = patchCache->srcPartial.tvf.resonance + 1; -			// EXP2F(1.0f - (32 - res) / 4.0f); -			float resAmp = synth->tables.resAmpMax[res]; +			// Using tiny exact table for computation of EXP2F(1.0f - (32 - res) / 4.0f) +			float resAmp = tables.resAmpMax[res];  			// The cutoffModifier may not be supposed to be directly added to the cutoff -  			// it may for example need to be multiplied in some way. @@ -268,7 +304,8 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  				cosineLen *= EXP2F((cutoffVal - 128.0f) / -16.0f); // found from sample analysis  #else -				cosineLen *= synth->tables.cutoffToCosineLen[Bit32u((cutoffVal - 128.0f) * 8.0f)]; +				static const float cosineLenFactor = EXP2F(128.0f / -16.0f); +				cosineLen *= EXP2I(Bit32u((256.0f - cutoffVal) * 256.0f)) * cosineLenFactor;  #endif  			} @@ -281,7 +318,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  			float pulseLen = 0.5f;  			if (pulseWidthVal > 128) { -				pulseLen += synth->tables.pulseLenFactor[pulseWidthVal - 128]; +				pulseLen += tables.pulseLenFactor[pulseWidthVal - 128];  			}  			pulseLen *= waveLen; @@ -303,7 +340,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  				resAmp *= sinf(FLOAT_PI * (cutoffVal - 128.0f) / 32.0f);  #else -				resAmp *= synth->tables.sinf10[Bit32u(64 * (cutoffVal - 128.0f))]; +				resAmp *= tables.sinf10[Bit32u(64 * (cutoffVal - 128.0f))];  #endif  			} @@ -314,7 +351,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  				sample = -cosf(FLOAT_PI * relWavePos / cosineLen);  #else -				sample = -synth->tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) + 1024]; +				sample = -tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) + 1024];  #endif  			} else @@ -328,7 +365,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  				sample = cosf(FLOAT_PI * (relWavePos - (cosineLen + hLen)) / cosineLen);  #else -				sample = synth->tables.sinf10[Bit32u(2048.0f * (relWavePos - (cosineLen + hLen)) / cosineLen) + 1024]; +				sample = tables.sinf10[Bit32u(2048.0f * (relWavePos - (cosineLen + hLen)) / cosineLen) + 1024];  #endif  			} else { @@ -343,7 +380,8 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  				sample *= EXP2F(-0.125f * (128.0f - cutoffVal));  #else -				sample *= synth->tables.cutoffToFilterAmp[Bit32u(cutoffVal * 8.0f)]; +				static const float cutoffAttenuationFactor = EXP2F(-0.125f * 128.0f); +				sample *= EXP2I(Bit32u(512.0f * cutoffVal)) * cutoffAttenuationFactor;  #endif  			} else { @@ -363,11 +401,17 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  				resSample *= sinf(FLOAT_PI * relWavePos / cosineLen);  #else -				resSample *= synth->tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) & 4095]; +				resSample *= tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) & 4095];  #endif  				// Resonance sine amp -				float resAmpFade = EXP2F(-synth->tables.resAmpFadeFactor[res >> 2] * (relWavePos / cosineLen));	// seems to be exact +				float resAmpFadeLog2 = -tables.resAmpFadeFactor[res >> 2] * (relWavePos / cosineLen); // seems to be exact +#if MT32EMU_ACCURATE_WG == 1 +				float resAmpFade = EXP2F(resAmpFadeLog2); +#else +				static const float resAmpFadeFactor = EXP2F(-30.0f); +				float resAmpFade = (resAmpFadeLog2 < -30.0f) ? 0.0f : EXP2I(Bit32u((30.0f + resAmpFadeLog2) * 4096.0f)) * resAmpFadeFactor; +#endif  				// Now relWavePos set negative to the left from center of any cosine  				relWavePos = wavePos; @@ -388,7 +432,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  					resAmpFade *= 0.5f * (1.0f - cosf(FLOAT_PI * relWavePos / (0.5f * cosineLen)));  #else -					resAmpFade *= 0.5f * (1.0f + synth->tables.sinf10[Bit32s(2048.0f * relWavePos / (0.5f * cosineLen)) + 3072]); +					resAmpFade *= 0.5f * (1.0f + tables.sinf10[Bit32s(2048.0f * relWavePos / (0.5f * cosineLen)) + 3072]);  #endif  				} @@ -400,7 +444,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)  #if MT32EMU_ACCURATE_WG == 1  				sample *= cosf(FLOAT_2PI * wavePos / waveLen);  #else -				sample *= synth->tables.sinf10[(Bit32u(4096.0f * wavePos / waveLen) & 4095) + 1024]; +				sample *= tables.sinf10[(Bit32u(4096.0f * wavePos / waveLen) & 4095) + 1024];  #endif  			} @@ -516,10 +560,9 @@ bool Partial::produceOutput(float *leftBuf, float *rightBuf, unsigned long lengt  			}  		}  		if (numGenerated > pairNumGenerated) { -			if (mixType == 1) { -				mixBuffersRingMix(partialBuf + pairNumGenerated, NULL, numGenerated - pairNumGenerated); -			} else { -				mixBuffersRing(partialBuf + pairNumGenerated, NULL, numGenerated - pairNumGenerated); +			if (mixType == 2) { +				numGenerated = pairNumGenerated; +				deactivate();  			}  		}  	} @@ -555,3 +598,5 @@ void Partial::startDecayAll() {  	tvp->startDecay();  	tvf->startDecay();  } + +} diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp index 0a6be826d6..f8c2dbcd48 100644 --- a/audio/softsynth/mt32/PartialManager.cpp +++ b/audio/softsynth/mt32/PartialManager.cpp @@ -20,7 +20,7 @@  #include "mt32emu.h"  #include "PartialManager.h" -using namespace MT32Emu; +namespace MT32Emu {  PartialManager::PartialManager(Synth *useSynth, Part **useParts) {  	synth = useSynth; @@ -248,3 +248,5 @@ const Partial *PartialManager::getPartial(unsigned int partialNum) const {  	}  	return partialTable[partialNum];  } + +} diff --git a/audio/softsynth/mt32/Poly.cpp b/audio/softsynth/mt32/Poly.cpp index a2f00db73c..c45391f672 100644 --- a/audio/softsynth/mt32/Poly.cpp +++ b/audio/softsynth/mt32/Poly.cpp @@ -58,6 +58,9 @@ bool Poly::noteOff(bool pedalHeld) {  		return false;  	}  	if (pedalHeld) { +		if (state == POLY_Held) { +			return false; +		}  		state = POLY_Held;  	} else {  		startDecay(); diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp index 7a1b5c2275..d47d954746 100644 --- a/audio/softsynth/mt32/Synth.cpp +++ b/audio/softsynth/mt32/Synth.cpp @@ -423,7 +423,6 @@ bool Synth::open(SynthProperties &useProp) {  #if MT32EMU_MONITOR_INIT  	printDebug("Initialising Constant Tables");  #endif -	tables.init();  #if !MT32EMU_REDUCE_REVERB_MEMORY  	for (int i = 0; i < 4; i++) {  		reverbModels[i]->open(useProp.sampleRate); @@ -627,6 +626,11 @@ void Synth::playMsg(Bit32u msg) {  		return;  	}  	playMsgOnPart(part, code, note, velocity); + +	// This ensures minimum 1-sample delay between sequential MIDI events +	// Without this, a sequence of NoteOn and immediately succeeding NoteOff messages is always silent +	// Technically, it's also impossible to send events through the MIDI interface faster than about each millisecond +	prerender();  }  void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) { diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h index ccabce7282..a30df62911 100644 --- a/audio/softsynth/mt32/Synth.h +++ b/audio/softsynth/mt32/Synth.h @@ -321,7 +321,6 @@ private:  	Bit32u renderedSampleCount; -	Tables tables;  	MemParams mt32ram, mt32default; diff --git a/audio/softsynth/mt32/TVA.cpp b/audio/softsynth/mt32/TVA.cpp index f3e3f7bbc7..c581259a38 100644 --- a/audio/softsynth/mt32/TVA.cpp +++ b/audio/softsynth/mt32/TVA.cpp @@ -154,7 +154,7 @@ void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartial  	playing = true; -	Tables *tables = &partial->getSynth()->tables; +	const Tables *tables = &Tables::getInstance();  	int key = partial->getPoly()->getKey();  	int velocity = partial->getPoly()->getVelocity(); @@ -215,7 +215,7 @@ void TVA::recalcSustain() {  		return;  	}  	// We're sustaining. Recalculate all the values -	Tables *tables = &partial->getSynth()->tables; +	const Tables *tables = &Tables::getInstance();  	int newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());  	newTarget += partialParam->tva.envLevel[3];  	// Since we're in TVA_PHASE_SUSTAIN at this point, we know that target has been reached and an interrupt fired, so we can rely on it being the current amp. @@ -241,7 +241,7 @@ int TVA::getPhase() const {  }  void TVA::nextPhase() { -	Tables *tables = &partial->getSynth()->tables; +	const Tables *tables = &Tables::getInstance();  	if (phase >= TVA_PHASE_DEAD || !playing) {  		partial->getSynth()->printDebug("TVA::nextPhase(): Shouldn't have got here with phase %d, playing=%s", phase, playing ? "true" : "false"); diff --git a/audio/softsynth/mt32/TVF.cpp b/audio/softsynth/mt32/TVF.cpp index 80b592ea67..47c4917632 100644 --- a/audio/softsynth/mt32/TVF.cpp +++ b/audio/softsynth/mt32/TVF.cpp @@ -117,7 +117,7 @@ void TVF::reset(const TimbreParam::PartialParam *newPartialParam, unsigned int b  	unsigned int key = partial->getPoly()->getKey();  	unsigned int velocity = partial->getPoly()->getVelocity(); -	Tables *tables = &partial->getSynth()->tables; +	const Tables *tables = &Tables::getInstance();  	baseCutoff = calcBaseCutoff(newPartialParam, basePitch, key);  #if MT32EMU_MONITOR_TVF >= 1 @@ -179,7 +179,7 @@ void TVF::startDecay() {  }  void TVF::nextPhase() { -	Tables *tables = &partial->getSynth()->tables; +	const Tables *tables = &Tables::getInstance();  	int newPhase = phase + 1;  	switch (newPhase) { diff --git a/audio/softsynth/mt32/TVP.cpp b/audio/softsynth/mt32/TVP.cpp index 0b339e8d71..32a6067841 100644 --- a/audio/softsynth/mt32/TVP.cpp +++ b/audio/softsynth/mt32/TVP.cpp @@ -171,9 +171,14 @@ void TVP::updatePitch() {  	if (newPitch < 0) {  		newPitch = 0;  	} + +// Note: Temporary #ifdef until we have proper "quirk" configuration +// This is about right emulation of MT-32 GEN0 quirk exploited in Colonel's Bequest timbre "Lightning" +#ifndef MT32EMU_QUIRK_PITCH_ENVELOPE_OVERFLOW_MT32  	if (newPitch > 59392) {  		newPitch = 59392;  	} +#endif  	pitch = (Bit16u)newPitch;  	// FIXME: We're doing this here because that's what the CM-32L does - we should probably move this somewhere more appropriate in future. diff --git a/audio/softsynth/mt32/Tables.cpp b/audio/softsynth/mt32/Tables.cpp index c9bd40b7a4..b6e63840bc 100644 --- a/audio/softsynth/mt32/Tables.cpp +++ b/audio/softsynth/mt32/Tables.cpp @@ -22,18 +22,14 @@  #include "mt32emu.h"  #include "mmath.h" -using namespace MT32Emu; +namespace MT32Emu { -Tables::Tables() { -	initialised = false; +const Tables &Tables::getInstance() { +	static const Tables instance; +	return instance;  } -void Tables::init() { -	if (initialised) { -		return; -	} -	initialised = true; - +Tables::Tables() {  	int lf;  	for (lf = 0; lf <= 100; lf++) {  		// CONFIRMED:KG: This matches a ROM table found by Mok @@ -83,19 +79,9 @@ void Tables::init() {  		pulseLenFactor[i] = (1.241857812f - pt) * pt;    // seems to be 2 ^ (5 / 16) = 1.241857812f  	} -	for (int i = 0; i < 65536; i++) { -		// Aka (slightly slower): EXP2F(pitchVal / 4096.0f - 16.0f) * 32000.0f -		pitchToFreq[i] = EXP2F(i / 4096.0f - 1.034215715f); -	} - -	// found from sample analysis -	for (int i = 0; i < 1024; i++) { -		cutoffToCosineLen[i] = EXP2F(i / -128.0f); -	} - -	// found from sample analysis -	for (int i = 0; i < 1024; i++) { -		cutoffToFilterAmp[i] = EXP2F(-0.125f * (128.0f - i / 8.0f)); +	// The LA32 chip presumably has such a table inside as the internal computaions seem to be performed using fixed point math with 12-bit fractions +	for (int i = 0; i < 4096; i++) { +		exp2[i] = EXP2F(i / 4096.0f);  	}  	// found from sample analysis @@ -117,3 +103,5 @@ void Tables::init() {  		sinf10[i] = sin(FLOAT_PI * i / 2048.0f);  	}  } + +} diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h index a2b5ff5d56..a63eaf6d26 100644 --- a/audio/softsynth/mt32/Tables.h +++ b/audio/softsynth/mt32/Tables.h @@ -20,14 +20,21 @@  namespace MT32Emu { +// Sample rate to use in mixing +const unsigned int SAMPLE_RATE = 32000; +  const int MIDDLEC = 60;  class Synth;  class Tables { -	bool initialised; +private: +	Tables(); +	Tables(Tables &);  public: +	static const Tables &getInstance(); +  	// Constant LUTs  	// CONFIRMED: This is used to convert several parameters to amp-modifying values in the TVA envelope: @@ -47,16 +54,11 @@ public:  	// CONFIRMED:  	Bit8u pulseWidth100To255[101]; +	float exp2[4096];  	float pulseLenFactor[128]; -	float pitchToFreq[65536]; -	float cutoffToCosineLen[1024]; -	float cutoffToFilterAmp[1024];  	float resAmpMax[32];  	float resAmpFadeFactor[8];  	float sinf10[5120]; - -	Tables(); -	void init();  };  } diff --git a/audio/softsynth/mt32/mmath.h b/audio/softsynth/mt32/mmath.h index 226d73e27e..25c79d57a9 100644 --- a/audio/softsynth/mt32/mmath.h +++ b/audio/softsynth/mt32/mmath.h @@ -52,6 +52,10 @@ static inline float EXP2F(float x) {  #endif  } +static inline float EXP2I(unsigned int i) { +	return float(1 << (i >> 12)) * Tables::getInstance().exp2[i & 0x0FFF]; +} +  static inline float EXP10F(float x) {  	return exp(FLOAT_LN_10 * x);  } diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h index 091819b95c..f10bc1faab 100644 --- a/audio/softsynth/mt32/mt32emu.h +++ b/audio/softsynth/mt32/mt32emu.h @@ -59,9 +59,16 @@  #define MT32EMU_MONITOR_TVA 0  #define MT32EMU_MONITOR_TVF 0 - -// 0: Use LUTs to speedup WG -// 1: Use precise float math +// The WG algorithm involves dozens of transcendent maths, e.g. exponents and trigonometry. +// Unfortunately, the majority of systems perform such computations inefficiently, +// standard math libs and FPUs make no optimisations for single precision floats, +// and use no LUTs to speedup computing internal taylor series. Though, there're rare exceptions, +// and there's a hope it will become common soon. +// So, this is the crucial point of speed optimisations. We have now eliminated all the transcendent maths +// within the critical path and use LUTs instead. +// Besides, since the LA32 chip is assumed to use similar LUTs inside, the overall emulation accuracy should be better. +// 0: Use LUTs to speedup WG. Most common setting. You can expect about 50% performance boost. +// 1: Use precise float math. Use this setting to achieve more accurate wave generator. If your system performs better with this setting, it is really notable. :)  #define MT32EMU_ACCURATE_WG 0  #define MT32EMU_USE_EXTINT 0  | 
