diff options
Diffstat (limited to 'audio/softsynth/mt32/Partial.cpp')
-rw-r--r-- | audio/softsynth/mt32/Partial.cpp | 119 |
1 files changed, 60 insertions, 59 deletions
diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp index c7848f02d8..75e674074f 100644 --- a/audio/softsynth/mt32/Partial.cpp +++ b/audio/softsynth/mt32/Partial.cpp @@ -24,15 +24,10 @@ namespace MT32Emu { -#ifdef INACCURATE_SMOOTH_PAN -// Mok wanted an option for smoother panning, and we love Mok. -static const float PAN_NUMERATOR_NORMAL[] = {0.0f, 0.5f, 1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 5.5f, 6.0f, 6.5f, 7.0f}; -#else -// CONFIRMED by Mok: These NUMERATOR values (as bytes, not floats, obviously) are sent exactly like this to the LA32. -static const float PAN_NUMERATOR_NORMAL[] = {0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 3.0f, 4.0f, 4.0f, 5.0f, 5.0f, 6.0f, 6.0f, 7.0f}; -#endif -static const float PAN_NUMERATOR_MASTER[] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f}; -static const float PAN_NUMERATOR_SLAVE[] = {0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f}; +static const Bit8u PAN_NUMERATOR_MASTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7}; +static const Bit8u PAN_NUMERATOR_SLAVE[] = {0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7}; + +static const Bit32s PAN_FACTORS[] = {0, 18, 37, 55, 73, 91, 110, 128, 146, 165, 183, 201, 219, 238, 256}; Partial::Partial(Synth *useSynth, int useDebugPartialNum) : synth(useSynth), debugPartialNum(useDebugPartialNum), sampleNum(0) { @@ -116,24 +111,30 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us structurePosition = patchCache->structurePosition; Bit8u panSetting = rhythmTemp != NULL ? rhythmTemp->panpot : part->getPatchTemp()->panpot; - float panVal; if (mixType == 3) { if (structurePosition == 0) { - panVal = PAN_NUMERATOR_MASTER[panSetting]; + panSetting = PAN_NUMERATOR_MASTER[panSetting] << 1; } else { - panVal = PAN_NUMERATOR_SLAVE[panSetting]; + panSetting = PAN_NUMERATOR_SLAVE[panSetting] << 1; } // Do a normal mix independent of any pair partial. mixType = 0; pairPartial = NULL; } else { - panVal = PAN_NUMERATOR_NORMAL[panSetting]; + // Mok wanted an option for smoother panning, and we love Mok. +#ifndef INACCURATE_SMOOTH_PAN + // CONFIRMED by Mok: exactly bytes like this (right shifted?) are sent to the LA32. + panSetting &= 0x0E; +#endif } - // FIXME: Sample analysis suggests that the use of panVal is linear, but there are some some quirks that still need to be resolved. - // FIXME: I suppose this should be panVal / 8 and undoubtly integer, clarify ASAP - stereoVolume.leftVol = panVal / 7.0f; - stereoVolume.rightVol = 1.0f - stereoVolume.leftVol; + leftPanValue = synth->reversedStereoEnabled ? 14 - panSetting : panSetting; + rightPanValue = 14 - leftPanValue; + +#if !MT32EMU_USE_FLOAT_SAMPLES + leftPanValue = PAN_FACTORS[leftPanValue]; + rightPanValue = PAN_FACTORS[rightPanValue]; +#endif // 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. @@ -150,8 +151,8 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us // 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; + leftPanValue = -leftPanValue; + rightPanValue = -rightPanValue; } if (patchCache->PCMPartial) { @@ -230,39 +231,6 @@ Bit32u Partial::getCutoffValue() { return (tvf->getBaseCutoff() << 18) + cutoffModifierRampVal; } -unsigned long Partial::generateSamples(Sample *partialBuf, unsigned long length) { - if (!isActive() || alreadyOutputed) { - return 0; - } - if (poly == NULL) { - synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::generateSamples()!", debugPartialNum); - return 0; - } - alreadyOutputed = true; - - for (sampleNum = 0; sampleNum < length; sampleNum++) { - if (!tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::MASTER)) { - deactivate(); - break; - } - la32Pair.generateNextSample(LA32PartialPair::MASTER, getAmpValue(), tvp->nextPitch(), getCutoffValue()); - if (hasRingModulatingSlave()) { - la32Pair.generateNextSample(LA32PartialPair::SLAVE, pair->getAmpValue(), pair->tvp->nextPitch(), pair->getCutoffValue()); - if (!pair->tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::SLAVE)) { - pair->deactivate(); - if (mixType == 2) { - deactivate(); - break; - } - } - } - *(partialBuf++) = la32Pair.nextOutSample(); - } - unsigned long renderedSamples = sampleNum; - sampleNum = 0; - return renderedSamples; -} - bool Partial::hasRingModulatingSlave() const { return pair != NULL && structurePosition == 0 && (mixType == 1 || mixType == 2); } @@ -305,19 +273,52 @@ bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long len synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::produceOutput()!", debugPartialNum); return false; } - Sample buffer[MAX_SAMPLES_PER_RUN]; - unsigned long numGenerated = generateSamples(buffer, length); - for (unsigned int i = 0; i < numGenerated; i++) { + alreadyOutputed = true; + + for (sampleNum = 0; sampleNum < length; sampleNum++) { + if (!tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::MASTER)) { + deactivate(); + break; + } + la32Pair.generateNextSample(LA32PartialPair::MASTER, getAmpValue(), tvp->nextPitch(), getCutoffValue()); + if (hasRingModulatingSlave()) { + la32Pair.generateNextSample(LA32PartialPair::SLAVE, pair->getAmpValue(), pair->tvp->nextPitch(), pair->getCutoffValue()); + if (!pair->tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::SLAVE)) { + pair->deactivate(); + if (mixType == 2) { + deactivate(); + break; + } + } + } + + // Although, LA32 applies panning itself, we assume here it is applied in the mixer, not within a pair. + // Applying the pan value in the log-space looks like a waste of unlog resources. Though, it needs clarification. + Sample sample = la32Pair.nextOutSample(); + + // FIXME: Sample analysis suggests that the use of panVal is linear, but there are some quirks that still need to be resolved. #if MT32EMU_USE_FLOAT_SAMPLES - *(leftBuf++) += buffer[i] * stereoVolume.leftVol; - *(rightBuf++) += buffer[i] * stereoVolume.rightVol; + Sample leftOut = (sample * (float)leftPanValue) / 14.0f; + Sample rightOut = (sample * (float)rightPanValue) / 14.0f; + *(leftBuf++) += leftOut; + *(rightBuf++) += rightOut; #else - *leftBuf = Synth::clipBit16s((Bit32s)*leftBuf + Bit32s(buffer[i] * stereoVolume.leftVol)); - *rightBuf = Synth::clipBit16s((Bit32s)*rightBuf + Bit32s(buffer[i] * stereoVolume.rightVol)); + // FIXME: Dividing by 7 (or by 14 in a Mok-friendly way) looks of course pointless. Need clarification. + // FIXME2: LA32 may produce distorted sound in case if the absolute value of maximal amplitude of the input exceeds 8191 + // when the panning value is non-zero. Most probably the distortion occurs in the same way it does with ring modulation, + // and it seems to be caused by limited precision of the common multiplication circuit. + // From analysis of this overflow, it is obvious that the right channel output is actually found + // by subtraction of the left channel output from the input. + // Though, it is unknown whether this overflow is exploited somewhere. + Sample leftOut = Sample((sample * leftPanValue) >> 8); + Sample rightOut = Sample((sample * rightPanValue) >> 8); + *leftBuf = Synth::clipBit16s((Bit32s)*leftBuf + (Bit32s)leftOut); + *rightBuf = Synth::clipBit16s((Bit32s)*rightBuf + (Bit32s)rightOut); leftBuf++; rightBuf++; #endif } + sampleNum = 0; return true; } |