aboutsummaryrefslogtreecommitdiff
path: root/audio/softsynth/mt32/AReverbModel.cpp
diff options
context:
space:
mode:
authorFilippos Karapetis2012-12-25 23:22:36 +0200
committerFilippos Karapetis2012-12-25 23:48:25 +0200
commitd9e555afd5932c458f559034c6dca1df346ead4b (patch)
treee7d41c550ff221d47664dd7fe4b3c1d4c9aa8af8 /audio/softsynth/mt32/AReverbModel.cpp
parent0e5bfb66f03551f998405ce6674f3e6eab4a0150 (diff)
downloadscummvm-rg350-d9e555afd5932c458f559034c6dca1df346ead4b.tar.gz
scummvm-rg350-d9e555afd5932c458f559034c6dca1df346ead4b.tar.bz2
scummvm-rg350-d9e555afd5932c458f559034c6dca1df346ead4b.zip
MT32: Update the MT32 emulator to a newer munt commit
Previous munt commit was f969d20 (Nov 15, 2012) Current munt commit is 84b2819 (Dec 22, 2012) We are still missing the changes from commit 788f7b1 onwards (Dec 22, 2012). There are bigger ROM access-related changes from that point, which we'll have to integrate as well.
Diffstat (limited to 'audio/softsynth/mt32/AReverbModel.cpp')
-rw-r--r--audio/softsynth/mt32/AReverbModel.cpp252
1 files changed, 145 insertions, 107 deletions
diff --git a/audio/softsynth/mt32/AReverbModel.cpp b/audio/softsynth/mt32/AReverbModel.cpp
index 151f6c2c81..ec24394e71 100644
--- a/audio/softsynth/mt32/AReverbModel.cpp
+++ b/audio/softsynth/mt32/AReverbModel.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011, 2012 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
@@ -16,64 +16,97 @@
*/
#include "mt32emu.h"
-#include "AReverbModel.h"
-
-namespace MT32Emu {
-
-// Default reverb settings for modes 0-2
-static const unsigned int NUM_ALLPASSES = 6;
-static const unsigned int NUM_DELAYS = 5;
+#if MT32EMU_USE_REVERBMODEL == 1
-static const Bit32u MODE_0_ALLPASSES[] = {729, 78, 394, 994, 1250, 1889};
-static const Bit32u MODE_0_DELAYS[] = {846, 4, 1819, 778, 346};
-static const float MODE_0_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.9f};
-static const float MODE_0_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 1.01575f};
-
-static const Bit32u MODE_1_ALLPASSES[] = {176, 809, 1324, 1258};
-static const Bit32u MODE_1_DELAYS[] = {2262, 124, 974, 2516, 356};
-static const float MODE_1_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.95f};
-static const float MODE_1_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 1.01575f};
+#include "AReverbModel.h"
-static const Bit32u MODE_2_ALLPASSES[] = {78, 729, 994, 389};
-static const Bit32u MODE_2_DELAYS[] = {846, 4, 1819, 778, 346};
-static const float MODE_2_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f};
-static const float MODE_2_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f};
+// Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that
+// the reverb model implemented in the real devices consists of three series allpass filters preceded by a non-feedback comb (or a delay with a LPF)
+// and followed by three parallel comb filters
-const AReverbSettings AReverbModel::REVERB_MODE_0_SETTINGS = {MODE_0_ALLPASSES, MODE_0_DELAYS, MODE_0_TIMES, MODE_0_LEVELS, 0.687770909f, 0.5f, 0.5f};
-const AReverbSettings AReverbModel::REVERB_MODE_1_SETTINGS = {MODE_1_ALLPASSES, MODE_1_DELAYS, MODE_1_TIMES, MODE_1_LEVELS, 0.712025098f, 0.375f, 0.625f};
-const AReverbSettings AReverbModel::REVERB_MODE_2_SETTINGS = {MODE_2_ALLPASSES, MODE_2_DELAYS, MODE_2_TIMES, MODE_2_LEVELS, 0.939522749f, 0.0f, 0.0f};
+namespace MT32Emu {
-RingBuffer::RingBuffer(Bit32u newsize) {
- index = 0;
- size = newsize;
+// Because LA-32 chip makes it's output available to process by the Boss chip with a significant delay,
+// the Boss chip puts to the buffer the LA32 dry output when it is ready and performs processing of the _previously_ latched data.
+// Of course, the right way would be to use a dedicated variable for this, but our reverb model is way higher level,
+// so we can simply increase the input buffer size.
+static const Bit32u PROCESS_DELAY = 1;
+
+// Default reverb settings for modes 0-2. These correspond to CM-32L / LAPC-I "new" reverb settings. MT-32 reverb is a bit different.
+// Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog).
+
+static const Bit32u NUM_ALLPASSES = 3;
+static const Bit32u NUM_COMBS = 4; // Well, actually there are 3 comb filters, but the entrance LPF + delay can be perfectly processed via a comb here.
+
+static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78};
+static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632};
+static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960};
+static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145};
+static const Bit32u MODE_0_COMB_FACTOR[] = {0x3C, 0x60, 0x60, 0x60};
+static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
+ 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
+ 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
+static const Bit32u MODE_0_LEVELS[] = {0, 10*3, 10*5, 10*7, 11*9, 11*12, 11*15, 13*15};
+static const Bit32u MODE_0_LPF_AMP = 6;
+
+static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176};
+static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519};
+static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518};
+static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274};
+static const Bit32u MODE_1_COMB_FACTOR[] = {0x30, 0x60, 0x60, 0x60};
+static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
+ 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
+ 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
+static const Bit32u MODE_1_LEVELS[] = {0, 10*3, 11*5, 11*7, 11*9, 11*12, 11*15, 14*15};
+static const Bit32u MODE_1_LPF_AMP = 6;
+
+static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157};
+static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539};
+static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769};
+static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1};
+static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20};
+static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0,
+ 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0,
+ 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0};
+static const Bit32u MODE_2_LEVELS[] = {0, 10*3, 11*5, 11*7, 11*9, 11*12, 12*15, 14*15};
+static const Bit32u MODE_2_LPF_AMP = 8;
+
+static const AReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_ALLPASSES, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_LEVELS, MODE_0_LPF_AMP};
+static const AReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_ALLPASSES, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_LEVELS, MODE_1_LPF_AMP};
+static const AReverbSettings REVERB_MODE_2_SETTINGS = {MODE_2_ALLPASSES, MODE_2_COMBS, MODE_2_OUTL, MODE_2_OUTR, MODE_2_COMB_FACTOR, MODE_2_COMB_FEEDBACK, MODE_2_LEVELS, MODE_2_LPF_AMP};
+
+static const AReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_0_SETTINGS};
+
+RingBuffer::RingBuffer(const Bit32u newsize) : size(newsize), index(0) {
buffer = new float[size];
}
RingBuffer::~RingBuffer() {
delete[] buffer;
buffer = NULL;
- size = 0;
}
float RingBuffer::next() {
- index++;
- if (index >= size) {
+ if (++index >= size) {
index = 0;
}
return buffer[index];
}
-bool RingBuffer::isEmpty() {
+bool RingBuffer::isEmpty() const {
if (buffer == NULL) return true;
float *buf = buffer;
- float total = 0;
+ float max = 0.001f;
for (Bit32u i = 0; i < size; i++) {
- total += (*buf < 0 ? -*buf : *buf);
+ if ((*buf < -max) || (*buf > max)) return false;
buf++;
}
- return ((total / size) < .0002 ? true : false);
+ return true;
}
void RingBuffer::mute() {
@@ -83,44 +116,51 @@ void RingBuffer::mute() {
}
}
-AllpassFilter::AllpassFilter(Bit32u useSize) : RingBuffer(useSize) {
-}
-
-Delay::Delay(Bit32u useSize) : RingBuffer(useSize) {
-}
+AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {}
-float AllpassFilter::process(float in) {
- // This model corresponds to the allpass filter implementation in the real CM-32L device
+float AllpassFilter::process(const float in) {
+ // This model corresponds to the allpass filter implementation of the real CM-32L device
// found from sample analysis
- float out;
-
- out = next();
+ const float bufferOut = next();
// store input - feedback / 2
- buffer[index] = in - 0.5f * out;
+ buffer[index] = in - 0.5f * bufferOut;
// return buffer output + feedforward / 2
- return out + 0.5f * buffer[index];
+ return bufferOut + 0.5f * buffer[index];
}
-float Delay::process(float in) {
- // Implements a very simple delay
+CombFilter::CombFilter(const Bit32u useSize) : RingBuffer(useSize) {}
- float out;
+void CombFilter::process(const float in) {
+ // This model corresponds to the comb filter implementation of the real CM-32L device
+ // found from sample analysis
- out = next();
+ // the previously stored value
+ float last = buffer[index];
- // store input
- buffer[index] = in;
+ // prepare input + feedback
+ float filterIn = in + next() * feedbackFactor;
- // return buffer output
- return out;
+ // store input + feedback processed by a low-pass filter
+ buffer[index] = filterFactor * last - filterIn;
}
-AReverbModel::AReverbModel(const AReverbSettings *useSettings) : allpasses(NULL), delays(NULL), currentSettings(useSettings) {
+float CombFilter::getOutputAt(const Bit32u outIndex) const {
+ return buffer[(size + index - outIndex) % size];
}
+void CombFilter::setFeedbackFactor(const float useFeedbackFactor) {
+ feedbackFactor = useFeedbackFactor;
+}
+
+void CombFilter::setFilterFactor(const float useFilterFactor) {
+ filterFactor = useFilterFactor;
+}
+
+AReverbModel::AReverbModel(const ReverbMode mode) : allpasses(NULL), combs(NULL), currentSettings(*REVERB_SETTINGS[mode]) {}
+
AReverbModel::~AReverbModel() {
close();
}
@@ -130,12 +170,14 @@ void AReverbModel::open(unsigned int /*sampleRate*/) {
// IIR filter values depend on sample rate as well
allpasses = new AllpassFilter*[NUM_ALLPASSES];
for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
- allpasses[i] = new AllpassFilter(currentSettings->allpassSizes[i]);
+ allpasses[i] = new AllpassFilter(currentSettings.allpassSizes[i]);
}
- delays = new Delay*[NUM_DELAYS];
- for (Bit32u i = 0; i < NUM_DELAYS; i++) {
- delays[i] = new Delay(currentSettings->delaySizes[i]);
+ combs = new CombFilter*[NUM_COMBS];
+ for (Bit32u i = 0; i < NUM_COMBS; i++) {
+ combs[i] = new CombFilter(currentSettings.combSizes[i]);
+ combs[i]->setFilterFactor(currentSettings.filterFactor[i] / 256.0f);
}
+ lpfAmp = currentSettings.lpfAmp / 16.0f;
mute();
}
@@ -150,84 +192,78 @@ void AReverbModel::close() {
delete[] allpasses;
allpasses = NULL;
}
- if (delays != NULL) {
- for (Bit32u i = 0; i < NUM_DELAYS; i++) {
- if (delays[i] != NULL) {
- delete delays[i];
- delays[i] = NULL;
+ if (combs != NULL) {
+ for (Bit32u i = 0; i < NUM_COMBS; i++) {
+ if (combs[i] != NULL) {
+ delete combs[i];
+ combs[i] = NULL;
}
}
- delete[] delays;
- delays = NULL;
+ delete[] combs;
+ combs = NULL;
}
}
void AReverbModel::mute() {
+ if (allpasses == NULL || combs == NULL) return;
for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
allpasses[i]->mute();
}
- for (Bit32u i = 0; i < NUM_DELAYS; i++) {
- delays[i]->mute();
+ for (Bit32u i = 0; i < NUM_COMBS; i++) {
+ combs[i]->mute();
}
- filterhist1 = 0;
- filterhist2 = 0;
- combhist = 0;
}
void AReverbModel::setParameters(Bit8u time, Bit8u level) {
// FIXME: wetLevel definitely needs ramping when changed
// Although, most games don't set reverb level during MIDI playback
- decayTime = currentSettings->decayTimes[time];
- wetLevel = currentSettings->wetLevels[level];
+ if (combs == NULL) return;
+ for (Bit32u i = 0; i < NUM_COMBS; i++) {
+ combs[i]->setFeedbackFactor(currentSettings.decayTimes[(i << 3) + (time & 7)] / 256.0f);
+ }
+ wetLevel = 0.5f * lpfAmp * currentSettings.wetLevels[(level & 7)] / 256.0f;
}
bool AReverbModel::isActive() const {
- bool bActive = false;
for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
- bActive |= !allpasses[i]->isEmpty();
+ if (!allpasses[i]->isEmpty()) return true;
}
- for (Bit32u i = 0; i < NUM_DELAYS; i++) {
- bActive |= !delays[i]->isEmpty();
+ for (Bit32u i = 0; i < NUM_COMBS; i++) {
+ if (!combs[i]->isEmpty()) return true;
}
- return bActive;
+ return false;
}
void AReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) {
-// Three series allpass filters followed by a delay, fourth allpass filter and another delay
- float dry, link, outL1, outL2, outR1, outR2;
+ float dry, link, outL1;
for (unsigned long i = 0; i < numSamples; i++) {
- dry = *inLeft + *inRight;
+ dry = wetLevel * (*inLeft + *inRight);
- // Implementation of 2-stage IIR single-pole low-pass filter
- // found at the entrance of reverb processing on real devices
- filterhist1 += (dry - filterhist1) * currentSettings->filtVal;
- filterhist2 += (filterhist1 - filterhist2) * currentSettings->filtVal;
+ // Get the last stored sample before processing in order not to loose it
+ link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1);
- link = allpasses[0]->process(-filterhist2);
- link = allpasses[1]->process(link);
+ combs[0]->process(-dry);
- // this implements a comb filter cross-linked with the fourth allpass filter
- link += combhist * decayTime;
+ link = allpasses[0]->process(link);
+ link = allpasses[1]->process(link);
link = allpasses[2]->process(link);
- link = delays[0]->process(link);
- outL1 = link;
- link = allpasses[3]->process(link);
- link = delays[1]->process(link);
- outR1 = link;
- link = allpasses[4]->process(link);
- link = delays[2]->process(link);
- outL2 = link;
- link = allpasses[5]->process(link);
- link = delays[3]->process(link);
- outR2 = link;
- link = delays[4]->process(link);
-
- // comb filter end point
- combhist = combhist * currentSettings->damp1 + link * currentSettings->damp2;
-
- *outLeft = (outL1 + outL2) * wetLevel;
- *outRight = (outR1 + outR2) * wetLevel;
+
+ // If the output position is equal to the comb size, get it now in order not to loose it
+ outL1 = 1.5f * combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1);
+
+ combs[1]->process(link);
+ combs[2]->process(link);
+ combs[3]->process(link);
+
+ link = outL1 + 1.5f * combs[2]->getOutputAt(currentSettings.outLPositions[1]);
+ link += combs[3]->getOutputAt(currentSettings.outLPositions[2]);
+ *outLeft = link;
+
+ link = 1.5f * combs[1]->getOutputAt(currentSettings.outRPositions[0]);
+ link += 1.5f * combs[2]->getOutputAt(currentSettings.outRPositions[1]);
+ link += combs[3]->getOutputAt(currentSettings.outRPositions[2]);
+ *outRight = link;
inLeft++;
inRight++;
@@ -237,3 +273,5 @@ void AReverbModel::process(const float *inLeft, const float *inRight, float *out
}
}
+
+#endif