diff options
author | Filippos Karapetis | 2012-11-15 14:09:52 +0200 |
---|---|---|
committer | Filippos Karapetis | 2012-11-15 14:30:41 +0200 |
commit | 3233edf9b843f16018b6142b344b08dedae74d67 (patch) | |
tree | 147da92c466ad5646373521ed8088ce9b399356e /audio | |
parent | 97854df1a8d5b322fab12bad11ad4c859a28062b (diff) | |
download | scummvm-rg350-3233edf9b843f16018b6142b344b08dedae74d67.tar.gz scummvm-rg350-3233edf9b843f16018b6142b344b08dedae74d67.tar.bz2 scummvm-rg350-3233edf9b843f16018b6142b344b08dedae74d67.zip |
MT32: Update the MT32 emulator to the latest munt revision
Previous munt revision was 189f607c88e7404ad99abcf4b90f23b103003ed1
(Feb 09, 2012).
Current munt revision is f969d2081d41b669c1bfebd0026b5419c09517ae
(Nov 15, 2012)
Diffstat (limited to 'audio')
-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 |