aboutsummaryrefslogtreecommitdiff
path: root/audio/softsynth/mt32/Partial.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'audio/softsynth/mt32/Partial.cpp')
-rw-r--r--audio/softsynth/mt32/Partial.cpp89
1 files changed, 67 insertions, 22 deletions
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();
}
+
+}