diff options
Diffstat (limited to 'audio/softsynth/mt32/Partial.cpp')
-rw-r--r-- | audio/softsynth/mt32/Partial.cpp | 89 |
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(); } + +} |