aboutsummaryrefslogtreecommitdiff
path: root/audio/softsynth
diff options
context:
space:
mode:
authorFilippos Karapetis2012-11-15 14:09:52 +0200
committerFilippos Karapetis2012-11-15 14:30:41 +0200
commit3233edf9b843f16018b6142b344b08dedae74d67 (patch)
tree147da92c466ad5646373521ed8088ce9b399356e /audio/softsynth
parent97854df1a8d5b322fab12bad11ad4c859a28062b (diff)
downloadscummvm-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/softsynth')
-rw-r--r--audio/softsynth/mt32/AReverbModel.cpp4
-rw-r--r--audio/softsynth/mt32/DelayReverb.cpp4
-rw-r--r--audio/softsynth/mt32/FreeverbModel.cpp4
-rw-r--r--audio/softsynth/mt32/LA32Ramp.cpp5
-rw-r--r--audio/softsynth/mt32/Part.cpp9
-rw-r--r--audio/softsynth/mt32/Partial.cpp89
-rw-r--r--audio/softsynth/mt32/PartialManager.cpp4
-rw-r--r--audio/softsynth/mt32/Poly.cpp3
-rw-r--r--audio/softsynth/mt32/Synth.cpp6
-rw-r--r--audio/softsynth/mt32/Synth.h1
-rw-r--r--audio/softsynth/mt32/TVA.cpp6
-rw-r--r--audio/softsynth/mt32/TVF.cpp4
-rw-r--r--audio/softsynth/mt32/TVP.cpp5
-rw-r--r--audio/softsynth/mt32/Tables.cpp32
-rw-r--r--audio/softsynth/mt32/Tables.h16
-rw-r--r--audio/softsynth/mt32/mmath.h4
-rw-r--r--audio/softsynth/mt32/mt32emu.h13
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