home *** CD-ROM | disk | FTP | other *** search
/ Large Pack of OldSkool DOS MOD Trackers / goattracker_2.68.zip / src / resid / filter.cpp < prev    next >
C/C++ Source or Header  |  2009-01-03  |  9KB  |  306 lines

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