diff options
author | Eugene Sandulenko | 2012-02-09 11:20:45 +0200 |
---|---|---|
committer | Eugene Sandulenko | 2012-02-09 11:31:03 +0200 |
commit | 030e155eeb7f82dc89315dbefa43e09a411c6110 (patch) | |
tree | 3b941d9841735fcc9878ab0663406d6be7a025aa /audio/softsynth/mt32/AReverbModel.cpp | |
parent | 06b52994619fb9a7ef9e54e50b5cc67d07f6a0a0 (diff) | |
download | scummvm-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.cpp | 237 |
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++; + } +} |