DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/hardware/reSID/filter.h
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
00016 //  along with this program; if not, write to the Free Software
00017 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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__