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