mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-03 09:23:37 +00:00
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)
This commit is contained in:
parent
97854df1a8
commit
3233edf9b8
@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
float amp = EXP2F((32772 - ampRampVal / 2048) / -2048.0f);
|
||||
|
||||
Bit16u pitch = tvp->nextPitch();
|
||||
float freq = synth->tables.pitchToFreq[pitch];
|
||||
#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;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -58,6 +58,9 @@ bool Poly::noteOff(bool pedalHeld) {
|
||||
return false;
|
||||
}
|
||||
if (pedalHeld) {
|
||||
if (state == POLY_Held) {
|
||||
return false;
|
||||
}
|
||||
state = POLY_Held;
|
||||
} else {
|
||||
startDecay();
|
||||
|
@ -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) {
|
||||
|
@ -321,7 +321,6 @@ private:
|
||||
|
||||
Bit32u renderedSampleCount;
|
||||
|
||||
Tables tables;
|
||||
|
||||
MemParams mt32ram, mt32default;
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user