aboutsummaryrefslogtreecommitdiff
path: root/audio/softsynth/mt32/AReverbModel.cpp
diff options
context:
space:
mode:
authorEugene Sandulenko2012-02-09 11:20:45 +0200
committerEugene Sandulenko2012-02-09 11:31:03 +0200
commit030e155eeb7f82dc89315dbefa43e09a411c6110 (patch)
tree3b941d9841735fcc9878ab0663406d6be7a025aa /audio/softsynth/mt32/AReverbModel.cpp
parent06b52994619fb9a7ef9e54e50b5cc67d07f6a0a0 (diff)
downloadscummvm-rg350-030e155eeb7f82dc89315dbefa43e09a411c6110.tar.gz
scummvm-rg350-030e155eeb7f82dc89315dbefa43e09a411c6110.tar.bz2
scummvm-rg350-030e155eeb7f82dc89315dbefa43e09a411c6110.zip
MT32: Update MT-32 emulator to latest Munt code
Several changes against original code were made. They were intentionally kept to the minimum
Diffstat (limited to 'audio/softsynth/mt32/AReverbModel.cpp')
-rw-r--r--audio/softsynth/mt32/AReverbModel.cpp237
1 files changed, 237 insertions, 0 deletions
diff --git a/audio/softsynth/mt32/AReverbModel.cpp b/audio/softsynth/mt32/AReverbModel.cpp
new file mode 100644
index 0000000000..4ee6c87943
--- /dev/null
+++ b/audio/softsynth/mt32/AReverbModel.cpp
@@ -0,0 +1,237 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011 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
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "mt32emu.h"
+#include "AReverbModel.h"
+
+using namespace MT32Emu;
+
+// Default reverb settings for modes 0-2
+
+static const unsigned int NUM_ALLPASSES = 6;
+static const unsigned int NUM_DELAYS = 5;
+
+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};
+
+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};
+
+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};
+
+RingBuffer::RingBuffer(Bit32u newsize) {
+ index = 0;
+ size = newsize;
+ buffer = new float[size];
+}
+
+RingBuffer::~RingBuffer() {
+ delete[] buffer;
+ buffer = NULL;
+ size = 0;
+}
+
+float RingBuffer::next() {
+ index++;
+ if (index >= size) {
+ index = 0;
+ }
+ return buffer[index];
+}
+
+bool RingBuffer::isEmpty() {
+ if (buffer == NULL) return true;
+
+ float *buf = buffer;
+ float total = 0;
+ for (Bit32u i = 0; i < size; i++) {
+ total += (*buf < 0 ? -*buf : *buf);
+ buf++;
+ }
+ return ((total / size) < .0002 ? true : false);
+}
+
+void RingBuffer::mute() {
+ float *buf = buffer;
+ for (Bit32u i = 0; i < size; i++) {
+ *buf++ = 0;
+ }
+}
+
+AllpassFilter::AllpassFilter(Bit32u useSize) : RingBuffer(useSize) {
+}
+
+Delay::Delay(Bit32u useSize) : RingBuffer(useSize) {
+}
+
+float AllpassFilter::process(float in) {
+ // This model corresponds to the allpass filter implementation in the real CM-32L device
+ // found from sample analysis
+
+ float out;
+
+ out = next();
+
+ // store input - feedback / 2
+ buffer[index] = in - 0.5f * out;
+
+ // return buffer output + feedforward / 2
+ return out + 0.5f * buffer[index];
+}
+
+float Delay::process(float in) {
+ // Implements a very simple delay
+
+ float out;
+
+ out = next();
+
+ // store input
+ buffer[index] = in;
+
+ // return buffer output
+ return out;
+}
+
+AReverbModel::AReverbModel(const AReverbSettings *useSettings) : allpasses(NULL), delays(NULL), currentSettings(useSettings) {
+}
+
+AReverbModel::~AReverbModel() {
+ close();
+}
+
+void AReverbModel::open(unsigned int /*sampleRate*/) {
+ // FIXME: filter sizes must be multiplied by sample rate to 32000Hz ratio
+ // 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]);
+ }
+ delays = new Delay*[NUM_DELAYS];
+ for (Bit32u i = 0; i < NUM_DELAYS; i++) {
+ delays[i] = new Delay(currentSettings->delaySizes[i]);
+ }
+ mute();
+}
+
+void AReverbModel::close() {
+ if (allpasses != NULL) {
+ for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
+ if (allpasses[i] != NULL) {
+ delete allpasses[i];
+ allpasses[i] = NULL;
+ }
+ }
+ 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;
+ }
+ }
+ delete[] delays;
+ delays = NULL;
+ }
+}
+
+void AReverbModel::mute() {
+ for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
+ allpasses[i]->mute();
+ }
+ for (Bit32u i = 0; i < NUM_DELAYS; i++) {
+ delays[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];
+}
+
+bool AReverbModel::isActive() const {
+ bool bActive = false;
+ for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
+ bActive |= !allpasses[i]->isEmpty();
+ }
+ for (Bit32u i = 0; i < NUM_DELAYS; i++) {
+ bActive |= !delays[i]->isEmpty();
+ }
+ return bActive;
+}
+
+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;
+
+ for (unsigned long i = 0; i < numSamples; i++) {
+ dry = *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;
+
+ link = allpasses[0]->process(-filterhist2);
+ link = allpasses[1]->process(link);
+
+ // this implements a comb filter cross-linked with the fourth allpass filter
+ link += combhist * decayTime;
+ 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;
+
+ inLeft++;
+ inRight++;
+ outLeft++;
+ outRight++;
+ }
+}