AudioJack: do not use QVector in processCallback()
[lmms.git] / plugins / sid / filter.cc
blobfb548e30acddc57525ace4d3b3b90d3655e2a27a
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.
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 // ---------------------------------------------------------------------------
20 #define __FILTER_CC__
21 #include "filter.h"
23 // Maximum cutoff frequency is specified as
24 // FCmax = 2.6e-5/C = 2.6e-5/2200e-12 = 11818.
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.
39 // The mapping function is specified with spline interpolation points and
40 // the function values are retrieved via table lookup.
42 // NB! Cutoff frequency characteristics may vary, we have modeled two
43 // particular Commodore 64s.
45 fc_point Filter::f0_points_6581[] =
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
82 fc_point Filter::f0_points_8580[] =
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
108 // ----------------------------------------------------------------------------
109 // Constructor.
110 // ----------------------------------------------------------------------------
111 Filter::Filter()
113 fc = 0;
115 res = 0;
117 filt = 0;
119 voice3off = 0;
121 hp_bp_lp = 0;
123 vol = 0;
125 // State of filter.
126 Vhp = 0;
127 Vbp = 0;
128 Vlp = 0;
129 Vnf = 0;
131 enable_filter(true);
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);
141 set_chip_model(MOS6581);
145 // ----------------------------------------------------------------------------
146 // Enable filter.
147 // ----------------------------------------------------------------------------
148 void Filter::enable_filter(bool enable)
150 enabled = enable;
154 // ----------------------------------------------------------------------------
155 // Set chip model.
156 // ----------------------------------------------------------------------------
157 void Filter::set_chip_model(chip_model model)
159 if (model == MOS6581) {
160 // The mixer has a small input DC offset. This is found as follows:
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.
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.
170 mixer_DC = -0xfff*0xff/18 >> 7;
172 f0 = f0_6581;
173 f0_points = f0_points_6581;
174 f0_count = sizeof(f0_points_6581)/sizeof(*f0_points_6581);
176 else {
177 // No DC offsets in the MOS8580.
178 mixer_DC = 0;
180 f0 = f0_8580;
181 f0_points = f0_points_8580;
182 f0_count = sizeof(f0_points_8580)/sizeof(*f0_points_8580);
185 set_w0();
186 set_Q();
190 // ----------------------------------------------------------------------------
191 // SID reset.
192 // ----------------------------------------------------------------------------
193 void Filter::reset()
195 fc = 0;
197 res = 0;
199 filt = 0;
201 voice3off = 0;
203 hp_bp_lp = 0;
205 vol = 0;
207 // State of filter.
208 Vhp = 0;
209 Vbp = 0;
210 Vlp = 0;
211 Vnf = 0;
213 set_w0();
214 set_Q();
218 // ----------------------------------------------------------------------------
219 // Register functions.
220 // ----------------------------------------------------------------------------
221 void Filter::writeFC_LO(reg8 fc_lo)
223 fc = ( fc & 0x7f8 ) | ( fc_lo & 0x007 );
224 set_w0();
227 void Filter::writeFC_HI(reg8 fc_hi)
229 fc = ( (fc_hi << 3) & 0x7f8 ) | ( fc & 0x007 );
230 set_w0();
233 void Filter::writeRES_FILT(reg8 res_filt)
235 res = (res_filt >> 4) & 0x0f;
236 set_Q();
238 filt = res_filt & 0x0f;
241 void Filter::writeMODE_VOL(reg8 mode_vol)
243 voice3off = mode_vol & 0x80;
245 hp_bp_lp = (mode_vol >> 4) & 0x07;
247 vol = mode_vol & 0x0f;
250 // Set filter cutoff frequency.
251 void Filter::set_w0()
253 const double pi = 3.1415926535897932385;
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);
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;
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;
268 // Set filter resonance.
269 void Filter::set_Q()
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.
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));
280 // ----------------------------------------------------------------------------
281 // Spline functions.
282 // ----------------------------------------------------------------------------
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)
290 points = f0_points;
291 count = f0_count;
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()
304 return PointPlotter<sound_sample>(f0);