diff options
author | Tarek Soliman | 2017-12-23 15:40:30 -0600 |
---|---|---|
committer | Tarek Soliman | 2018-01-03 10:40:23 -0600 |
commit | 50d79c5f265aad592ae7f17209653ccbb1fde488 (patch) | |
tree | 1951526e3ff2910acb0588f3a23ba0e9e7e66544 /audio/softsynth/mt32/Partial.cpp | |
parent | bb5e8d3a11711d409f89739cf3f054cd5bac8c4f (diff) | |
download | scummvm-rg350-50d79c5f265aad592ae7f17209653ccbb1fde488.tar.gz scummvm-rg350-50d79c5f265aad592ae7f17209653ccbb1fde488.tar.bz2 scummvm-rg350-50d79c5f265aad592ae7f17209653ccbb1fde488.zip |
MT32: Update to munt 2.3.0
This uses upstream commit 939cc986d9ffd044f8c6149361127ad5d94e430f
Closes gh-1091
Diffstat (limited to 'audio/softsynth/mt32/Partial.cpp')
-rw-r--r-- | audio/softsynth/mt32/Partial.cpp | 181 |
1 files changed, 120 insertions, 61 deletions
diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp index 6afef364df..60c06b7be7 100644 --- a/audio/softsynth/mt32/Partial.cpp +++ b/audio/softsynth/mt32/Partial.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2017 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -21,6 +21,7 @@ #include "Partial.h" #include "Part.h" +#include "PartialManager.h" #include "Poly.h" #include "Synth.h" #include "Tables.h" @@ -33,10 +34,26 @@ namespace MT32Emu { 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}; +// We assume the pan is applied using the same 13-bit multiplier circuit that is also used for ring modulation +// because of the observed sample overflow, so the panSetting values are likely mapped in a similar way via a LUT. +// FIXME: Sample analysis suggests that the use of panSetting is linear, but there are some quirks that still need to be resolved. +static Bit32s getPANFactor(Bit32s panSetting) { + static const Bit32u PAN_FACTORS_COUNT = 15; + static Bit32s PAN_FACTORS[PAN_FACTORS_COUNT]; + static bool firstRun = true; + + if (firstRun) { + firstRun = false; + for (Bit32u i = 1; i < PAN_FACTORS_COUNT; i++) { + PAN_FACTORS[i] = Bit32s(0.5 + i * 8192.0 / double(PAN_FACTORS_COUNT - 1)); + } + } + return PAN_FACTORS[panSetting]; +} -Partial::Partial(Synth *useSynth, int useDebugPartialNum) : - synth(useSynth), debugPartialNum(useDebugPartialNum), sampleNum(0) { +Partial::Partial(Synth *useSynth, int usePartialIndex) : + synth(useSynth), partialIndex(usePartialIndex), sampleNum(0), + floatMode(useSynth->getSelectedRendererType() == RendererType_FLOAT) { // Initialisation of tva, tvp and tvf uses 'this' pointer // and thus should not be in the initializer list to avoid a compiler warning tva = new TVA(this, &Ramp); @@ -45,9 +62,20 @@ Partial::Partial(Synth *useSynth, int useDebugPartialNum) : ownerPart = -1; poly = NULL; pair = NULL; + switch (synth->getSelectedRendererType()) { + case RendererType_BIT16S: + la32Pair = new LA32IntPartialPair; + break; + case RendererType_FLOAT: + la32Pair = new LA32FloatPartialPair; + break; + default: + la32Pair = NULL; + } } Partial::~Partial() { + delete la32Pair; delete tva; delete tvp; delete tvf; @@ -55,7 +83,7 @@ Partial::~Partial() { // Only used for debugging purposes int Partial::debugGetPartialNum() const { - return debugPartialNum; + return partialIndex; } // Only used for debugging purposes @@ -85,6 +113,7 @@ void Partial::deactivate() { return; } ownerPart = -1; + synth->partialManager->partialDeactivated(partialIndex); if (poly != NULL) { poly->partialDeactivated(this); } @@ -93,9 +122,9 @@ void Partial::deactivate() { synth->printPartialUsage(sampleNum); #endif if (isRingModulatingSlave()) { - pair->la32Pair.deactivate(LA32PartialPair::SLAVE); + pair->la32Pair->deactivate(LA32PartialPair::SLAVE); } else { - la32Pair.deactivate(LA32PartialPair::MASTER); + la32Pair->deactivate(LA32PartialPair::MASTER); if (hasRingModulatingSlave()) { pair->deactivate(); pair = NULL; @@ -108,7 +137,7 @@ void Partial::deactivate() { void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *usePatchCache, const MemParams::RhythmTemp *rhythmTemp, Partial *pairPartial) { if (usePoly == NULL || usePatchCache == NULL) { - synth->printDebug("[Partial %d] *** Error: Starting partial for owner %d, usePoly=%s, usePatchCache=%s", debugPartialNum, ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", usePatchCache == NULL ? "*** NULL ***" : "OK"); + synth->printDebug("[Partial %d] *** Error: Starting partial for owner %d, usePoly=%s, usePatchCache=%s", partialIndex, ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", usePatchCache == NULL ? "*** NULL ***" : "OK"); return; } patchCache = usePatchCache; @@ -137,17 +166,17 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us leftPanValue = synth->reversedStereoEnabled ? 14 - panSetting : panSetting; rightPanValue = 14 - leftPanValue; -#if !MT32EMU_USE_FLOAT_SAMPLES - leftPanValue = PAN_FACTORS[leftPanValue]; - rightPanValue = PAN_FACTORS[rightPanValue]; -#endif + if (!floatMode) { + leftPanValue = getPANFactor(leftPanValue); + rightPanValue = getPANFactor(rightPanValue); + } // 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. + // In this case that timbre can sound totally different depending on 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. @@ -155,8 +184,7 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us // 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) { + if (partialIndex & 4) { leftPanValue = -leftPanValue; rightPanValue = -rightPanValue; } @@ -192,11 +220,11 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us LA32PartialPair *useLA32Pair; if (isRingModulatingSlave()) { pairType = LA32PartialPair::SLAVE; - useLA32Pair = &pair->la32Pair; + useLA32Pair = pair->la32Pair; } else { pairType = LA32PartialPair::MASTER; - la32Pair.init(hasRingModulatingSlave(), mixType == 1); - useLA32Pair = &la32Pair; + la32Pair->init(hasRingModulatingSlave(), mixType == 1); + useLA32Pair = la32Pair; } if (isPCM()) { useLA32Pair->initPCM(pairType, &synth->pcmROMData[pcmWave->addr], pcmWave->len, pcmWave->loop); @@ -204,7 +232,7 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us useLA32Pair->initSynth(pairType, (patchCache->waveform & 1) != 0, pulseWidthVal, patchCache->srcPartial.tvf.resonance + 1); } if (!hasRingModulatingSlave()) { - la32Pair.deactivate(LA32PartialPair::SLAVE); + la32Pair->deactivate(LA32PartialPair::SLAVE); } } @@ -245,6 +273,10 @@ bool Partial::isRingModulatingSlave() const { return pair != NULL && structurePosition == 1 && (mixType == 1 || mixType == 2); } +bool Partial::isRingModulatingNoMix() const { + return pair != NULL && ((structurePosition == 1 && mixType == 1) || mixType == 2); +} + bool Partial::isPCM() const { return pcmWave != NULL; } @@ -271,63 +303,90 @@ void Partial::backupCache(const PatchCache &cache) { } } -bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, Bit32u length) { +bool Partial::canProduceOutput() { if (!isActive() || alreadyOutputed || isRingModulatingSlave()) { return false; } if (poly == NULL) { - synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::produceOutput()!", debugPartialNum); + synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::produceOutput()!", partialIndex); return false; } - alreadyOutputed = true; + return 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; - } +template <class LA32PairImpl> +bool Partial::generateNextSample(LA32PairImpl *la32PairImpl) { + if (!tva->isPlaying() || !la32PairImpl->isActive(LA32PartialPair::MASTER)) { + deactivate(); + return false; + } + la32PairImpl->generateNextSample(LA32PartialPair::MASTER, getAmpValue(), tvp->nextPitch(), getCutoffValue()); + if (hasRingModulatingSlave()) { + la32PairImpl->generateNextSample(LA32PartialPair::SLAVE, pair->getAmpValue(), pair->tvp->nextPitch(), pair->getCutoffValue()); + if (!pair->tva->isPlaying() || !la32PairImpl->isActive(LA32PartialPair::SLAVE)) { + pair->deactivate(); + if (mixType == 2) { + deactivate(); + return false; } } + } + return true; +} - // 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 - Sample leftOut = (sample * (float)leftPanValue) / 14.0f; - Sample rightOut = (sample * (float)rightPanValue) / 14.0f; - *(leftBuf++) += leftOut; - *(rightBuf++) += rightOut; -#else - // 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::clipSampleEx(SampleEx(*leftBuf) + SampleEx(leftOut)); - *rightBuf = Synth::clipSampleEx(SampleEx(*rightBuf) + SampleEx(rightOut)); - leftBuf++; - rightBuf++; -#endif +void Partial::produceAndMixSample(IntSample *&leftBuf, IntSample *&rightBuf, LA32IntPartialPair *la32IntPair) { + IntSampleEx sample = la32IntPair->nextOutSample(); + + // FIXME: 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. + + IntSampleEx leftOut = ((sample * leftPanValue) >> 13) + IntSampleEx(*leftBuf); + IntSampleEx rightOut = ((sample * rightPanValue) >> 13) + IntSampleEx(*rightBuf); + *(leftBuf++) = Synth::clipSampleEx(leftOut); + *(rightBuf++) = Synth::clipSampleEx(rightOut); +} + +void Partial::produceAndMixSample(FloatSample *&leftBuf, FloatSample *&rightBuf, LA32FloatPartialPair *la32FloatPair) { + FloatSample sample = la32FloatPair->nextOutSample(); + FloatSample leftOut = (sample * leftPanValue) / 14.0f; + FloatSample rightOut = (sample * rightPanValue) / 14.0f; + *(leftBuf++) += leftOut; + *(rightBuf++) += rightOut; +} + +template <class Sample, class LA32PairImpl> +bool Partial::doProduceOutput(Sample *leftBuf, Sample *rightBuf, Bit32u length, LA32PairImpl *la32PairImpl) { + if (!canProduceOutput()) return false; + alreadyOutputed = true; + + for (sampleNum = 0; sampleNum < length; sampleNum++) { + if (!generateNextSample(la32PairImpl)) break; + produceAndMixSample(leftBuf, rightBuf, la32PairImpl); } sampleNum = 0; return true; } +bool Partial::produceOutput(IntSample *leftBuf, IntSample *rightBuf, Bit32u length) { + if (floatMode) { + synth->printDebug("Partial: Invalid call to produceOutput()! Renderer = %d\n", synth->getSelectedRendererType()); + return false; + } + return doProduceOutput(leftBuf, rightBuf, length, static_cast<LA32IntPartialPair *>(la32Pair)); +} + +bool Partial::produceOutput(FloatSample *leftBuf, FloatSample *rightBuf, Bit32u length) { + if (!floatMode) { + synth->printDebug("Partial: Invalid call to produceOutput()! Renderer = %d\n", synth->getSelectedRendererType()); + return false; + } + return doProduceOutput(leftBuf, rightBuf, length, static_cast<LA32FloatPartialPair *>(la32Pair)); +} + bool Partial::shouldReverb() { if (!isActive()) { return false; |