/* 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 . */ #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++; } }