filter.cc 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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. #define __FILTER_CC__
  20. #include "filter.h"
  21. // Maximum cutoff frequency is specified as
  22. // FCmax = 2.6e-5/C = 2.6e-5/2200e-12 = 11818.
  23. //
  24. // Measurements indicate a cutoff frequency range of approximately
  25. // 220Hz - 18kHz on a MOS6581 fitted with 470pF capacitors. The function
  26. // mapping FC to cutoff frequency has the shape of the tanh function, with
  27. // a discontinuity at FCHI = 0x80.
  28. // In contrast, the MOS8580 almost perfectly corresponds with the
  29. // specification of a linear mapping from 30Hz to 12kHz.
  30. //
  31. // The mappings have been measured by feeding the SID with an external
  32. // signal since the chip itself is incapable of generating waveforms of
  33. // higher fundamental frequency than 4kHz. It is best to use the bandpass
  34. // output at full resonance to pick out the cutoff frequency at any given
  35. // FC setting.
  36. //
  37. // The mapping function is specified with spline interpolation points and
  38. // the function values are retrieved via table lookup.
  39. //
  40. // NB! Cutoff frequency characteristics may vary, we have modeled two
  41. // particular Commodore 64s.
  42. fc_point Filter::f0_points_6581[] =
  43. {
  44. // FC f FCHI FCLO
  45. // ----------------------------
  46. { 0, 220 }, // 0x00 - repeated end point
  47. { 0, 220 }, // 0x00
  48. { 128, 230 }, // 0x10
  49. { 256, 250 }, // 0x20
  50. { 384, 300 }, // 0x30
  51. { 512, 420 }, // 0x40
  52. { 640, 780 }, // 0x50
  53. { 768, 1600 }, // 0x60
  54. { 832, 2300 }, // 0x68
  55. { 896, 3200 }, // 0x70
  56. { 960, 4300 }, // 0x78
  57. { 992, 5000 }, // 0x7c
  58. { 1008, 5400 }, // 0x7e
  59. { 1016, 5700 }, // 0x7f
  60. { 1023, 6000 }, // 0x7f 0x07
  61. { 1023, 6000 }, // 0x7f 0x07 - discontinuity
  62. { 1024, 4600 }, // 0x80 -
  63. { 1024, 4600 }, // 0x80
  64. { 1032, 4800 }, // 0x81
  65. { 1056, 5300 }, // 0x84
  66. { 1088, 6000 }, // 0x88
  67. { 1120, 6600 }, // 0x8c
  68. { 1152, 7200 }, // 0x90
  69. { 1280, 9500 }, // 0xa0
  70. { 1408, 12000 }, // 0xb0
  71. { 1536, 14500 }, // 0xc0
  72. { 1664, 16000 }, // 0xd0
  73. { 1792, 17100 }, // 0xe0
  74. { 1920, 17700 }, // 0xf0
  75. { 2047, 18000 }, // 0xff 0x07
  76. { 2047, 18000 } // 0xff 0x07 - repeated end point
  77. };
  78. fc_point Filter::f0_points_8580[] =
  79. {
  80. // FC f FCHI FCLO
  81. // ----------------------------
  82. { 0, 0 }, // 0x00 - repeated end point
  83. { 0, 0 }, // 0x00
  84. { 128, 800 }, // 0x10
  85. { 256, 1600 }, // 0x20
  86. { 384, 2500 }, // 0x30
  87. { 512, 3300 }, // 0x40
  88. { 640, 4100 }, // 0x50
  89. { 768, 4800 }, // 0x60
  90. { 896, 5600 }, // 0x70
  91. { 1024, 6500 }, // 0x80
  92. { 1152, 7500 }, // 0x90
  93. { 1280, 8400 }, // 0xa0
  94. { 1408, 9200 }, // 0xb0
  95. { 1536, 9800 }, // 0xc0
  96. { 1664, 10500 }, // 0xd0
  97. { 1792, 11000 }, // 0xe0
  98. { 1920, 11700 }, // 0xf0
  99. { 2047, 12500 }, // 0xff 0x07
  100. { 2047, 12500 } // 0xff 0x07 - repeated end point
  101. };
  102. // ----------------------------------------------------------------------------
  103. // Constructor.
  104. // ----------------------------------------------------------------------------
  105. Filter::Filter()
  106. {
  107. fc = 0;
  108. res = 0;
  109. filt = 0;
  110. voice3off = 0;
  111. hp_bp_lp = 0;
  112. vol = 0;
  113. // State of filter.
  114. Vhp = 0;
  115. Vbp = 0;
  116. Vlp = 0;
  117. Vnf = 0;
  118. enable_filter(true);
  119. // Create mappings from FC to cutoff frequency.
  120. interpolate(f0_points_6581, f0_points_6581
  121. + sizeof(f0_points_6581)/sizeof(*f0_points_6581) - 1,
  122. PointPlotter<sound_sample>(f0_6581), 1.0);
  123. interpolate(f0_points_8580, f0_points_8580
  124. + sizeof(f0_points_8580)/sizeof(*f0_points_8580) - 1,
  125. PointPlotter<sound_sample>(f0_8580), 1.0);
  126. set_chip_model(MOS6581);
  127. }
  128. // ----------------------------------------------------------------------------
  129. // Enable filter.
  130. // ----------------------------------------------------------------------------
  131. void Filter::enable_filter(bool enable)
  132. {
  133. enabled = enable;
  134. }
  135. // ----------------------------------------------------------------------------
  136. // Set chip model.
  137. // ----------------------------------------------------------------------------
  138. void Filter::set_chip_model(chip_model model)
  139. {
  140. if (model == MOS6581) {
  141. // The mixer has a small input DC offset. This is found as follows:
  142. //
  143. // The "zero" output level of the mixer measured on the SID audio
  144. // output pin is 5.50V at zero volume, and 5.44 at full
  145. // volume. This yields a DC offset of (5.44V - 5.50V) = -0.06V.
  146. //
  147. // The DC offset is thus -0.06V/1.05V ~ -1/18 of the dynamic range
  148. // of one voice. See voice.cc for measurement of the dynamic
  149. // range.
  150. mixer_DC = -0xfff*0xff/18 >> 7;
  151. f0 = f0_6581;
  152. f0_points = f0_points_6581;
  153. f0_count = sizeof(f0_points_6581)/sizeof(*f0_points_6581);
  154. }
  155. else {
  156. // No DC offsets in the MOS8580.
  157. mixer_DC = 0;
  158. f0 = f0_8580;
  159. f0_points = f0_points_8580;
  160. f0_count = sizeof(f0_points_8580)/sizeof(*f0_points_8580);
  161. }
  162. set_w0();
  163. set_Q();
  164. }
  165. // ----------------------------------------------------------------------------
  166. // SID reset.
  167. // ----------------------------------------------------------------------------
  168. void Filter::reset()
  169. {
  170. fc = 0;
  171. res = 0;
  172. filt = 0;
  173. voice3off = 0;
  174. hp_bp_lp = 0;
  175. vol = 0;
  176. // State of filter.
  177. Vhp = 0;
  178. Vbp = 0;
  179. Vlp = 0;
  180. Vnf = 0;
  181. set_w0();
  182. set_Q();
  183. }
  184. // ----------------------------------------------------------------------------
  185. // Register functions.
  186. // ----------------------------------------------------------------------------
  187. void Filter::writeFC_LO(reg8 fc_lo)
  188. {
  189. fc = ( fc & 0x7f8 ) | ( fc_lo & 0x007 );
  190. set_w0();
  191. }
  192. void Filter::writeFC_HI(reg8 fc_hi)
  193. {
  194. fc = ( (fc_hi << 3) & 0x7f8 ) | ( fc & 0x007 );
  195. set_w0();
  196. }
  197. void Filter::writeRES_FILT(reg8 res_filt)
  198. {
  199. res = (res_filt >> 4) & 0x0f;
  200. set_Q();
  201. filt = res_filt & 0x0f;
  202. }
  203. void Filter::writeMODE_VOL(reg8 mode_vol)
  204. {
  205. voice3off = mode_vol & 0x80;
  206. hp_bp_lp = (mode_vol >> 4) & 0x07;
  207. vol = mode_vol & 0x0f;
  208. }
  209. // Set filter cutoff frequency.
  210. void Filter::set_w0()
  211. {
  212. const double pi = 3.1415926535897932385;
  213. // Multiply with 1.048576 to facilitate division by 1 000 000 by right-
  214. // shifting 20 times (2 ^ 20 = 1048576).
  215. w0 = static_cast<sound_sample>(2*pi*f0[fc]*1.048576);
  216. // Limit f0 to 16kHz to keep 1 cycle filter stable.
  217. const sound_sample w0_max_1 = static_cast<sound_sample>(2*pi*16000*1.048576);
  218. w0_ceil_1 = w0 <= w0_max_1 ? w0 : w0_max_1;
  219. // Limit f0 to 4kHz to keep delta_t cycle filter stable.
  220. const sound_sample w0_max_dt = static_cast<sound_sample>(2*pi*4000*1.048576);
  221. w0_ceil_dt = w0 <= w0_max_dt ? w0 : w0_max_dt;
  222. }
  223. // Set filter resonance.
  224. void Filter::set_Q()
  225. {
  226. // Q is controlled linearly by res. Q has approximate range [0.707, 1.7].
  227. // As resonance is increased, the filter must be clocked more often to keep
  228. // stable.
  229. // The coefficient 1024 is dispensed of later by right-shifting 10 times
  230. // (2 ^ 10 = 1024).
  231. _1024_div_Q = static_cast<sound_sample>(1024.0/(0.707 + 1.0*res/0x0f));
  232. }
  233. // ----------------------------------------------------------------------------
  234. // Spline functions.
  235. // ----------------------------------------------------------------------------
  236. // ----------------------------------------------------------------------------
  237. // Return the array of spline interpolation points used to map the FC register
  238. // to filter cutoff frequency.
  239. // ----------------------------------------------------------------------------
  240. void Filter::fc_default(const fc_point*& points, int& count)
  241. {
  242. points = f0_points;
  243. count = f0_count;
  244. }
  245. // ----------------------------------------------------------------------------
  246. // Given an array of interpolation points p with n points, the following
  247. // statement will specify a new FC mapping:
  248. // interpolate(p, p + n - 1, filter.fc_plotter(), 1.0);
  249. // Note that the x range of the interpolation points *must* be [0, 2047],
  250. // and that additional end points *must* be present since the end points
  251. // are not interpolated.
  252. // ----------------------------------------------------------------------------
  253. PointPlotter<sound_sample> Filter::fc_plotter()
  254. {
  255. return PointPlotter<sound_sample>(f0);
  256. }