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

  1. //  This file is part of reSID, a MOS6581 SID emulator engine.
  2. //  Copyright (C) 2004  Dag Lem <resid@nimrod.no>
  3. //
  4. //  This program is free software; you can redistribute it and/or modify
  5. //  it under the terms of the GNU General Public License as published by
  6. //  the Free Software Foundation; either version 2 of the License, or
  7. //  (at your option) any later version.
  8. //
  9. //  This program is distributed in the hope that it will be useful,
  10. //  but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //  GNU General Public License for more details.
  13. //
  14. //  You should have received a copy of the GNU General Public License
  15. //  along with this program; if not, write to the Free Software
  16. //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  17. //  ---------------------------------------------------------------------------
  18. //  Filter distortion code written by Antti S. Lankila 2007 - 2008.
  19.  
  20. #ifndef __FILTER_FP_H__
  21. #define __FILTER_FP_H__
  22.  
  23. #include <math.h>
  24. #include "siddefs-fp.h"
  25.  
  26. // ----------------------------------------------------------------------------
  27. // The SID filter is modeled with a two-integrator-loop biquadratic filter,
  28. // which has been confirmed by Bob Yannes to be the actual circuit used in
  29. // the SID chip.
  30. //
  31. // Measurements show that excellent emulation of the SID filter is achieved,
  32. // except when high resonance is combined with high sustain levels.
  33. // In this case the SID op-amps are performing less than ideally and are
  34. // causing some peculiar behavior of the SID filter. This however seems to
  35. // have more effect on the overall amplitude than on the color of the sound.
  36. //
  37. // The theory for the filter circuit can be found in "Microelectric Circuits"
  38. // by Adel S. Sedra and Kenneth C. Smith.
  39. // The circuit is modeled based on the explanation found there except that
  40. // an additional inverter is used in the feedback from the bandpass output,
  41. // allowing the summer op-amp to operate in single-ended mode. This yields
  42. // inverted filter outputs with levels independent of Q, which corresponds with
  43. // the results obtained from a real SID.
  44. //
  45. // We have been able to model the summer and the two integrators of the circuit
  46. // to form components of an IIR filter.
  47. // Vhp is the output of the summer, Vbp is the output of the first integrator,
  48. // and Vlp is the output of the second integrator in the filter circuit.
  49. //
  50. // According to Bob Yannes, the active stages of the SID filter are not really
  51. // op-amps. Rather, simple NMOS inverters are used. By biasing an inverter
  52. // into its region of quasi-linear operation using a feedback resistor from
  53. // input to output, a MOS inverter can be made to act like an op-amp for
  54. // small signals centered around the switching threshold.
  55. //
  56. // Qualified guesses at SID filter schematics are depicted below.
  57. //
  58. // SID filter
  59. // ----------
  60. // 
  61. //     -----------------------------------------------
  62. //    |                                               |
  63. //    |            ---Rq--                            |
  64. //    |           |       |                           |
  65. //    |  ------------<A]-----R1---------              |
  66. //    | |                               |             |
  67. //    | |                        ---C---|      ---C---|
  68. //    | |                       |       |     |       |
  69. //    |  --R1--    ---R1--      |---Rs--|     |---Rs--| 
  70. //    |        |  |       |     |       |     |       |
  71. //     ----R1--|-----[A>--|--R-----[A>--|--R-----[A>--|
  72. //             |          |             |             |
  73. // vi -----R1--           |             |             |
  74. // 
  75. //                       vhp           vbp           vlp
  76. // 
  77. // 
  78. // vi  - input voltage
  79. // vhp - highpass output
  80. // vbp - bandpass output
  81. // vlp - lowpass output
  82. // [A> - op-amp
  83. // R1  - summer resistor
  84. // Rq  - resistor array controlling resonance (4 resistors)
  85. // R   - NMOS FET voltage controlled resistor controlling cutoff frequency
  86. // Rs  - shunt resitor
  87. // C   - capacitor
  88. // 
  89. // 
  90. // 
  91. // SID integrator
  92. // --------------
  93. // 
  94. //                                   V+
  95. // 
  96. //                                   |
  97. //                                   |
  98. //                              -----|
  99. //                             |     |
  100. //                             | ||--
  101. //                              -||
  102. //                   ---C---     ||->
  103. //                  |       |        |
  104. //                  |---Rs-----------|---- vo
  105. //                  |                |
  106. //                  |            ||--
  107. // vi ----     -----|------------||
  108. //        |   ^     |            ||->
  109. //        |___|     |                |
  110. //        -----     |                |
  111. //          |       |                |
  112. //          |---R2--                 |
  113. //          |
  114. //          R1                       V-
  115. //          |
  116. //          |
  117. // 
  118. //          Vw
  119. //
  120. // ----------------------------------------------------------------------------
  121. class FilterFP
  122. {
  123. public:
  124.   FilterFP();
  125.  
  126.   void enable_filter(bool enable);
  127.   void set_chip_model(chip_model model);
  128.   void set_distortion_properties(float, float, float);
  129.   void set_type3_properties(float, float, float, float);
  130.   void set_type4_properties(float, float);
  131.   void set_clock_frequency(float);
  132.  
  133.   RESID_INLINE
  134.   float clock(float voice1, float voice2, float voice3,
  135.               float ext_in);
  136.   void reset();
  137.  
  138.   // Write registers.
  139.   void writeFC_LO(reg8);
  140.   void writeFC_HI(reg8);
  141.   void writeRES_FILT(reg8);
  142.   void writeMODE_VOL(reg8);
  143.  
  144. private:
  145.   void set_Q();
  146.   void set_w0();
  147.   float type3_w0(const float source, const float offset);
  148.   float type4_w0();
  149.   void calculate_helpers();
  150.   void nuke_denormals();
  151.  
  152.   // Filter enabled.
  153.   bool enabled;
  154.  
  155.   // 6581/8580 filter model (XXX: we should specialize in separate classes)
  156.   chip_model model;
  157.  
  158.   // Filter cutoff frequency.
  159.   reg12 fc;
  160.  
  161.   // Filter resonance.
  162.   reg8 res;
  163.  
  164.   // Selects which inputs to route through filter.
  165.   reg8 filt;
  166.  
  167.   // Switch voice 3 off.
  168.   reg8 voice3off;
  169.  
  170.   // Highpass, bandpass, and lowpass filter modes.
  171.   reg8 hp_bp_lp;
  172.  
  173.   // Output master volume.
  174.   reg4 vol;
  175.   float volf; /* avoid integer-to-float conversion at output */
  176.  
  177.   // clock
  178.   float clock_frequency;
  179.  
  180.   /* Distortion params for Type3 */
  181.   float distortion_rate, distortion_point, distortion_cf_threshold;
  182.  
  183.   /* Type3 params. */
  184.   float type3_baseresistance, type3_offset, type3_steepness, type3_minimumfetresistance;
  185.  
  186.   /* Type4 params */
  187.   float type4_k, type4_b;
  188.  
  189.   // State of filter.
  190.   float Vhp, Vbp, Vlp;
  191.  
  192.   /* Resonance/Distortion/Type3/Type4 helpers. */
  193.   float type4_w0_cache, _1_div_Q, type3_fc_kink_exp, distortion_CT,
  194.         type3_fc_distortion_offset_bp, type3_fc_distortion_offset_hp;
  195.  
  196. friend class SIDFP;
  197. };
  198.  
  199. // ----------------------------------------------------------------------------
  200. // Inline functions.
  201. // The following functions are defined inline because they are called every
  202. // time a sample is calculated.
  203. // ----------------------------------------------------------------------------
  204.  
  205. /* kinkiness of DAC:
  206.  * some chips have more, some less. We should make this tunable. */
  207. const float kinkiness = 0.966f;
  208. const float sidcaps_6581 = 470e-12f;
  209. const float outputleveldifference_lp_bp = 1.4f;
  210. const float outputleveldifference_bp_hp = 1.2f;
  211.  
  212. RESID_INLINE
  213. static float fastexp(float val) {
  214.     typedef union {
  215.         int i;
  216.         float f;
  217.     } conv;
  218.  
  219.     conv tmp;
  220.  
  221.     /* single precision fp has 1 + 8 + 23 bits, exponent bias is 127.
  222.      * It therefore follows that we need to shift left by 23 bits, and to
  223.      * calculate exp(x) instead of pow(2, x) we divide the power by ln(2). */
  224.     const float a = (1 << 23) / M_LN2_f;
  225.     /* The other factor corrects for the exponent bias so that 2^0 = 1. */
  226.     const float b = (1 << 23) * 127;
  227.     /* According to "A Fast, Compact Approximation of the Exponential Function"
  228.      * by Nicol N. Schraudolph, 60801.48 yields the minimum RMS error for the
  229.      * piecewise-linear approximation when using doubles (20 bits residual).
  230.      * We have 23 bits, so we scale this value by 8. */
  231.     const float c = 60801.48f * 8.f + 0.5f;
  232.  
  233.     /* Parenthesis are important: C standard disallows folding subtraction.
  234.      * Unfortunately GCC appears to generate a write to memory rather than
  235.      * handle this conversion entirely in registers. */
  236.     tmp.i = (int)(a * val + (b - c));
  237.     return tmp.f;
  238. }
  239.  
  240. RESID_INLINE
  241. float FilterFP::type3_w0(const float source, const float distoffset)
  242. {
  243.     /* The distortion appears to be the result of MOSFET entering saturation
  244.      * mode. The conductance of a FET is proportional to:
  245.      *
  246.      * ohmic = 2 * (Vgs - Vt) * Vds - Vds^2
  247.      * saturation = (Vgs - Vt)^2
  248.      *
  249.      * The FET switches to saturation mode when Vgs - Vt < Vds.
  250.      *
  251.      * In the circuit, the Vgs is mixed with the Vds signal, which gives
  252.      * (Vgs + Vds) / 2 as the gate voltage. Doing the substitutions we get:
  253.      *
  254.      * ohmic = 2 * ((Vgs + Vds) / 2 - Vt) * Vds - Vds^2 = (Vgs - Vt) * Vds
  255.      * saturation = ((Vgs + Vds) / 2 - Vt)^2
  256.      *
  257.      * Therefore: once the Vds crosses a threshold given by the gate and
  258.      * threshold FET conductance begins to increase faster. The exact shape
  259.      * for this effect is a parabola.
  260.      *
  261.      * The scaling term here tries to match the FC control level with
  262.      * the signal level in simulation. On the chip, the FC control is
  263.      * biased by forcing its highest DAC bit in the 1 position, thus
  264.      * limiting the electrical range to half. Therefore one can guess that
  265.      * the real FC range is half of the full voice range.
  266.      *
  267.      * On the simulation, FC goes to 2047 and the voices to 4095 * 255.
  268.      * If the FC control was intact, then the scaling factor would be
  269.      * 1/512. (Simulation voices are 512 times "louder" intrinsically.)
  270.      * As the real chip's FC has reduced range, the scaling required to
  271.      * match levels is 1/256. */
  272.  
  273.     float fetresistance = type3_fc_kink_exp;
  274.     if (source > distoffset) {
  275.         const float dist = source - distoffset;
  276.         fetresistance *= fastexp(dist * type3_steepness * distortion_rate);
  277.     }
  278.     const float dynamic_resistance = type3_minimumfetresistance + fetresistance;
  279.  
  280.     /* 2 parallel resistors */
  281.     const float _1_div_resistance = (type3_baseresistance + dynamic_resistance) / (type3_baseresistance * dynamic_resistance);
  282.     /* 1.f / (clock * caps * resistance) */
  283.     return distortion_CT * _1_div_resistance;
  284. }
  285.  
  286. RESID_INLINE
  287. float FilterFP::type4_w0()
  288. {
  289.     const float freq = type4_k * fc + type4_b;
  290.     return 2.f * M_PI_f * freq / clock_frequency;
  291. }
  292.  
  293. // ----------------------------------------------------------------------------
  294. // SID clocking - 1 cycle.
  295. // ----------------------------------------------------------------------------
  296. RESID_INLINE
  297. float FilterFP::clock(float voice1,
  298.                    float voice2,
  299.                    float voice3,
  300.                    float ext_in)
  301. {
  302.     /* Avoid denormal numbers by using small offsets from 0 */
  303.     float Vi = 0.f, Vnf = 0.f, Vf = 0.f;
  304.  
  305.     // Route voices into or around filter.
  306.     ((filt & 1) ? Vi : Vnf) += voice1;
  307.     ((filt & 2) ? Vi : Vnf) += voice2;
  308.     // NB! Voice 3 is not silenced by voice3off if it is routed through
  309.     // the filter.
  310.     if (filt & 4)
  311.         Vi += voice3;
  312.     else if (! voice3off)
  313.         Vnf += voice3;
  314.     ((filt & 8) ? Vi : Vnf) += ext_in;
  315.   
  316.     if (! enabled)
  317.         return (Vnf - Vi) * volf;
  318.  
  319.     if (hp_bp_lp & 1)
  320.         Vf += Vlp;
  321.     if (hp_bp_lp & 2)
  322.         Vf += Vbp;
  323.     if (hp_bp_lp & 4)
  324.         Vf += Vhp;
  325.     
  326.     if (model == MOS6581FP) {
  327.         float diff1, diff2;
  328.  
  329.         Vhp = Vbp * _1_div_Q * (1.f/outputleveldifference_bp_hp) - Vlp * (1.f/outputleveldifference_bp_hp) - Vi * 0.5f;
  330.  
  331.         /* the input summer mixing, or something like it... */
  332.         diff1 = (Vlp - Vbp) * distortion_cf_threshold;
  333.         diff2 = (Vhp - Vbp) * distortion_cf_threshold;
  334.         Vlp -= diff1;
  335.         Vbp += diff1;
  336.         Vbp += diff2;
  337.         Vhp -= diff2;
  338.  
  339.         /* Model output strip mixing. Doing it now that HP state
  340.          * variable modifying still makes some difference.
  341.          * (Phase error, though.) */
  342.         if (hp_bp_lp & 1)
  343.             Vlp += (Vf + Vnf - Vlp) * distortion_cf_threshold;
  344.         if (hp_bp_lp & 2)
  345.             Vbp += (Vf + Vnf - Vbp) * distortion_cf_threshold;
  346.         if (hp_bp_lp & 4)
  347.             Vhp += (Vf + Vnf - Vhp) * distortion_cf_threshold;
  348.        
  349.         /* Simulating the exponential VCR that the FET block is... */
  350.         Vlp -= Vbp * type3_w0(Vbp, type3_fc_distortion_offset_bp);
  351.         Vbp -= Vhp * type3_w0(Vhp, type3_fc_distortion_offset_hp) * outputleveldifference_bp_hp;
  352.  
  353.         /* Tuned based on Fred Gray's Break Thru. It is probably not a hard
  354.          * discontinuity but a saturation effect... */
  355.         if (Vnf > 3.2e6f)
  356.             Vnf = 3.2e6f;
  357.         
  358.         Vf += Vnf + Vlp * (outputleveldifference_lp_bp - 1.f);
  359.     } else {
  360.         /* On the 8580, BP appears mixed in phase with the rest. */
  361.         Vhp = -Vbp * _1_div_Q - Vlp - Vi;
  362.         Vlp += Vbp * type4_w0_cache;
  363.         Vbp += Vhp * type4_w0_cache;
  364.  
  365.         Vf += Vnf;
  366.     }
  367.     
  368.     return Vf * volf;
  369. }
  370.  
  371. RESID_INLINE
  372. void FilterFP::nuke_denormals()
  373. {
  374.     /* We could use the flush-to-zero flag or denormals-are-zero on systems
  375.      * where compiling with -msse and -mfpmath=sse is acceptable. Since this
  376.      * doesn't include general VICE builds, we do this instead. */
  377.     if (Vbp > -1e-12f && Vbp < 1e-12f)
  378.         Vbp = 0;
  379.     if (Vlp > -1e-12f && Vlp < 1e-12f)
  380.         Vlp = 0;
  381. }
  382.  
  383. #endif // not __FILTER_FP_H__
  384.