extfilt.h 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // ---------------------------------------------------------------------------
  2. // This file is part of reSID, a MOS6581 SID emulator engine.
  3. // Copyright (C) 2004 Dag Lem <resid@nimrod.no>
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation; either version 2 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with this program; if not, write to the Free Software
  17. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. // ---------------------------------------------------------------------------
  19. #ifndef __EXTFILT_H__
  20. #define __EXTFILT_H__
  21. #include "siddefs.h"
  22. // ----------------------------------------------------------------------------
  23. // The audio output stage in a Commodore 64 consists of two STC networks,
  24. // a low-pass filter with 3-dB frequency 16kHz followed by a high-pass
  25. // filter with 3-dB frequency 16Hz (the latter provided an audio equipment
  26. // input impedance of 1kOhm).
  27. // The STC networks are connected with a BJT supposedly meant to act as
  28. // a unity gain buffer, which is not really how it works. A more elaborate
  29. // model would include the BJT, however DC circuit analysis yields BJT
  30. // base-emitter and emitter-base impedances sufficiently low to produce
  31. // additional low-pass and high-pass 3dB-frequencies in the order of hundreds
  32. // of kHz. This calls for a sampling frequency of several MHz, which is far
  33. // too high for practical use.
  34. // ----------------------------------------------------------------------------
  35. class ExternalFilter
  36. {
  37. public:
  38. ExternalFilter();
  39. void enable_filter(bool enable);
  40. void set_chip_model(chip_model model);
  41. RESID_INLINE void clock(sound_sample Vi);
  42. RESID_INLINE void clock(cycle_count delta_t, sound_sample Vi);
  43. void reset();
  44. // Audio output (20 bits).
  45. RESID_INLINE sound_sample output();
  46. protected:
  47. // Filter enabled.
  48. bool enabled;
  49. // Maximum mixer DC offset.
  50. sound_sample mixer_DC;
  51. // State of filters.
  52. sound_sample Vlp; // lowpass
  53. sound_sample Vhp; // highpass
  54. sound_sample Vo;
  55. // Cutoff frequencies.
  56. sound_sample w0lp;
  57. sound_sample w0hp;
  58. friend class cSID;
  59. };
  60. // ----------------------------------------------------------------------------
  61. // Inline functions.
  62. // The following functions are defined inline because they are called every
  63. // time a sample is calculated.
  64. // ----------------------------------------------------------------------------
  65. #if RESID_INLINING || defined(__EXTFILT_CC__)
  66. // ----------------------------------------------------------------------------
  67. // SID clocking - 1 cycle.
  68. // ----------------------------------------------------------------------------
  69. RESID_INLINE
  70. void ExternalFilter::clock(sound_sample Vi)
  71. {
  72. // This is handy for testing.
  73. if (!enabled) {
  74. // Remove maximum DC level since there is no filter to do it.
  75. Vlp = Vhp = 0;
  76. Vo = Vi - mixer_DC;
  77. return;
  78. }
  79. // delta_t is converted to seconds given a 1MHz clock by dividing
  80. // with 1 000 000.
  81. // Calculate filter outputs.
  82. // Vo = Vlp - Vhp;
  83. // Vlp = Vlp + w0lp*(Vi - Vlp)*delta_t;
  84. // Vhp = Vhp + w0hp*(Vlp - Vhp)*delta_t;
  85. sound_sample dVlp = (w0lp >> 8)*(Vi - Vlp) >> 12;
  86. sound_sample dVhp = w0hp*(Vlp - Vhp) >> 20;
  87. Vo = Vlp - Vhp;
  88. Vlp += dVlp;
  89. Vhp += dVhp;
  90. }
  91. // ----------------------------------------------------------------------------
  92. // SID clocking - delta_t cycles.
  93. // ----------------------------------------------------------------------------
  94. RESID_INLINE
  95. void ExternalFilter::clock(cycle_count delta_t,
  96. sound_sample Vi)
  97. {
  98. // This is handy for testing.
  99. if (!enabled) {
  100. // Remove maximum DC level since there is no filter to do it.
  101. Vlp = Vhp = 0;
  102. Vo = Vi - mixer_DC;
  103. return;
  104. }
  105. // Maximum delta cycles for the external filter to work satisfactorily
  106. // is approximately 8.
  107. cycle_count delta_t_flt = 8;
  108. while (delta_t) {
  109. if (delta_t < delta_t_flt) {
  110. delta_t_flt = delta_t;
  111. }
  112. // delta_t is converted to seconds given a 1MHz clock by dividing
  113. // with 1 000 000.
  114. // Calculate filter outputs.
  115. // Vo = Vlp - Vhp;
  116. // Vlp = Vlp + w0lp*(Vi - Vlp)*delta_t;
  117. // Vhp = Vhp + w0hp*(Vlp - Vhp)*delta_t;
  118. sound_sample dVlp = (w0lp*delta_t_flt >> 8)*(Vi - Vlp) >> 12;
  119. sound_sample dVhp = w0hp*delta_t_flt*(Vlp - Vhp) >> 20;
  120. Vo = Vlp - Vhp;
  121. Vlp += dVlp;
  122. Vhp += dVhp;
  123. delta_t -= delta_t_flt;
  124. }
  125. }
  126. // ----------------------------------------------------------------------------
  127. // Audio output (19.5 bits).
  128. // ----------------------------------------------------------------------------
  129. RESID_INLINE
  130. sound_sample ExternalFilter::output()
  131. {
  132. return Vo;
  133. }
  134. #endif // RESID_INLINING || defined(__EXTFILT_CC__)
  135. #endif // not __EXTFILT_H__