DOSBox-X
|
00001 // --------------------------------------------------------------------------- 00002 // This file is part of reSID, a MOS6581 SID emulator engine. 00003 // Copyright (C) 2004 Dag Lem <resid@nimrod.no> 00004 // 00005 // This program is free software; you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation; either version 2 of the License, or 00008 // (at your option) any later version. 00009 // 00010 // This program is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License along 00016 // with this program; if not, write to the Free Software Foundation, Inc., 00017 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 // --------------------------------------------------------------------------- 00019 00020 #ifndef __FILTER_H__ 00021 #define __FILTER_H__ 00022 00023 #include "siddefs.h" 00024 #include "spline.h" 00025 #include <sstream> 00026 00027 // ---------------------------------------------------------------------------- 00028 // The SID filter is modeled with a two-integrator-loop biquadratic filter, 00029 // which has been confirmed by Bob Yannes to be the actual circuit used in 00030 // the SID chip. 00031 // 00032 // Measurements show that excellent emulation of the SID filter is achieved, 00033 // except when high resonance is combined with high sustain levels. 00034 // In this case the SID op-amps are performing less than ideally and are 00035 // causing some peculiar behavior of the SID filter. This however seems to 00036 // have more effect on the overall amplitude than on the color of the sound. 00037 // 00038 // The theory for the filter circuit can be found in "Microelectric Circuits" 00039 // by Adel S. Sedra and Kenneth C. Smith. 00040 // The circuit is modeled based on the explanation found there except that 00041 // an additional inverter is used in the feedback from the bandpass output, 00042 // allowing the summer op-amp to operate in single-ended mode. This yields 00043 // inverted filter outputs with levels independent of Q, which corresponds with 00044 // the results obtained from a real SID. 00045 // 00046 // We have been able to model the summer and the two integrators of the circuit 00047 // to form components of an IIR filter. 00048 // Vhp is the output of the summer, Vbp is the output of the first integrator, 00049 // and Vlp is the output of the second integrator in the filter circuit. 00050 // 00051 // According to Bob Yannes, the active stages of the SID filter are not really 00052 // op-amps. Rather, simple NMOS inverters are used. By biasing an inverter 00053 // into its region of quasi-linear operation using a feedback resistor from 00054 // input to output, a MOS inverter can be made to act like an op-amp for 00055 // small signals centered around the switching threshold. 00056 // 00057 // Qualified guesses at SID filter schematics are depicted below. 00058 // 00059 // SID filter 00060 // ---------- 00061 // 00062 // ----------------------------------------------- 00063 // | | 00064 // | ---Rq-- | 00065 // | | | | 00066 // | ------------<A]-----R1--------- | 00067 // | | | | 00068 // | | ---C---| ---C---| 00069 // | | | | | | 00070 // | --R1-- ---R1-- |---Rs--| |---Rs--| 00071 // | | | | | | | | 00072 // ----R1--|-----[A>--|--R-----[A>--|--R-----[A>--| 00073 // | | | | 00074 // vi -----R1-- | | | 00075 // 00076 // vhp vbp vlp 00077 // 00078 // 00079 // vi - input voltage 00080 // vhp - highpass output 00081 // vbp - bandpass output 00082 // vlp - lowpass output 00083 // [A> - op-amp 00084 // R1 - summer resistor 00085 // Rq - resistor array controlling resonance (4 resistors) 00086 // R - NMOS FET voltage controlled resistor controlling cutoff frequency 00087 // Rs - shunt resitor 00088 // C - capacitor 00089 // 00090 // 00091 // 00092 // SID integrator 00093 // -------------- 00094 // 00095 // V+ 00096 // 00097 // | 00098 // | 00099 // -----| 00100 // | | 00101 // | ||-- 00102 // -|| 00103 // ---C--- ||-> 00104 // | | | 00105 // |---Rs-----------|---- vo 00106 // | | 00107 // | ||-- 00108 // vi ---- -----|------------|| 00109 // | ^ | ||-> 00110 // |___| | | 00111 // ----- | | 00112 // | | | 00113 // |---R2-- | 00114 // | 00115 // R1 V- 00116 // | 00117 // | 00118 // 00119 // Vw 00120 // 00121 // ---------------------------------------------------------------------------- 00122 class Filter 00123 { 00124 public: 00125 Filter(); 00126 00127 void enable_filter(bool enable); 00128 void set_chip_model(chip_model model); 00129 00130 RESID_INLINE 00131 void clock(sound_sample voice1, sound_sample voice2, sound_sample voice3, 00132 sound_sample ext_in); 00133 RESID_INLINE 00134 void clock(cycle_count delta_t, 00135 sound_sample voice1, sound_sample voice2, sound_sample voice3, 00136 sound_sample ext_in); 00137 void reset(); 00138 00139 // Write registers. 00140 void writeFC_LO(reg8); 00141 void writeFC_HI(reg8); 00142 void writeRES_FILT(reg8); 00143 void writeMODE_VOL(reg8); 00144 00145 // SID audio output (16 bits). 00146 sound_sample output(); 00147 00148 // Spline functions. 00149 void fc_default(const fc_point*& points, int& count); 00150 PointPlotter<sound_sample> fc_plotter(); 00151 00152 void SaveState( std::ostream& stream ); 00153 void LoadState( std::istream& stream ); 00154 00155 protected: 00156 void set_w0(); 00157 void set_Q(); 00158 00159 // Filter enabled. 00160 bool enabled; 00161 00162 // Filter cutoff frequency. 00163 reg12 fc; 00164 00165 // Filter resonance. 00166 reg8 res; 00167 00168 // Selects which inputs to route through filter. 00169 reg8 filt; 00170 00171 // Switch voice 3 off. 00172 reg8 voice3off; 00173 00174 // Highpass, bandpass, and lowpass filter modes. 00175 reg8 hp_bp_lp; 00176 00177 // Output master volume. 00178 reg4 vol; 00179 00180 // Mixer DC offset. 00181 sound_sample mixer_DC; 00182 00183 // State of filter. 00184 sound_sample Vhp; // highpass 00185 sound_sample Vbp; // bandpass 00186 sound_sample Vlp; // lowpass 00187 sound_sample Vnf; // not filtered 00188 00189 // Cutoff frequency, resonance. 00190 sound_sample w0, w0_ceil_1, w0_ceil_dt; 00191 sound_sample _1024_div_Q; 00192 00193 // Cutoff frequency tables. 00194 // FC is an 11 bit register. 00195 sound_sample f0_6581[2048]; 00196 sound_sample f0_8580[2048]; 00197 sound_sample* f0; 00198 static fc_point f0_points_6581[]; 00199 static fc_point f0_points_8580[]; 00200 fc_point* f0_points; 00201 int f0_count; 00202 00203 friend class SID2; 00204 }; 00205 00206 00207 // ---------------------------------------------------------------------------- 00208 // Inline functions. 00209 // The following functions are defined inline because they are called every 00210 // time a sample is calculated. 00211 // ---------------------------------------------------------------------------- 00212 00213 #if RESID_INLINING || defined(__FILTER_CC__) 00214 00215 // ---------------------------------------------------------------------------- 00216 // SID clocking - 1 cycle. 00217 // ---------------------------------------------------------------------------- 00218 RESID_INLINE 00219 void Filter::clock(sound_sample voice1, 00220 sound_sample voice2, 00221 sound_sample voice3, 00222 sound_sample ext_in) 00223 { 00224 // Scale each voice down from 20 to 13 bits. 00225 voice1 >>= 7; 00226 voice2 >>= 7; 00227 00228 // NB! Voice 3 is not silenced by voice3off if it is routed through 00229 // the filter. 00230 if (voice3off && !(filt & 0x04)) { 00231 voice3 = 0; 00232 } 00233 else { 00234 voice3 >>= 7; 00235 } 00236 00237 ext_in >>= 7; 00238 00239 // This is handy for testing. 00240 if (!enabled) { 00241 Vnf = voice1 + voice2 + voice3 + ext_in; 00242 Vhp = Vbp = Vlp = 0; 00243 return; 00244 } 00245 00246 // Route voices into or around filter. 00247 // The code below is expanded to a switch for faster execution. 00248 // (filt1 ? Vi : Vnf) += voice1; 00249 // (filt2 ? Vi : Vnf) += voice2; 00250 // (filt3 ? Vi : Vnf) += voice3; 00251 00252 sound_sample Vi; 00253 00254 switch (filt) { 00255 default: 00256 case 0x0: 00257 Vi = 0; 00258 Vnf = voice1 + voice2 + voice3 + ext_in; 00259 break; 00260 case 0x1: 00261 Vi = voice1; 00262 Vnf = voice2 + voice3 + ext_in; 00263 break; 00264 case 0x2: 00265 Vi = voice2; 00266 Vnf = voice1 + voice3 + ext_in; 00267 break; 00268 case 0x3: 00269 Vi = voice1 + voice2; 00270 Vnf = voice3 + ext_in; 00271 break; 00272 case 0x4: 00273 Vi = voice3; 00274 Vnf = voice1 + voice2 + ext_in; 00275 break; 00276 case 0x5: 00277 Vi = voice1 + voice3; 00278 Vnf = voice2 + ext_in; 00279 break; 00280 case 0x6: 00281 Vi = voice2 + voice3; 00282 Vnf = voice1 + ext_in; 00283 break; 00284 case 0x7: 00285 Vi = voice1 + voice2 + voice3; 00286 Vnf = ext_in; 00287 break; 00288 case 0x8: 00289 Vi = ext_in; 00290 Vnf = voice1 + voice2 + voice3; 00291 break; 00292 case 0x9: 00293 Vi = voice1 + ext_in; 00294 Vnf = voice2 + voice3; 00295 break; 00296 case 0xa: 00297 Vi = voice2 + ext_in; 00298 Vnf = voice1 + voice3; 00299 break; 00300 case 0xb: 00301 Vi = voice1 + voice2 + ext_in; 00302 Vnf = voice3; 00303 break; 00304 case 0xc: 00305 Vi = voice3 + ext_in; 00306 Vnf = voice1 + voice2; 00307 break; 00308 case 0xd: 00309 Vi = voice1 + voice3 + ext_in; 00310 Vnf = voice2; 00311 break; 00312 case 0xe: 00313 Vi = voice2 + voice3 + ext_in; 00314 Vnf = voice1; 00315 break; 00316 case 0xf: 00317 Vi = voice1 + voice2 + voice3 + ext_in; 00318 Vnf = 0; 00319 break; 00320 } 00321 00322 // delta_t = 1 is converted to seconds given a 1MHz clock by dividing 00323 // with 1 000 000. 00324 00325 // Calculate filter outputs. 00326 // Vhp = Vbp/Q - Vlp - Vi; 00327 // dVbp = -w0*Vhp*dt; 00328 // dVlp = -w0*Vbp*dt; 00329 00330 sound_sample dVbp = (w0_ceil_1*Vhp >> 20); 00331 sound_sample dVlp = (w0_ceil_1*Vbp >> 20); 00332 Vbp -= dVbp; 00333 Vlp -= dVlp; 00334 Vhp = (Vbp*_1024_div_Q >> 10) - Vlp - Vi; 00335 } 00336 00337 // ---------------------------------------------------------------------------- 00338 // SID clocking - delta_t cycles. 00339 // ---------------------------------------------------------------------------- 00340 RESID_INLINE 00341 void Filter::clock(cycle_count delta_t, 00342 sound_sample voice1, 00343 sound_sample voice2, 00344 sound_sample voice3, 00345 sound_sample ext_in) 00346 { 00347 // Scale each voice down from 20 to 13 bits. 00348 voice1 >>= 7; 00349 voice2 >>= 7; 00350 00351 // NB! Voice 3 is not silenced by voice3off if it is routed through 00352 // the filter. 00353 if (voice3off && !(filt & 0x04)) { 00354 voice3 = 0; 00355 } 00356 else { 00357 voice3 >>= 7; 00358 } 00359 00360 ext_in >>= 7; 00361 00362 // Enable filter on/off. 00363 // This is not really part of SID, but is useful for testing. 00364 // On slow CPUs it may be necessary to bypass the filter to lower the CPU 00365 // load. 00366 if (!enabled) { 00367 Vnf = voice1 + voice2 + voice3 + ext_in; 00368 Vhp = Vbp = Vlp = 0; 00369 return; 00370 } 00371 00372 // Route voices into or around filter. 00373 // The code below is expanded to a switch for faster execution. 00374 // (filt1 ? Vi : Vnf) += voice1; 00375 // (filt2 ? Vi : Vnf) += voice2; 00376 // (filt3 ? Vi : Vnf) += voice3; 00377 00378 sound_sample Vi; 00379 00380 switch (filt) { 00381 default: 00382 case 0x0: 00383 Vi = 0; 00384 Vnf = voice1 + voice2 + voice3 + ext_in; 00385 break; 00386 case 0x1: 00387 Vi = voice1; 00388 Vnf = voice2 + voice3 + ext_in; 00389 break; 00390 case 0x2: 00391 Vi = voice2; 00392 Vnf = voice1 + voice3 + ext_in; 00393 break; 00394 case 0x3: 00395 Vi = voice1 + voice2; 00396 Vnf = voice3 + ext_in; 00397 break; 00398 case 0x4: 00399 Vi = voice3; 00400 Vnf = voice1 + voice2 + ext_in; 00401 break; 00402 case 0x5: 00403 Vi = voice1 + voice3; 00404 Vnf = voice2 + ext_in; 00405 break; 00406 case 0x6: 00407 Vi = voice2 + voice3; 00408 Vnf = voice1 + ext_in; 00409 break; 00410 case 0x7: 00411 Vi = voice1 + voice2 + voice3; 00412 Vnf = ext_in; 00413 break; 00414 case 0x8: 00415 Vi = ext_in; 00416 Vnf = voice1 + voice2 + voice3; 00417 break; 00418 case 0x9: 00419 Vi = voice1 + ext_in; 00420 Vnf = voice2 + voice3; 00421 break; 00422 case 0xa: 00423 Vi = voice2 + ext_in; 00424 Vnf = voice1 + voice3; 00425 break; 00426 case 0xb: 00427 Vi = voice1 + voice2 + ext_in; 00428 Vnf = voice3; 00429 break; 00430 case 0xc: 00431 Vi = voice3 + ext_in; 00432 Vnf = voice1 + voice2; 00433 break; 00434 case 0xd: 00435 Vi = voice1 + voice3 + ext_in; 00436 Vnf = voice2; 00437 break; 00438 case 0xe: 00439 Vi = voice2 + voice3 + ext_in; 00440 Vnf = voice1; 00441 break; 00442 case 0xf: 00443 Vi = voice1 + voice2 + voice3 + ext_in; 00444 Vnf = 0; 00445 break; 00446 } 00447 00448 // Maximum delta cycles for the filter to work satisfactorily under current 00449 // cutoff frequency and resonance constraints is approximately 8. 00450 cycle_count delta_t_flt = 8; 00451 00452 while (delta_t) { 00453 if (delta_t < delta_t_flt) { 00454 delta_t_flt = delta_t; 00455 } 00456 00457 // delta_t is converted to seconds given a 1MHz clock by dividing 00458 // with 1 000 000. This is done in two operations to avoid integer 00459 // multiplication overflow. 00460 00461 // Calculate filter outputs. 00462 // Vhp = Vbp/Q - Vlp - Vi; 00463 // dVbp = -w0*Vhp*dt; 00464 // dVlp = -w0*Vbp*dt; 00465 sound_sample w0_delta_t = w0_ceil_dt*delta_t_flt >> 6; 00466 00467 sound_sample dVbp = (w0_delta_t*Vhp >> 14); 00468 sound_sample dVlp = (w0_delta_t*Vbp >> 14); 00469 Vbp -= dVbp; 00470 Vlp -= dVlp; 00471 Vhp = (Vbp*_1024_div_Q >> 10) - Vlp - Vi; 00472 00473 delta_t -= delta_t_flt; 00474 } 00475 } 00476 00477 00478 // ---------------------------------------------------------------------------- 00479 // SID audio output (20 bits). 00480 // ---------------------------------------------------------------------------- 00481 RESID_INLINE 00482 sound_sample Filter::output() 00483 { 00484 // This is handy for testing. 00485 if (!enabled) { 00486 return (Vnf + mixer_DC)*static_cast<sound_sample>(vol); 00487 } 00488 00489 // Mix highpass, bandpass, and lowpass outputs. The sum is not 00490 // weighted, this can be confirmed by sampling sound output for 00491 // e.g. bandpass, lowpass, and bandpass+lowpass from a SID chip. 00492 00493 // The code below is expanded to a switch for faster execution. 00494 // if (hp) Vf += Vhp; 00495 // if (bp) Vf += Vbp; 00496 // if (lp) Vf += Vlp; 00497 00498 sound_sample Vf; 00499 00500 switch (hp_bp_lp) { 00501 default: 00502 case 0x0: 00503 Vf = 0; 00504 break; 00505 case 0x1: 00506 Vf = Vlp; 00507 break; 00508 case 0x2: 00509 Vf = Vbp; 00510 break; 00511 case 0x3: 00512 Vf = Vlp + Vbp; 00513 break; 00514 case 0x4: 00515 Vf = Vhp; 00516 break; 00517 case 0x5: 00518 Vf = Vlp + Vhp; 00519 break; 00520 case 0x6: 00521 Vf = Vbp + Vhp; 00522 break; 00523 case 0x7: 00524 Vf = Vlp + Vbp + Vhp; 00525 break; 00526 } 00527 00528 // Sum non-filtered and filtered output. 00529 // Multiply the sum with volume. 00530 return (Vnf + Vf + mixer_DC)*static_cast<sound_sample>(vol); 00531 } 00532 00533 #endif // RESID_INLINING || defined(__FILTER_CC__) 00534 00535 #endif // not __FILTER_H__