scummvm/audio/softsynth/mt32/AReverbModel.cpp

240 lines
6.8 KiB
C++
Raw Normal View History

2012-02-10 07:51:41 +01:00
/* 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"
2012-11-18 19:07:46 +01:00
namespace MT32Emu {
2012-02-10 07:51:41 +01:00
// 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++;
}
}
2012-11-18 19:07:46 +01:00
}