libsidplayfp 1.0.3
|
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