1
// This file is part of reSID, a MOS6581 SID emulator engine.
2
// Copyright (C) 2004 Dag Lem <resid@nimrod.no>
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.
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.
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.
24
#include "siddefs-fp.h"
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
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.
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.
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.
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.
56
// Qualified guesses at SID filter schematics are depicted below.
61
// -----------------------------------------------
65
// | ------------<A]-----R1--------- |
67
// | | ---C---| ---C---|
69
// | --R1-- ---R1-- |---Rs--| |---Rs--|
71
// ----R1--|-----[A>--|--R-----[A>--|--R-----[A>--|
79
// vhp - highpass output
80
// vbp - bandpass output
81
// vlp - lowpass output
83
// R1 - summer resistor
84
// Rq - resistor array controlling resonance (4 resistors)
85
// R - NMOS FET voltage controlled resistor controlling cutoff frequency
104
// |---Rs-----------|---- vo
107
// vi ---- -----|------------||
120
// ----------------------------------------------------------------------------
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);
134
float clock(float voice1, float voice2, float voice3,
139
void writeFC_LO(reg8);
140
void writeFC_HI(reg8);
141
void writeRES_FILT(reg8);
142
void writeMODE_VOL(reg8);
147
float type3_w0(const float source, const float offset);
149
void calculate_helpers();
150
void nuke_denormals();
155
// 6581/8580 filter model (XXX: we should specialize in separate classes)
158
// Filter cutoff frequency.
164
// Selects which inputs to route through filter.
167
// Switch voice 3 off.
170
// Highpass, bandpass, and lowpass filter modes.
173
// Output master volume.
175
float volf; /* avoid integer-to-float conversion at output */
178
float clock_frequency;
180
/* Distortion params for Type3 */
181
float distortion_rate, distortion_point, distortion_cf_threshold;
184
float type3_baseresistance, type3_offset, type3_steepness, type3_minimumfetresistance;
187
float type4_k, type4_b;
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;
199
// ----------------------------------------------------------------------------
201
// The following functions are defined inline because they are called every
202
// time a sample is calculated.
203
// ----------------------------------------------------------------------------
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;
213
static float fastexp(float val) {
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;
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));
241
float FilterFP::type3_w0(const float source, const float distoffset)
243
/* The distortion appears to be the result of MOSFET entering saturation
244
* mode. The conductance of a FET is proportional to:
246
* ohmic = 2 * (Vgs - Vt) * Vds - Vds^2
247
* saturation = (Vgs - Vt)^2
249
* The FET switches to saturation mode when Vgs - Vt < Vds.
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:
254
* ohmic = 2 * ((Vgs + Vds) / 2 - Vt) * Vds - Vds^2 = (Vgs - Vt) * Vds
255
* saturation = ((Vgs + Vds) / 2 - Vt)^2
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.
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.
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. */
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);
278
const float dynamic_resistance = type3_minimumfetresistance + fetresistance;
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;
287
float FilterFP::type4_w0()
289
const float freq = type4_k * fc + type4_b;
290
return 2.f * M_PI_f * freq / clock_frequency;
293
// ----------------------------------------------------------------------------
294
// SID clocking - 1 cycle.
295
// ----------------------------------------------------------------------------
297
float FilterFP::clock(float voice1,
302
/* Avoid denormal numbers by using small offsets from 0 */
303
float Vi = 0.f, Vnf = 0.f, Vf = 0.f;
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
312
else if (! voice3off)
314
((filt & 8) ? Vi : Vnf) += ext_in;
317
return (Vnf - Vi) * volf;
326
if (model == MOS6581FP) {
329
Vhp = Vbp * _1_div_Q * (1.f/outputleveldifference_bp_hp) - Vlp * (1.f/outputleveldifference_bp_hp) - Vi * 0.5f;
331
/* the input summer mixing, or something like it... */
332
diff1 = (Vlp - Vbp) * distortion_cf_threshold;
333
diff2 = (Vhp - Vbp) * distortion_cf_threshold;
339
/* Model output strip mixing. Doing it now that HP state
340
* variable modifying still makes some difference.
341
* (Phase error, though.) */
343
Vlp += (Vf + Vnf - Vlp) * distortion_cf_threshold;
345
Vbp += (Vf + Vnf - Vbp) * distortion_cf_threshold;
347
Vhp += (Vf + Vnf - Vhp) * distortion_cf_threshold;
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;
353
/* Tuned based on Fred Gray's Break Thru. It is probably not a hard
354
* discontinuity but a saturation effect... */
358
Vf += Vnf + Vlp * (outputleveldifference_lp_bp - 1.f);
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;
372
void FilterFP::nuke_denormals()
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)
379
if (Vlp > -1e-12f && Vlp < 1e-12f)
383
#endif // not __FILTER_H__