DOSBox-X
|
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 == 2 00021 00022 #include "BReverbModel.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 static const Bit32u MODE_3_ADDITIONAL_DELAY = 1; 00037 static const Bit32u MODE_3_FEEDBACK_DELAY = 1; 00038 00039 // Default reverb settings for modes 0-2. These correspond to CM-32L / LAPC-I "new" reverb settings. MT-32 reverb is a bit different. 00040 // Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog). 00041 00042 static const Bit32u MODE_0_NUMBER_OF_ALLPASSES = 3; 00043 static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78}; 00044 static const Bit32u MODE_0_NUMBER_OF_COMBS = 4; // Well, actually there are 3 comb filters, but the entrance LPF + delay can be processed via a hacked comb. 00045 static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632}; 00046 static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960}; 00047 static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145}; 00048 static const Bit32u MODE_0_COMB_FACTOR[] = {0xA0, 0x60, 0x60, 0x60}; 00049 static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00050 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 00051 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 00052 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; 00053 static const Bit32u MODE_0_DRY_AMP[] = {0xA0, 0xA0, 0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xD0}; 00054 static const Bit32u MODE_0_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; 00055 static const Bit32u MODE_0_LPF_AMP = 0x60; 00056 00057 static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3; 00058 static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; 00059 static const Bit32u MODE_1_NUMBER_OF_COMBS = 4; // Same as for mode 0 above 00060 static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; 00061 static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; 00062 static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; 00063 static const Bit32u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60}; 00064 static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00065 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, 00066 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 00067 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; 00068 static const Bit32u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0}; 00069 static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; 00070 static const Bit32u MODE_1_LPF_AMP = 0x60; 00071 00072 static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3; 00073 static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; 00074 static const Bit32u MODE_2_NUMBER_OF_COMBS = 4; // Same as for mode 0 above 00075 static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; 00076 static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; 00077 static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; 00078 static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; 00079 static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00080 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, 00081 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, 00082 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0}; 00083 static const Bit32u MODE_2_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xC0, 0xE0}; 00084 static const Bit32u MODE_2_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; 00085 static const Bit32u MODE_2_LPF_AMP = 0x80; 00086 00087 static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0; 00088 static const Bit32u MODE_3_NUMBER_OF_COMBS = 1; 00089 static const Bit32u MODE_3_DELAY[] = {16000 + MODE_3_FEEDBACK_DELAY + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY}; 00090 static const Bit32u MODE_3_OUTL[] = {400, 624, 960, 1488, 2256, 3472, 5280, 8000}; 00091 static const Bit32u MODE_3_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000}; 00092 static const Bit32u MODE_3_COMB_FACTOR[] = {0x68}; 00093 static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; 00094 static const Bit32u MODE_3_DRY_AMP[] = {0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50}; 00095 static const Bit32u MODE_3_WET_AMP[] = {0x18, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; 00096 00097 static const BReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_NUMBER_OF_ALLPASSES, MODE_0_ALLPASSES, MODE_0_NUMBER_OF_COMBS, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_DRY_AMP, MODE_0_WET_AMP, MODE_0_LPF_AMP}; 00098 static const BReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_NUMBER_OF_ALLPASSES, MODE_1_ALLPASSES, MODE_1_NUMBER_OF_COMBS, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_DRY_AMP, MODE_1_WET_AMP, MODE_1_LPF_AMP}; 00099 static const BReverbSettings REVERB_MODE_2_SETTINGS = {MODE_2_NUMBER_OF_ALLPASSES, MODE_2_ALLPASSES, MODE_2_NUMBER_OF_COMBS, MODE_2_COMBS, MODE_2_OUTL, MODE_2_OUTR, MODE_2_COMB_FACTOR, MODE_2_COMB_FEEDBACK, MODE_2_DRY_AMP, MODE_2_WET_AMP, MODE_2_LPF_AMP}; 00100 static const BReverbSettings REVERB_MODE_3_SETTINGS = {MODE_3_NUMBER_OF_ALLPASSES, NULL, MODE_3_NUMBER_OF_COMBS, MODE_3_DELAY, MODE_3_OUTL, MODE_3_OUTR, MODE_3_COMB_FACTOR, MODE_3_COMB_FEEDBACK, MODE_3_DRY_AMP, MODE_3_WET_AMP, 0}; 00101 00102 static const BReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_3_SETTINGS}; 00103 00104 // This algorithm tries to emulate exactly Boss multiplication operation (at least this is what we see on reverb RAM data lines). 00105 // Also LA32 is suspected to use the similar one to perform PCM interpolation and ring modulation. 00106 static Bit32s weirdMul(Bit32s a, Bit8u addMask, Bit8u carryMask) { 00107 Bit8u mask = 0x80; 00108 Bit32s res = 0; 00109 for (int i = 0; i < 8; i++) { 00110 Bit32s carry = (a < 0) && (mask & carryMask) > 0 ? a & 1 : 0; 00111 a >>= 1; 00112 res += (mask & addMask) > 0 ? a + carry : 0; 00113 mask >>= 1; 00114 } 00115 return res; 00116 } 00117 00118 RingBuffer::RingBuffer(Bit32u newsize) : size(newsize), index(0) { 00119 buffer = new Bit16s[size]; 00120 } 00121 00122 RingBuffer::~RingBuffer() { 00123 delete[] buffer; 00124 buffer = NULL; 00125 } 00126 00127 Bit32s RingBuffer::next() { 00128 if (++index >= size) { 00129 index = 0; 00130 } 00131 return buffer[index]; 00132 } 00133 00134 bool RingBuffer::isEmpty() const { 00135 if (buffer == NULL) return true; 00136 00137 Bit16s *buf = buffer; 00138 for (Bit32u i = 0; i < size; i++) { 00139 if (*buf < -8 || *buf > 8) return false; 00140 buf++; 00141 } 00142 return true; 00143 } 00144 00145 void RingBuffer::mute() { 00146 Bit16s *buf = buffer; 00147 for (Bit32u i = 0; i < size; i++) { 00148 *buf++ = 0; 00149 } 00150 } 00151 00152 AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {} 00153 00154 Bit32s AllpassFilter::process(const Bit32s in) { 00155 // This model corresponds to the allpass filter implementation of the real CM-32L device 00156 // found from sample analysis 00157 00158 Bit16s bufferOut = next(); 00159 00160 // store input - feedback / 2 00161 buffer[index] = in - (bufferOut >> 1); 00162 00163 // return buffer output + feedforward / 2 00164 return bufferOut + (buffer[index] >> 1); 00165 } 00166 00167 CombFilter::CombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : RingBuffer(useSize), filterFactor(useFilterFactor) {} 00168 00169 void CombFilter::process(const Bit32s in) { 00170 // This model corresponds to the comb filter implementation of the real CM-32L device 00171 00172 // the previously stored value 00173 Bit32s last = buffer[index]; 00174 00175 // prepare input + feedback 00176 Bit32s filterIn = in + weirdMul(next(), feedbackFactor, 0xF0 /* Maybe 0x80 ? */); 00177 00178 // store input + feedback processed by a low-pass filter 00179 buffer[index] = weirdMul(last, filterFactor, 0x40) - filterIn; 00180 } 00181 00182 Bit32s CombFilter::getOutputAt(const Bit32u outIndex) const { 00183 return buffer[(size + index - outIndex) % size]; 00184 } 00185 00186 void CombFilter::setFeedbackFactor(const Bit32u useFeedbackFactor) { 00187 feedbackFactor = useFeedbackFactor; 00188 } 00189 00190 DelayWithLowPassFilter::DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp) 00191 : CombFilter(useSize, useFilterFactor), amp(useAmp) {} 00192 00193 void DelayWithLowPassFilter::process(const Bit32s in) { 00194 // the previously stored value 00195 Bit32s last = buffer[index]; 00196 00197 // move to the next index 00198 next(); 00199 00200 // low-pass filter process 00201 Bit32s lpfOut = weirdMul(last, filterFactor, 0xFF) + in; 00202 00203 // store lpfOut multiplied by LPF amp factor 00204 buffer[index] = weirdMul(lpfOut, amp, 0xFF); 00205 } 00206 00207 TapDelayCombFilter::TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : CombFilter(useSize, useFilterFactor) {} 00208 00209 void TapDelayCombFilter::process(const Bit32s in) { 00210 // the previously stored value 00211 Bit32s last = buffer[index]; 00212 00213 // move to the next index 00214 next(); 00215 00216 // prepare input + feedback 00217 // Actually, the size of the filter varies with the TIME parameter, the feedback sample is taken from the position just below the right output 00218 Bit32s filterIn = in + weirdMul(getOutputAt(outR + MODE_3_FEEDBACK_DELAY), feedbackFactor, 0xF0); 00219 00220 // store input + feedback processed by a low-pass filter 00221 buffer[index] = weirdMul(last, filterFactor, 0xF0) - filterIn; 00222 } 00223 00224 Bit32s TapDelayCombFilter::getLeftOutput() const { 00225 return getOutputAt(outL + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY); 00226 } 00227 00228 Bit32s TapDelayCombFilter::getRightOutput() const { 00229 return getOutputAt(outR + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY); 00230 } 00231 00232 void TapDelayCombFilter::setOutputPositions(const Bit32u useOutL, const Bit32u useOutR) { 00233 outL = useOutL; 00234 outR = useOutR; 00235 } 00236 00237 BReverbModel::BReverbModel(const ReverbMode mode) 00238 : allpasses(NULL), combs(NULL), currentSettings(*REVERB_SETTINGS[mode]), tapDelayMode(mode == REVERB_MODE_TAP_DELAY) {} 00239 00240 BReverbModel::~BReverbModel() { 00241 close(); 00242 } 00243 00244 void BReverbModel::open() { 00245 if (currentSettings.numberOfAllpasses > 0) { 00246 allpasses = new AllpassFilter*[currentSettings.numberOfAllpasses]; 00247 for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { 00248 allpasses[i] = new AllpassFilter(currentSettings.allpassSizes[i]); 00249 } 00250 } 00251 combs = new CombFilter*[currentSettings.numberOfCombs]; 00252 if (tapDelayMode) { 00253 *combs = new TapDelayCombFilter(*currentSettings.combSizes, *currentSettings.filterFactors); 00254 } else { 00255 combs[0] = new DelayWithLowPassFilter(currentSettings.combSizes[0], currentSettings.filterFactors[0], currentSettings.lpfAmp); 00256 for (Bit32u i = 1; i < currentSettings.numberOfCombs; i++) { 00257 combs[i] = new CombFilter(currentSettings.combSizes[i], currentSettings.filterFactors[i]); 00258 } 00259 } 00260 mute(); 00261 } 00262 00263 void BReverbModel::close() { 00264 if (allpasses != NULL) { 00265 for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { 00266 if (allpasses[i] != NULL) { 00267 delete allpasses[i]; 00268 allpasses[i] = NULL; 00269 } 00270 } 00271 delete[] allpasses; 00272 allpasses = NULL; 00273 } 00274 if (combs != NULL) { 00275 for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { 00276 if (combs[i] != NULL) { 00277 delete combs[i]; 00278 combs[i] = NULL; 00279 } 00280 } 00281 delete[] combs; 00282 combs = NULL; 00283 } 00284 } 00285 00286 void BReverbModel::mute() { 00287 if (allpasses != NULL) { 00288 for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { 00289 allpasses[i]->mute(); 00290 } 00291 } 00292 if (combs != NULL) { 00293 for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { 00294 combs[i]->mute(); 00295 } 00296 } 00297 } 00298 00299 void BReverbModel::setParameters(Bit8u time, Bit8u level) { 00300 if (combs == NULL) return; 00301 level &= 7; 00302 time &= 7; 00303 if (tapDelayMode) { 00304 TapDelayCombFilter *comb = static_cast<TapDelayCombFilter *> (*combs); 00305 comb->setOutputPositions(currentSettings.outLPositions[time], currentSettings.outRPositions[time & 7]); 00306 comb->setFeedbackFactor(currentSettings.feedbackFactors[((level < 3) || (time < 6)) ? 0 : 1]); 00307 } else { 00308 for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { 00309 combs[i]->setFeedbackFactor(currentSettings.feedbackFactors[(i << 3) + time]); 00310 } 00311 } 00312 if (time == 0 && level == 0) { 00313 dryAmp = wetLevel = 0; 00314 } else { 00315 dryAmp = currentSettings.dryAmps[level]; 00316 wetLevel = currentSettings.wetLevels[level]; 00317 } 00318 } 00319 00320 bool BReverbModel::isActive() const { 00321 for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { 00322 if (!allpasses[i]->isEmpty()) return true; 00323 } 00324 for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { 00325 if (!combs[i]->isEmpty()) return true; 00326 } 00327 return false; 00328 } 00329 00330 void BReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) { 00331 Bit32s dry, link, outL1, outR1; 00332 00333 for (unsigned long i = 0; i < numSamples; i++) { 00334 if (tapDelayMode) { 00335 dry = Bit32s(*inLeft * 8192.0f) + Bit32s(*inRight * 8192.0f); 00336 } else { 00337 dry = Bit32s(*inLeft * 8192.0f) / 2 + Bit32s(*inRight * 8192.0f) / 2; 00338 } 00339 00340 // Looks like dryAmp doesn't change in MT-32 but it does in CM-32L / LAPC-I 00341 dry = weirdMul(dry, dryAmp, 0xFF); 00342 00343 if (tapDelayMode) { 00344 TapDelayCombFilter *comb = static_cast<TapDelayCombFilter *> (*combs); 00345 comb->process(dry); 00346 *outLeft = weirdMul(comb->getLeftOutput(), wetLevel, 0xFF) / 8192.0f; 00347 *outRight = weirdMul(comb->getRightOutput(), wetLevel, 0xFF) / 8192.0f; 00348 } else { 00349 // Get the last stored sample before processing in order not to loose it 00350 link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1); 00351 00352 // Entrance LPF. Note, comb.process() differs a bit here. 00353 combs[0]->process(dry); 00354 00355 // This introduces reverb noise which actually makes output from the real Boss chip nondeterministic 00356 link = link - 1; 00357 link = allpasses[0]->process(link); 00358 link = allpasses[1]->process(link); 00359 link = allpasses[2]->process(link); 00360 00361 // If the output position is equal to the comb size, get it now in order not to loose it 00362 outL1 = combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1); 00363 outL1 += outL1 >> 1; 00364 00365 combs[1]->process(link); 00366 combs[2]->process(link); 00367 combs[3]->process(link); 00368 00369 link = combs[2]->getOutputAt(currentSettings.outLPositions[1]); 00370 link += link >> 1; 00371 link += outL1; 00372 link += combs[3]->getOutputAt(currentSettings.outLPositions[2]); 00373 *outLeft = weirdMul(link, wetLevel, 0xFF) / 8192.0f; 00374 00375 outR1 = combs[1]->getOutputAt(currentSettings.outRPositions[0]); 00376 outR1 += outR1 >> 1; 00377 link = combs[2]->getOutputAt(currentSettings.outRPositions[1]); 00378 link += link >> 1; 00379 link += outR1; 00380 link += combs[3]->getOutputAt(currentSettings.outRPositions[2]); 00381 *outRight = weirdMul(link, wetLevel, 0xFF) / 8192.0f; 00382 } 00383 00384 inLeft++; 00385 inRight++; 00386 outLeft++; 00387 outRight++; 00388 } 00389 } 00390 00391 } 00392 00393 #endif