DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/mt32/AReverbModel.cpp
00001 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
00002  * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
00003  *
00004  *  This program is free software: you can redistribute it and/or modify
00005  *  it under the terms of the GNU Lesser General Public License as published by
00006  *  the Free Software Foundation, either version 2.1 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU Lesser General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU Lesser General Public License
00015  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016  */
00017 
00018 #include "mt32emu.h"
00019 
00020 #if MT32EMU_USE_REVERBMODEL == 1
00021 
00022 #include "AReverbModel.h"
00023 
00024 // Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that
00025 // 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)
00026 // and followed by three parallel comb filters
00027 
00028 namespace MT32Emu {
00029 
00030 // Because LA-32 chip makes it's output available to process by the Boss chip with a significant delay,
00031 // the Boss chip puts to the buffer the LA32 dry output when it is ready and performs processing of the _previously_ latched data.
00032 // Of course, the right way would be to use a dedicated variable for this, but our reverb model is way higher level,
00033 // so we can simply increase the input buffer size.
00034 static const Bit32u PROCESS_DELAY = 1;
00035 
00036 // Default reverb settings for modes 0-2. These correspond to CM-32L / LAPC-I "new" reverb settings. MT-32 reverb is a bit different.
00037 // Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog).
00038 
00039 static const Bit32u NUM_ALLPASSES = 3;
00040 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.
00041 
00042 static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78};
00043 static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632};
00044 static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960};
00045 static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145};
00046 static const Bit32u MODE_0_COMB_FACTOR[] = {0x3C, 0x60, 0x60, 0x60};
00047 static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00048                                               0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
00049                                               0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
00050                                               0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
00051 static const Bit32u MODE_0_LEVELS[] = {10*1, 10*3, 10*5, 10*7, 11*9, 11*12, 11*15, 13*15};
00052 static const Bit32u MODE_0_LPF_AMP = 6;
00053 
00054 static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176};
00055 static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519};
00056 static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518};
00057 static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274};
00058 static const Bit32u MODE_1_COMB_FACTOR[] = {0x30, 0x60, 0x60, 0x60};
00059 static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00060                                                                                           0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
00061                                                                                           0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
00062                                                                                           0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
00063 static const Bit32u MODE_1_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 11*15, 14*15};
00064 static const Bit32u MODE_1_LPF_AMP = 6;
00065 
00066 static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157};
00067 static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539};
00068 static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769};
00069 static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1};
00070 static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20};
00071 static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00072                                               0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0,
00073                                               0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0,
00074                                               0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0};
00075 static const Bit32u MODE_2_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 12*15, 14*15};
00076 static const Bit32u MODE_2_LPF_AMP = 8;
00077 
00078 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};
00079 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};
00080 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};
00081 
00082 static const AReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_0_SETTINGS};
00083 
00084 RingBuffer::RingBuffer(const Bit32u newsize) : size(newsize), index(0) {
00085         buffer = new float[size];
00086 }
00087 
00088 RingBuffer::~RingBuffer() {
00089         delete[] buffer;
00090         buffer = NULL;
00091 }
00092 
00093 float RingBuffer::next() {
00094         if (++index >= size) {
00095                 index = 0;
00096         }
00097         return buffer[index];
00098 }
00099 
00100 bool RingBuffer::isEmpty() const {
00101         if (buffer == NULL) return true;
00102 
00103         float *buf = buffer;
00104         float max = 0.001f;
00105         for (Bit32u i = 0; i < size; i++) {
00106                 if ((*buf < -max) || (*buf > max)) return false;
00107                 buf++;
00108         }
00109         return true;
00110 }
00111 
00112 void RingBuffer::mute() {
00113         float *buf = buffer;
00114         for (Bit32u i = 0; i < size; i++) {
00115                 *buf++ = 0;
00116         }
00117 }
00118 
00119 AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {}
00120 
00121 float AllpassFilter::process(const float in) {
00122         // This model corresponds to the allpass filter implementation of the real CM-32L device
00123         // found from sample analysis
00124 
00125         const float bufferOut = next();
00126 
00127         // store input - feedback / 2
00128         buffer[index] = in - 0.5f * bufferOut;
00129 
00130         // return buffer output + feedforward / 2
00131         return bufferOut + 0.5f * buffer[index];
00132 }
00133 
00134 CombFilter::CombFilter(const Bit32u useSize) : RingBuffer(useSize), feedbackFactor(0.0F), filterFactor(0.0F) {}
00135 
00136 void CombFilter::process(const float in) {
00137         // This model corresponds to the comb filter implementation of the real CM-32L device
00138         // found from sample analysis
00139 
00140         // the previously stored value
00141         float last = buffer[index];
00142 
00143         // prepare input + feedback
00144         float filterIn = in + next() * feedbackFactor;
00145 
00146         // store input + feedback processed by a low-pass filter
00147         buffer[index] = filterFactor * last - filterIn;
00148 }
00149 
00150 float CombFilter::getOutputAt(const Bit32u outIndex) const {
00151         return buffer[(size + index - outIndex) % size];
00152 }
00153 
00154 void CombFilter::setFeedbackFactor(const float useFeedbackFactor) {
00155         feedbackFactor = useFeedbackFactor;
00156 }
00157 
00158 void CombFilter::setFilterFactor(const float useFilterFactor) {
00159         filterFactor = useFilterFactor;
00160 }
00161 
00162 AReverbModel::AReverbModel(const ReverbMode mode) : allpasses(NULL), combs(NULL), currentSettings(*REVERB_SETTINGS[mode]), lpfAmp(0.0F), wetLevel(0.0F) {}
00163 
00164 AReverbModel::~AReverbModel() {
00165         AReverbModel::close();
00166 }
00167 
00168 void AReverbModel::open() {
00169         allpasses = new AllpassFilter*[NUM_ALLPASSES];
00170         for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
00171                 allpasses[i] = new AllpassFilter(currentSettings.allpassSizes[i]);
00172         }
00173         combs = new CombFilter*[NUM_COMBS];
00174         for (Bit32u i = 0; i < NUM_COMBS; i++) {
00175                 combs[i] = new CombFilter(currentSettings.combSizes[i]);
00176                 combs[i]->setFilterFactor(currentSettings.filterFactor[i] / 256.0f);
00177         }
00178         lpfAmp = currentSettings.lpfAmp / 16.0f;
00179         mute();
00180 }
00181 
00182 void AReverbModel::close() {
00183         if (allpasses != NULL) {
00184                 for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
00185                         if (allpasses[i] != NULL) {
00186                                 delete allpasses[i];
00187                                 allpasses[i] = NULL;
00188                         }
00189                 }
00190                 delete[] allpasses;
00191                 allpasses = NULL;
00192         }
00193         if (combs != NULL) {
00194                 for (Bit32u i = 0; i < NUM_COMBS; i++) {
00195                         if (combs[i] != NULL) {
00196                                 delete combs[i];
00197                                 combs[i] = NULL;
00198                         }
00199                 }
00200                 delete[] combs;
00201                 combs = NULL;
00202         }
00203 }
00204 
00205 void AReverbModel::mute() {
00206         if (allpasses == NULL || combs == NULL) return;
00207         for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
00208                 allpasses[i]->mute();
00209         }
00210         for (Bit32u i = 0; i < NUM_COMBS; i++) {
00211                 combs[i]->mute();
00212         }
00213 }
00214 
00215 void AReverbModel::setParameters(Bit8u time, Bit8u level) {
00216 // FIXME: wetLevel definitely needs ramping when changed
00217 // Although, most games don't set reverb level during MIDI playback
00218         if (combs == NULL) return;
00219         level &= 7;
00220         time &= 7;
00221         for (Bit32u i = 0; i < NUM_COMBS; i++) {
00222                 combs[i]->setFeedbackFactor(currentSettings.decayTimes[(i << 3) + time] / 256.0f);
00223         }
00224         wetLevel = (level == 0 && time == 0) ? 0.0f : 0.5f * lpfAmp * currentSettings.wetLevels[level] / 256.0f;
00225 }
00226 
00227 bool AReverbModel::isActive() const {
00228         for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
00229                 if (!allpasses[i]->isEmpty()) return true;
00230         }
00231         for (Bit32u i = 0; i < NUM_COMBS; i++) {
00232                 if (!combs[i]->isEmpty()) return true;
00233         }
00234         return false;
00235 }
00236 
00237 void AReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) {
00238         for (unsigned long i = 0; i < numSamples; i++) {
00239                 float dry = wetLevel * (*inLeft + *inRight);
00240 
00241                 // Get the last stored sample before processing in order not to loose it
00242                 float link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1);
00243 
00244                 combs[0]->process(-dry);
00245 
00246                 link = allpasses[0]->process(link);
00247                 link = allpasses[1]->process(link);
00248                 link = allpasses[2]->process(link);
00249 
00250                 // If the output position is equal to the comb size, get it now in order not to loose it
00251                 float outL1 = 1.5f * combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1);
00252 
00253                 combs[1]->process(link);
00254                 combs[2]->process(link);
00255                 combs[3]->process(link);
00256 
00257                 link = outL1 + 1.5f * combs[2]->getOutputAt(currentSettings.outLPositions[1]);
00258                 link += combs[3]->getOutputAt(currentSettings.outLPositions[2]);
00259                 *outLeft = link;
00260 
00261                 link = 1.5f * combs[1]->getOutputAt(currentSettings.outRPositions[0]);
00262                 link += 1.5f * combs[2]->getOutputAt(currentSettings.outRPositions[1]);
00263                 link += combs[3]->getOutputAt(currentSettings.outRPositions[2]);
00264                 *outRight = link;
00265 
00266                 inLeft++;
00267                 inRight++;
00268                 outLeft++;
00269                 outRight++;
00270         }
00271 }
00272 
00273 }
00274 
00275 #endif