libsidplayfp 1.0.3
extfilt.h
00001 //  ---------------------------------------------------------------------------
00002 //  This file is part of reSID, a MOS6581 SID emulator engine.
00003 //  Copyright (C) 2010  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 RESID_EXTFILT_H
00021 #define RESID_EXTFILT_H
00022 
00023 #include "resid-config.h"
00024 
00025 namespace reSID
00026 {
00027 
00028 // ----------------------------------------------------------------------------
00029 // The audio output stage in a Commodore 64 consists of two STC networks,
00030 // a low-pass filter with 3-dB frequency 16kHz followed by a high-pass
00031 // filter with 3-dB frequency 1.6Hz (the latter provided an audio equipment
00032 // input impedance of 10kOhm).
00033 // The STC networks are connected with a BJT supposedly meant to act as
00034 // a unity gain buffer, which is not really how it works. A more elaborate
00035 // model would include the BJT, however DC circuit analysis yields BJT
00036 // base-emitter and emitter-base impedances sufficiently low to produce
00037 // additional low-pass and high-pass 3dB-frequencies in the order of hundreds
00038 // of kHz. This calls for a sampling frequency of several MHz, which is far
00039 // too high for practical use.
00040 // ----------------------------------------------------------------------------
00041 class ExternalFilter
00042 {
00043 public:
00044   ExternalFilter();
00045 
00046   void enable_filter(bool enable);
00047 
00048   void clock(short Vi);
00049   void clock(cycle_count delta_t, short Vi);
00050   void reset();
00051 
00052   // Audio output (16 bits).
00053   short output();
00054 
00055 protected:
00056   // Filter enabled.
00057   bool enabled;
00058 
00059   // State of filters (27 bits).
00060   int Vlp; // lowpass
00061   int Vhp; // highpass
00062 
00063   // Cutoff frequencies.
00064   int w0lp_1_s7;
00065   int w0hp_1_s17;
00066 
00067 friend class SID;
00068 };
00069 
00070 
00071 // ----------------------------------------------------------------------------
00072 // Inline functions.
00073 // The following functions are defined inline because they are called every
00074 // time a sample is calculated.
00075 // ----------------------------------------------------------------------------
00076 
00077 #if RESID_INLINING || defined(RESID_EXTFILT_CC)
00078 
00079 // ----------------------------------------------------------------------------
00080 // SID clocking - 1 cycle.
00081 // ----------------------------------------------------------------------------
00082 RESID_INLINE
00083 void ExternalFilter::clock(short Vi)
00084 {
00085   // This is handy for testing.
00086   if (unlikely(!enabled)) {
00087     // Vo  = Vlp - Vhp;
00088     Vlp = Vi << 11;
00089     Vhp = 0;
00090     return;
00091   }
00092 
00093   // Calculate filter outputs.
00094   // Vlp = Vlp + w0lp*(Vi - Vlp)*delta_t;
00095   // Vhp = Vhp + w0hp*(Vlp - Vhp)*delta_t;
00096   // Vo  = Vlp - Vhp;
00097 
00098   int dVlp = w0lp_1_s7*((Vi << 11) - Vlp) >> 7;
00099   int dVhp = w0hp_1_s17*(Vlp - Vhp) >> 17;
00100   Vlp += dVlp;
00101   Vhp += dVhp;
00102 }
00103 
00104 // ----------------------------------------------------------------------------
00105 // SID clocking - delta_t cycles.
00106 // ----------------------------------------------------------------------------
00107 RESID_INLINE
00108 void ExternalFilter::clock(cycle_count delta_t, short Vi)
00109 {
00110   // This is handy for testing.
00111   if (unlikely(!enabled)) {
00112     // Vo  = Vlp - Vhp;
00113     Vlp = Vi << 11;
00114     Vhp = 0;
00115     return;
00116   }
00117 
00118   // Maximum delta cycles for the external filter to work satisfactorily
00119   // is approximately 8.
00120   cycle_count delta_t_flt = 8;
00121 
00122   while (delta_t) {
00123     if (unlikely(delta_t < delta_t_flt)) {
00124       delta_t_flt = delta_t;
00125     }
00126 
00127     // Calculate filter outputs.
00128     // Vlp = Vlp + w0lp*(Vi - Vlp)*delta_t;
00129     // Vhp = Vhp + w0hp*(Vlp - Vhp)*delta_t;
00130     // Vo  = Vlp - Vhp;
00131 
00132     int dVlp = (w0lp_1_s7*delta_t_flt >> 3)*((Vi << 11) - Vlp) >> 4;
00133     int dVhp = (w0hp_1_s17*delta_t_flt >> 3)*(Vlp - Vhp) >> 14;
00134     Vlp += dVlp;
00135     Vhp += dVhp;
00136 
00137     delta_t -= delta_t_flt;
00138   }
00139 }
00140 
00141 
00142 // ----------------------------------------------------------------------------
00143 // Audio output (16 bits).
00144 // ----------------------------------------------------------------------------
00145 RESID_INLINE
00146 short ExternalFilter::output()
00147 {
00148   // Saturated arithmetics to guard against 16 bit sample overflow.
00149   const int half = 1 << 15;
00150   int Vo = (Vlp - Vhp) >> 11;
00151   if (Vo >= half) {
00152     Vo = half - 1;
00153   }
00154   else if (Vo < -half) {
00155     Vo = -half;
00156   }
00157   return Vo;
00158 }
00159 
00160 #endif // RESID_INLINING || defined(RESID_EXTFILT_CC)
00161 
00162 } // namespace reSID
00163 
00164 #endif // not RESID_EXTFILT_H