aboutsummaryrefslogtreecommitdiff
path: root/audio/softsynth/mt32/Partial.cpp
diff options
context:
space:
mode:
authorTarek Soliman2017-12-23 15:40:30 -0600
committerTarek Soliman2018-01-03 10:40:23 -0600
commit50d79c5f265aad592ae7f17209653ccbb1fde488 (patch)
tree1951526e3ff2910acb0588f3a23ba0e9e7e66544 /audio/softsynth/mt32/Partial.cpp
parentbb5e8d3a11711d409f89739cf3f054cd5bac8c4f (diff)
downloadscummvm-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.cpp181
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, &ampRamp);
@@ -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;