BogaudioModules

BogaudioModules for VCV Rack
Log | Files | Refs | README | LICENSE

TestVCF.cpp (26645B)


      1 
      2 #include "TestVCF.hpp"
      3 
      4 #include <complex>
      5 
      6 void TestVCF::LPFModel::setParams(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
      7 	resonance = std::max(0.1f, 10.0f * resonance);
      8 	_filter.setParams(APP->engine->getSampleRate(), cutoff, resonance);
      9 }
     10 
     11 float TestVCF::LPFModel::next(float sample) {
     12 	return _filter.next(sample);
     13 }
     14 
     15 void TestVCF::MultipoleModel::setParams(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
     16 	MultipoleFilter::Type type = mode == HIGHPASS_MODE ? MultipoleFilter::HP_TYPE : MultipoleFilter::LP_TYPE;
     17 	_filter.setParams(type, 2 * (1 + (int)poles), APP->engine->getSampleRate(), cutoff, topology * 0.29);
     18 }
     19 
     20 float TestVCF::MultipoleModel::next(float sample) {
     21 	return _filter.next(sample);
     22 }
     23 
     24 void TestVCF::ComplexModel::setParams(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
     25 	_pair1a.setComplexParams(
     26 		1.0f,
     27 		0.0f,
     28 		0.0f,
     29 		0.7f,
     30 		2.0f * M_PI * cutoff / APP->engine->getSampleRate()
     31 	);
     32 	_pair1b.setComplexParams(
     33 		1.0f,
     34 		0.0f,
     35 		0.0f,
     36 		0.7f,
     37 		-2.0f * M_PI * cutoff / APP->engine->getSampleRate()
     38 	);
     39 }
     40 
     41 float TestVCF::ComplexModel::next(float sample) {
     42 	return _pair1a.next(sample); // + _pair1b.next(sample);
     43 }
     44 
     45 void TestVCF::BookExampleModel::setParams(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
     46 	_poles = poles;
     47 
     48 	// _filter.setParams(
     49 	// 	0.00473f,
     50 	// 	0.00946f,
     51 	// 	0.00473f,
     52 	// 	1.0f,
     53 	// 	-1.8485f,
     54 	// 	0.8674f
     55 	// );
     56 
     57 	resonance = std::max(0.1f, resonance) * 10.0f;
     58 	float iq = 1.0f / resonance;
     59 	float wa = tanf(0.5f * (2.0f * M_PI * cutoff * APP->engine->getSampleTime()));
     60 	float wa2 = wa * wa;
     61 	if (mode == HIGHPASS_MODE) {
     62 		float a0 = 1.0f;
     63 		float a1 = -2.0f;
     64 		float a2 = 1.0f;
     65 		float b0 = 1.0f * wa2 + wa * iq + 1.0f;
     66 		float b1 = 2.0f * wa2 - 2.0f;
     67 		float b2 = 1.0f * wa2 - wa * iq + 1.0f;
     68 		_filter1.setParams(a0, a1, a2, b0, b1, b2);
     69 		_filter2.setParams(a0, a1, a2, b0, b1, b2);
     70 		_filter3.setParams(a0, a1, a2, b0, b1, b2);
     71 		_filter4.setParams(a0, a1, a2, b0, b1, b2);
     72 	}
     73 	else {
     74 		float a0 = wa2;
     75 		float a1 = 2.0f * wa2;
     76 		float a2 = wa2;
     77 		float b0 = wa2 + wa * iq + 1.0f;
     78 		float b1 = 2.0f * wa2 - 2.0f;
     79 		float b2 = wa2 - wa * iq + 1.0f;
     80 		_filter1.setParams(a0, a1, a2, b0, b1, b2);
     81 		_filter2.setParams(a0, a1, a2, b0, b1, b2);
     82 		_filter3.setParams(a0, a1, a2, b0, b1, b2);
     83 		_filter4.setParams(a0, a1, a2, b0, b1, b2);
     84 
     85 		// {
     86 		// 	iq = 0.7654f;
     87 		// 	float a0 = wa2;
     88 		// 	float a1 = 2.0f * wa2;
     89 		// 	float a2 = wa2;
     90 		// 	float b0 = wa2 + wa * iq + 1.0f;
     91 		// 	float b1 = 2.0f * wa2 - 2.0f;
     92 		// 	float b2 = wa2 - wa * iq + 1.0f;
     93 		// 	_filter1.setParams(a0, a1, a2, b0, b1, b2);
     94 		// }
     95 		// {
     96 		// 	iq = 1.847f;
     97 		// 	float a0 = wa2;
     98 		// 	float a1 = 2.0f * wa2;
     99 		// 	float a2 = wa2;
    100 		// 	float b0 = wa2 + wa * iq + 1.0f;
    101 		// 	float b1 = 2.0f * wa2 - 2.0f;
    102 		// 	float b2 = wa2 - wa * iq + 1.0f;
    103 		// 	_filter2.setParams(a0, a1, a2, b0, b1, b2);
    104 		// }
    105 		// // _filter3.setParams(a0, a1, a2, b0, b1, b2);
    106 		// // _filter4.setParams(a0, a1, a2, b0, b1, b2);
    107 	}
    108 }
    109 
    110 float TestVCF::BookExampleModel::next(float sample) {
    111 	float out = _filter1.next(sample);
    112 	switch (_poles) {
    113 		case POLES_2: {
    114 			break;
    115 		}
    116 		case POLES_4: {
    117 			out = _filter2.next(out);
    118 			break;
    119 		}
    120 		case POLES_6: {
    121 			out = _filter2.next(out);
    122 			out = _filter3.next(out);
    123 			break;
    124 		}
    125 		default: {
    126 			out = _filter2.next(out);
    127 			out = _filter3.next(out);
    128 			out = _filter4.next(out);
    129 			break;
    130 		}
    131 	}
    132 	return out;
    133 }
    134 
    135 void TestVCF::ButterworthModel::setParams(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
    136 	_nFilters = 1 + (int)poles;
    137 	assert(_nFilters <= maxPoles / 2);
    138 
    139 	cutoff = std::max(2.0f, cutoff); // FIXME: another way to avoid DC exploding near 0 hz?
    140 	// resonance = 1.0f + resonance * 20.0f / (2.0f * (float)_nFilters); // FIXME
    141 	// float iq = 1.0f / resonance;
    142 	float iq = 0.707 - 0.65 * resonance / (1.0f + std::pow(0.6f * std::log((float)_nFilters), 2.0));
    143 	float wa = tanf(0.5f * (2.0f * M_PI * cutoff * APP->engine->getSampleTime()));
    144 	float wa2 = wa * wa;
    145 	if (mode == HIGHPASS_MODE) {
    146 		float a0 = 1.0f;
    147 		float a1 = -2.0f;
    148 		float a2 = 1.0f;
    149 		float b1 = 2.0f * wa2 - 2.0f;
    150 		float i2n = 1.0f / (4.0f * _nFilters);
    151 		for (int i = 0; i < _nFilters; ++i) {
    152 			float x = iq * 2.0f * cosf((float)i * M_PI * i2n);
    153 			float b0 = 1.0f * wa2 + wa * x + 1.0f;
    154 			float b2 = 1.0f * wa2 - wa * x + 1.0f;
    155 			_filters[i].setParams(a0, a1, a2, b0, b1, b2);
    156 		}
    157 	}
    158 	else {
    159 		float a0 = wa2;
    160 		float a1 = 2.0f * wa2;
    161 		float a2 = wa2;
    162 		float b1 = 2.0f * wa2 - 2.0f;
    163 		float i2n = 1.0f / (4.0f * _nFilters);
    164 		for (int i = 0; i < _nFilters; ++i) {
    165 			float x = iq * 2.0f * cosf((float)i * M_PI * i2n);
    166 			float b0 = wa2 + wa * x + 1.0f;
    167 			float b2 = wa2 - wa * x + 1.0f;
    168 			_filters[i].setParams(a0, a1, a2, b0, b1, b2);
    169 		}
    170 	}
    171 }
    172 
    173 float TestVCF::ButterworthModel::next(float sample) {
    174 	for (int i = 0; i < _nFilters; ++i) {
    175 		sample = _filters[i].next(sample);
    176 	}
    177 	return sample;
    178 }
    179 
    180 template<typename T>
    181 void TestVCF::BPButterworthModel1<T>::setParams(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
    182 	_nFilters = 1 + (int)poles;
    183 	assert(_nFilters <= maxPoles / 2);
    184 
    185 	bandwidth = 0.1;
    186 	T span = std::max(1.0, bandwidth * 1000.0);
    187 	T wdl = std::max(1.0, cutoff - span);
    188 	T wdh = std::min((T)maxCutoff, cutoff + span);
    189 	T wal = std::tan(0.5 * (2.0 * M_PI * wdl * APP->engine->getSampleTime()));
    190 	T wah = std::tan(0.5 * (2.0 * M_PI * wdh * APP->engine->getSampleTime()));
    191 	T W = wah - wal;
    192 	T W2 = W * W;
    193 	T w02 = wah * wal;
    194 
    195 	resonance = 1.0 + resonance * 10.0 / (1.0 * (T)_nFilters); // FIXME
    196 	T iq = 1.0 / resonance;
    197 
    198 	T a0 = W2;
    199 	T a1 = W2;
    200 	T a2 = -W2;
    201 	T a3 = -W2;
    202 
    203 	T i2n = 1.0 / (2.0 * _nFilters);
    204 	for (int i = 0; i < _nFilters; ++i) {
    205 		T x = iq * 2.0 * std::cos((T)i * M_PI * i2n);
    206 		T w04etc = w02 * w02 + W * x * w02;
    207 		T b0 = 1.0 + W * x + W2 + 2.0 * w02 + w04etc;
    208 		T b1 = -3.0 - W * x + W2 + 2.0 * w02 + 3.0 * w04etc;
    209 		T b2 = 3.0 - W * x - W2 - 2.0 * w02 + 3.0 * w04etc;
    210 		T b3 = -1.0 + W * x - W2 - 2.0 * w02 + w04etc;
    211 		_filters[i].setParams(a0, a1, a2, a3, b0, b1, b2, b3);
    212 	}
    213 }
    214 
    215 template<typename T>
    216 float TestVCF::BPButterworthModel1<T>::next(float sample) {
    217 	for (int i = 0; i < _nFilters; ++i) {
    218 		sample = _filters[i].next(sample);
    219 	}
    220 	return sample;
    221 }
    222 
    223 template<typename T>
    224 void TestVCF::BandButterworthModel<T>::setParams(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
    225 	_nFilterPairs = 1 + (int)poles;
    226 	assert(_nFilterPairs <= maxPoles);
    227 
    228 	T span = std::max(1.0, (T)bandwidth / 2.0);
    229 	T wdl = std::max(1.0, cutoff - span);
    230 	T wdh = std::min((T)maxCutoff, cutoff + span);
    231 	T wal = std::tan(0.5 * (2.0 * M_PI * wdl * APP->engine->getSampleTime()));
    232 	T wah = std::tan(0.5 * (2.0 * M_PI * wdh * APP->engine->getSampleTime()));
    233 	T W = wah - wal;
    234 	T w02 = wah * wal;
    235 	const T iq = 1.0 / (0.5 * std::sqrt(2.0));
    236 
    237 	if (mode == BAND_REJECT_MODE) {
    238 		T a0 = 1.0 + w02;
    239 		T a1 = 2.0 * w02 - 2.0;
    240 		T a2 = a0;
    241 		T i2n = 1.0 / (4.0 * _nFilterPairs);
    242 		for (int i = 0; i < _nFilterPairs; i += 2) {
    243 			T baseX = iq * 2.0 * std::cos((T)i * M_PI * i2n);
    244 			{
    245 				T x = baseX;
    246 				x = 0.5 * (-x + std::pow(std::max(0.0, x * x - 4.0), 0.5));
    247 				T b0 = -x + W - x * w02;
    248 				T b1 = 2.0 * x - 2.0 * x * w02;
    249 				T b2 = -x - W - x * w02;
    250 				_filters[i].setParams(a0, a1, a2, b0, b1, b2);
    251 			}
    252 			{
    253 				T x = baseX;
    254 				x = 0.5 * (-x - std::pow(std::max(0.0, x * x - 4.0), 0.5));
    255 				T b0 = -x + W - x * w02;
    256 				T b1 = 2.0 * x - 2.0 * x * w02;
    257 				T b2 = -x - W - x * w02;
    258 				_filters[i + 1].setParams(a0, a1, a2, b0, b1, b2);
    259 			}
    260 		}
    261 	}
    262 	else {
    263 		T a0 = W;
    264 		T a1 = 0.0;
    265 		T a2 = -W;
    266 		T i2n = 1.0 / (4.0 * _nFilterPairs);
    267 		for (int i = 0; i < _nFilterPairs; i += 2) {
    268 			T baseX = iq * 2.0 * std::cos((T)i * M_PI * i2n);
    269 			{
    270 				T x = baseX;
    271 				x = 0.5 * (-x + std::pow(std::max(0.0, x * x - 4.0), 0.5));
    272 				T b0 = 1.0 - x * W + w02;
    273 				T b1 = -2.0 + 2.0 * w02;
    274 				T b2 = 1.0 + x * W + w02;
    275 				_filters[i].setParams(a0, a1, a2, b0, b1, b2);
    276 			}
    277 			{
    278 				T x = baseX;
    279 				x = 0.5 * (-x - std::pow(std::max(0.0, x * x - 4.0), 0.5));
    280 				T b0 = 1.0 - x * W + w02;
    281 				T b1 = -2.0 + 2.0 * w02;
    282 				T b2 = 1.0 + x * W + w02;
    283 				_filters[i + 1].setParams(a0, a1, a2, b0, b1, b2);
    284 			}
    285 		}
    286 	}
    287 }
    288 
    289 template<typename T>
    290 float TestVCF::BandButterworthModel<T>::next(float sample) {
    291 	for (int i = 0; i < _nFilterPairs; i += 2) {
    292 		sample = _filters[i].next(sample);
    293 		sample = _filters[i + 1].next(sample);
    294 	}
    295 	return sample;
    296 }
    297 
    298 template<typename T>
    299 void TestVCF::QuarticBandButterworthModel<T>::setParams(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
    300 	_nFilters = 1 + (int)poles;
    301 	assert(_nFilters <= maxPoles / 2);
    302 
    303 	T span = std::max(1.0l, (T)bandwidth / 2.0);
    304 	T wdl = std::max(1.0l, cutoff - span);
    305 	T wdh = std::min((T)maxCutoff, cutoff + span);
    306 	T wal = std::tan(0.5 * (2.0 * M_PI * wdl * APP->engine->getSampleTime()));
    307 	T wah = std::tan(0.5 * (2.0 * M_PI * wdh * APP->engine->getSampleTime()));
    308 	T W = wah - wal;
    309 	T W2 = W * W;
    310 	T w02 = wah * wal;
    311 	T w04 = w02 * w02;
    312 	const T iq = 1.0 / (0.5 * std::sqrt(2.0));
    313 
    314 	T a0 = W2;
    315 	T a1 = 0.0;
    316 	T a2 = -2.0 * W2;
    317 	T a3 = 0.0;
    318 	T a4 = W2;
    319 
    320 	T i2n = 1.0 / (4.0 * _nFilters);
    321 	for (int i = 0; i < _nFilters; ++i) {
    322 		T x = iq * 2.0 * std::cos((T)i * M_PI * i2n);
    323 		T xW = x * W;
    324 		T W2w02 = W2 + 2.0 * w02;
    325 		T xWw02 = xW * w02;
    326 		T b0 = +1.0  +  +1.0 * xW  +  +1.0 * W2w02  +  +1.0 * xWw02  +  +1.0 * w04;
    327 		T b1 = -4.0  +  -2.0 * xW  +  +0.0 * W2w02  +  +2.0 * xWw02  +  +4.0 * w04;
    328 		T b2 = +6.0  +  +0.0 * xW  +  -2.0 * W2w02  +  +0.0 * xWw02  +  +6.0 * w04;
    329 		T b3 = -4.0  +  +2.0 * xW  +  +0.0 * W2w02  +  -2.0 * xWw02  +  +4.0 * w04;
    330 		T b4 = +1.0  +  +1.0 * xW  +  +1.0 * W2w02  +  -1.0 * xWw02  +  +1.0 * w04;
    331 		_filters[i].setParams(a0, a1, a2, a3, a4, b0, b1, b2, b3, b4);
    332 	}
    333 }
    334 
    335 template<typename T>
    336 float TestVCF::QuarticBandButterworthModel<T>::next(float sample) {
    337 	for (int i = 0; i < _nFilters; ++i) {
    338 		sample = _filters[i].next(sample);
    339 	}
    340 	return sample;
    341 }
    342 
    343 void TestVCF::AllPassModel::setParams(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
    344 	float tq = std::tan(resonance * M_PI * APP->engine->getSampleTime());
    345 	float alpha = (tq - 1.0f) / (tq + 1.0f);
    346 	float beta = -std::cos(2.0f * M_PI * cutoff * APP->engine->getSampleTime());
    347 	float a1 = beta * (1.0f - alpha);
    348 	_filter.setParams(
    349 		-alpha,
    350 		a1,
    351 		1.0f,
    352 		1.0f,
    353 		a1,
    354 		-alpha
    355 	);
    356 }
    357 
    358 float TestVCF::AllPassModel::next(float sample) {
    359 	return _filter.next(sample);
    360 }
    361 
    362 
    363 template<typename T>
    364 void TestVCF::ChebyshevModel<T>::setParams(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
    365 	switch (mode) {
    366 		case BANDPASS_MODE:
    367 		case BAND_REJECT_MODE: {
    368 			setParamsBPBR(cutoff, bandwidth, resonance, mode, poles, topology);
    369 			break;
    370 		}
    371 		default: {
    372 			setParamsLPHP(cutoff, bandwidth, resonance, mode, poles, topology);
    373 		}
    374 	}
    375 }
    376 
    377 template<typename T>
    378 void TestVCF::ChebyshevModel<T>::setParamsLPHP(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
    379 	int nf = 1 + (int)poles;
    380 	assert(nf <= maxPoles / 2);
    381 	for (int i = _nFilters; i < nf; ++i) {
    382 		_filters[i].reset();
    383 	}
    384 	_nFilters = nf;
    385 	int nPoles = 2 * _nFilters;
    386 
    387 	cutoff = std::max(2.0f, cutoff); // FIXME: another way to avoid DC exploding near 0 hz?
    388 	T iq = (1.0 / std::sqrt(2.0)) - 0.65 * resonance; // / (1.0 + std::pow(0.6 * std::log((float)_nFilters), 2.0)); // FIXME
    389 	T wa = std::tan(0.5 * (2.0 * M_PI * cutoff * APP->engine->getSampleTime()));
    390 
    391 	T ripple = 3.0 + std::max(0.0, 12.0 * resonance);
    392 	if (topology >= 0.5) {
    393 		T e = ripple / 10.0;
    394 		e = std::pow(10.0, e);
    395 		e -= 1.0f;
    396 		e = std::sqrt(e);
    397 		T ef = std::asinh(1.0 / e) / (float)nPoles;
    398 
    399 		// _outGain = 1.0f / (std::pow(2.0, (T)(nPoles - 1)) * e);
    400 		_outGain = 1.0f / std::pow(2.0, (T)(nPoles - 1));
    401 
    402 		for (int i = 0, fi = 0; i < _nFilters; ++i) {
    403 			int k = i + 1;
    404 			T a = ((T)(2 * k + nPoles - 1)) * M_PI / (T)(2 * nPoles);
    405 			T re = -std::sinh(ef) * std::sin(a);
    406 			T im = std::cosh(ef) * std::cos(a);
    407 			T x = 2.0 * re;
    408 			T y = re * re + im * im;
    409 			// fi += polesToFilters(mode, _filters + fi, x, y, wa, 0.0, 0.0);
    410 			polesToFilterLPHP(mode, _filters[fi], x, y, wa);
    411 			++fi;
    412 		}
    413 	}
    414 	else {
    415 		_outGain = 1.0f;
    416 
    417 		for (int i = 0, fi = 0; i < _nFilters; ++i) {
    418 			int k = i + 1;
    419 			T a = ((T)(2 * k + nPoles - 1)) * M_PI / (T)(2 * nPoles);
    420 			T re = std::cos(a);
    421 			T im = std::sin(a);
    422 			T x = (i == _nFilters / 2 ? iq : 1.0) * 2.0 * re;
    423 			T y = re * re + im * im;
    424 			// fi += polesToFilters(mode, _filters + fi, x, y, wa, 0.0, 0.0);
    425 			polesToFilterLPHP(mode, _filters[fi], x, y, wa);
    426 			++fi;
    427 		}
    428 	}
    429 }
    430 
    431 template<typename T>
    432 void TestVCF::ChebyshevModel<T>::setParamsBPBR(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
    433 	int nf = 2 * (1 + (int)poles);
    434 	assert(nf <= maxPoles);
    435 	for (int i = _nFilters; i < nf; ++i) {
    436 		_filters[i].reset();
    437 	}
    438 	_nFilters = nf;
    439 	int nPoles = _nFilters;
    440 
    441 	T span = std::max(1.0, (T)bandwidth / 2.0);
    442 	T wdl = std::max(1.0, cutoff - span);
    443 	T wdh = std::min((T)maxCutoff, cutoff + span);
    444 	T wal = std::tan(0.5 * (2.0 * M_PI * wdl * APP->engine->getSampleTime()));
    445 	T wah = std::tan(0.5 * (2.0 * M_PI * wdh * APP->engine->getSampleTime()));
    446 	T W = wah - wal;
    447 	T w02 = wah * wal;
    448 	// const T iq = 1.0 / (0.5 * std::sqrt(2.0));
    449 	// const T iq = 0.707;
    450 
    451 	T ripple = 3.0; // + std::max(0.0, 12.0 * resonance);
    452 	if (topology >= 0.5) {
    453 		T e = ripple / 10.0;
    454 		e = std::pow(10.0, e);
    455 		e -= 1.0f;
    456 		e = std::sqrt(e);
    457 		T ef = std::asinh(1.0 / e) / (float)nPoles;
    458 
    459 		// _outGain = 1.0f / (std::pow(2.0, (T)(nPoles - 1)) * e);
    460 		_outGain = 1.0f / std::pow(2.0, (T)(nPoles - 1));
    461 
    462 		for (int i = 0, fi = 0, n = _nFilters / 2; i < n; ++i) {
    463 			int k = i + 1;
    464 			T a = ((T)(2 * k + nPoles - 1)) * M_PI / (T)(2 * nPoles);
    465 			T re = -std::sinh(ef) * std::sin(a);
    466 			T im = std::cosh(ef) * std::cos(a);
    467 			// T x = 2.0 * re;
    468 			// T y = re * re + im * im;
    469 			// fi += polesToFilters(mode, _filters + fi, x, y, 0.0, W, w02);
    470 			polesToFiltersBPBR(mode, _filters[fi], _filters[fi + 1], re, im, W, w02);
    471 			fi += 2;
    472 		}
    473 	}
    474 	else {
    475 		_outGain = 1.0f;
    476 
    477 		for (int i = 0, fi = 0, n = _nFilters / 2; i < n; ++i) {
    478 			int k = i + 1;
    479 			T a = ((T)(2 * k + nPoles - 1)) * M_PI / (T)(2 * nPoles);
    480 			T re = std::cos(a);
    481 			T im = std::sin(a);
    482 			// T x = iq * 2.0 * re;
    483 			// T y = re * re + im * im;
    484 			// fi += polesToFilters(mode, _filters + fi, x, y, 0.0, W, w02);
    485 			polesToFiltersBPBR(mode, _filters[fi], _filters[fi + 1], re, im, W, w02);
    486 			fi += 2;
    487 		}
    488 	}
    489 }
    490 
    491 template<typename T>
    492 int TestVCF::ChebyshevModel<T>::polesToFilters(Mode mode, BiquadFilter<T>* fs, T x, T y, T wa, T W, T w02) {
    493 	switch (mode) {
    494 		case BANDPASS_MODE: {
    495 			// y = 1.0;
    496 			x = -x;
    497 			T a0 = W;
    498 			T a1 = 0.0;
    499 			T a2 = -W;
    500 			// x = 0.5 * (-x + std::pow(std::max(0.0, x * x - 4.0), 0.5));
    501 			T rt = std::sqrt(std::max(0.0, x * x - 4.0 * y));
    502 			{
    503 				T xx = 0.5 * (-x + rt);
    504 				T b0 = 1.0 - xx * W + w02;
    505 				T b1 = -2.0 + 2.0 * w02;
    506 				T b2 = 1.0 + xx * W + w02;
    507 				fs[0].setParams(a0, a1, a2, b0, b1, b2);
    508 			}
    509 			{
    510 				T xx = 0.5 * (-x - rt);
    511 				T b0 = 1.0 - xx * W + w02;
    512 				T b1 = -2.0 + 2.0 * w02;
    513 				T b2 = 1.0 + xx * W + w02;
    514 				fs[1].setParams(a0, a1, a2, b0, b1, b2);
    515 			}
    516 			return 2;
    517 		}
    518 
    519 		case BAND_REJECT_MODE: {
    520 			x = -x;
    521 			T a0 = 1.0 + w02;
    522 			T a1 = 2.0 * w02 - 2.0;
    523 			T a2 = a0;
    524 			T rt = std::sqrt(std::max(0.0, x * x - 4.0 * y));
    525 			{
    526 				T xx = 0.5 * (-x + rt);
    527 				T b0 = -xx + W - xx * w02;
    528 				T b1 = 2.0 * xx - 2.0 * xx * w02;
    529 				T b2 = -xx - W - xx * w02;
    530 				fs[0].setParams(a0, a1, a2, b0, b1, b2);
    531 			}
    532 			{
    533 				T xx = 0.5 * (-x + rt);
    534 				T b0 = -xx + W - xx * w02;
    535 				T b1 = 2.0 * xx - 2.0 * xx * w02;
    536 				T b2 = -xx - W - xx * w02;
    537 				fs[1].setParams(a0, a1, a2, b0, b1, b2);
    538 			}
    539 			return 2;
    540 		}
    541 
    542 		case HIGHPASS_MODE: {
    543 			T wa2 = wa * wa;
    544 			T a0 = 1.0;
    545 			T a1 = -2.0f;
    546 			T a2 = 1.0;
    547 			T b0 = wa2 - x * wa + y;
    548 			T b1 = 2.0 * wa2 - 2.0 * y;
    549 			T b2 = wa2 + x * wa + y;
    550 			fs[0].setParams(a0, a1, a2, b0, b1, b2);
    551 			return 1;
    552 		}
    553 
    554 		default: {
    555 			T wa2 = wa * wa;
    556 			T a0 = wa2;
    557 			T a1 = 2.0f * wa2;
    558 			T a2 = wa2;
    559 			T b0 = 1.0 - x * wa + y * wa2;
    560 			T b1 = -2.0 + 2.0 * y * wa2;
    561 			T b2 = 1.0 + x * wa + y * wa2;
    562 			fs[0].setParams(a0, a1, a2, b0, b1, b2);
    563 			return 1;
    564 		}
    565 	}
    566 }
    567 
    568 template<typename T>
    569 void TestVCF::ChebyshevModel<T>::polesToFilterLPHP(Mode mode, BiquadFilter<T>& f, T x, T y, T wa) {
    570 	switch (mode) {
    571 		case LOWPASS_MODE: {
    572 			T wa2 = wa * wa;
    573 			T a0 = wa2;
    574 			T a1 = 2.0f * wa2;
    575 			T a2 = wa2;
    576 			T b0 = 1.0 - x * wa + y * wa2;
    577 			T b1 = -2.0 + 2.0 * y * wa2;
    578 			T b2 = 1.0 + x * wa + y * wa2;
    579 			f.setParams(a0, a1, a2, b0, b1, b2);
    580 			break;
    581 		}
    582 
    583 		case HIGHPASS_MODE: {
    584 			T wa2 = wa * wa;
    585 			T a0 = 1.0;
    586 			T a1 = -2.0f;
    587 			T a2 = 1.0;
    588 			T b0 = wa2 - x * wa + y;
    589 			T b1 = 2.0 * wa2 - 2.0 * y;
    590 			T b2 = wa2 + x * wa + y;
    591 			f.setParams(a0, a1, a2, b0, b1, b2);
    592 			break;
    593 		}
    594 
    595 		default: {
    596 			assert(false);
    597 		}
    598 	}
    599 }
    600 
    601 template<typename T>
    602 void TestVCF::ChebyshevModel<T>::polesToFiltersBPBR(Mode mode, BiquadFilter<T>& f0, BiquadFilter<T>& f1, T re, T im, T W, T w02) {
    603 	typedef std::complex<T> TC;
    604 	switch (mode) {
    605 		case BANDPASS_MODE: {
    606 			// TC pole(-re, im);
    607 			// TC poleW2 = pole * W * 0.5;
    608 			// T R = std::abs(poleW2);
    609 			// T r = std::arg(poleW2);
    610 			// TC X = pole * pole;
    611 			// X *= W * W;
    612 			// X -= 4.0 * w02;
    613 			// X = std::sqrt(X);
    614 			// X *= 0.5;
    615 			// T Q = std::abs(X);
    616 			// T q = std::arg(X);
    617 			// T f = 2.0 * Q * std::cos(q);
    618 			// T F1a = 2.0 * R * std::cos(r);
    619 			// T F1b = F1a + f;
    620 			// F1a -= f;
    621 			// f = 2.0 * R * Q * std::cos(r - q);
    622 			// T F2a = R * R + Q * Q;
    623 			// T F2b = F2a + f;
    624 			// F2a -= f;
    625 			TC P(-re, im);
    626 			TC PC = std::conj(P);
    627 			TC X = P * P;
    628 			X *= W * W;
    629 			X -= 4.0 * w02;
    630 			X = std::sqrt(X);
    631 			TC XC = std::conj(X);
    632 			TC Y1 = (X - W * P) * 0.5;
    633 			TC Y1C = (XC - W * PC) * 0.5;
    634 			TC Y2 = (-X - W * P) * 0.5;
    635 			TC Y2C = (-XC - W * PC) * 0.5;
    636 			TC cF1a = -(Y1 + Y1C);
    637 			TC cF2a = Y1 * Y1C;
    638 			TC cF1b = -(Y2 + Y2C);
    639 			TC cF2b = Y2 * Y2C;
    640 			T F1a = std::real(cF1a);
    641 			T F1b = std::real(cF1b);
    642 			T F2a = std::real(cF2a);
    643 			T F2b = std::real(cF2b);
    644 
    645 			T a0 = W;
    646 			T a1 = 0.0;
    647 			T a2 = -W;
    648 			{
    649 				T b0 = 1.0 + F1a + F2a;
    650 				T b1 = -2.0 + 2.0 * F2a;
    651 				T b2 = 1.0 - F1a + F2a;
    652 				f0.setParams(a0, a1, a2, b0, b1, b2);
    653 			}
    654 			{
    655 				T b0 = 1.0 + F1b + F2b;
    656 				T b1 = -2.0 + 2.0 * F2b;
    657 				T b2 = 1.0 - F1b + F2b;
    658 				f1.setParams(a0, a1, a2, b0, b1, b2);
    659 			}
    660 			break;
    661 		}
    662 
    663 		case BAND_REJECT_MODE: {
    664 			TC P(-re, im);
    665 			TC PC = std::conj(P);
    666 			T R = std::abs(P);
    667 			TC X = P * P;
    668 			X *= -4.0 * w02;
    669 			X += W * W;
    670 			X = std::sqrt(X);
    671 			TC XC = std::conj(X);
    672 			TC Y1 = (X - W) / (2.0 * P);
    673 			TC Y1C = (XC - W) / (2.0 * PC);
    674 			TC Y2 = (-X - W) / (2.0 * P);
    675 			TC Y2C = (-XC - W) / (2.0 * PC);
    676 			TC cF1a = -R * (Y1 + Y1C);
    677 			TC cF2a = R * Y1 * Y1C;
    678 			TC cF1b = -R * (Y2 + Y2C);
    679 			TC cF2b = R * Y2 * Y2C;
    680 			T F1a = std::real(cF1a);
    681 			T F1b = std::real(cF1b);
    682 			T F2a = std::real(cF2a);
    683 			T F2b = std::real(cF2b);
    684 
    685 			T a0 = 1.0 + w02;
    686 			T a1 = -2.0 + 2.0 * w02;
    687 			T a2 = 1.0 + w02;
    688 			{
    689 				T b0 = R + F1a + F2a;
    690 				T b1 = -2.0 * R + 2.0 * F2a;
    691 				T b2 = R - F1a + F2a;
    692 				f0.setParams(a0, a1, a2, b0, b1, b2);
    693 			}
    694 			{
    695 				T b0 = R + F1b + F2b;
    696 				T b1 = -2.0 * R + 2.0 * F2b;
    697 				T b2 = R - F1b + F2b;
    698 				f1.setParams(a0, a1, a2, b0, b1, b2);
    699 			}
    700 			break;
    701 		}
    702 
    703 		default: {
    704 			assert(false);
    705 		}
    706 	}
    707 }
    708 
    709 template<typename T>
    710 float TestVCF::ChebyshevModel<T>::next(float sample) {
    711 	for (int i = 0; i < _nFilters; ++i) {
    712 		sample = _filters[i].next(sample);
    713 	}
    714 	return _outGain * sample;
    715 }
    716 
    717 template<typename T>
    718 void TestVCF::TwoPoleResonatorModel<T>::setParams(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
    719 	// T wa = std::tan(0.5 * (2.0 * M_PI * cutoff * APP->engine->getSampleTime()));
    720 	T wa = 2.0 * M_PI * cutoff * APP->engine->getSampleTime();
    721 	T r = std::max(0.1f, 1.0f * resonance);
    722 	_filter.setParams(1.0, 0.0, -1.0 * r, 1.0, -2.0 * r * std::cos(wa), r * r);
    723 	_outGain = 1.0; // * (1.0 - r * r);
    724 }
    725 
    726 template<typename T>
    727 float TestVCF::TwoPoleResonatorModel<T>::next(float sample) {
    728 	return _filter.next(sample) * _outGain;
    729 }
    730 
    731 template<typename M, int FACTOR>
    732 void TestVCF::OversamplingModel<M, FACTOR>::setParams(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
    733 	_model.setParams(cutoff / (float)FACTOR, bandwidth / (float)FACTOR, resonance, mode, poles, topology);
    734 	_interpolator.setParams(APP->engine->getSampleRate(), FACTOR);
    735 	_decimator.setParams(APP->engine->getSampleRate(), FACTOR);
    736 }
    737 
    738 template<typename M, int FACTOR>
    739 float TestVCF::OversamplingModel<M, FACTOR>::next(float sample) {
    740 	float buf[FACTOR];
    741 	_interpolator.next(sample, buf);
    742 	for (int i = 0; i < FACTOR; ++i) {
    743 		buf[i] = _model.next(buf[i]);
    744 	}
    745 	return _decimator.next(buf);
    746 }
    747 
    748 template<typename M>
    749 void TestVCF::FeedbackModel<M>::setParams(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
    750 	_q = resonance;
    751 	_model.setParams(cutoff, bandwidth, 0.0f, mode, poles, topology);
    752 }
    753 
    754 template<typename M>
    755 float TestVCF::FeedbackModel<M>::next(float sample) {
    756 	return _last = _model.next(sample - _q * _last);
    757 }
    758 
    759 template<typename M, typename T>
    760 void TestVCF::AddResonanceModel<M, T>::setParams(float cutoff, float bandwidth, float resonance, Mode mode, Poles poles, float topology) {
    761 	_model.setParams(cutoff, bandwidth, 0.0f, mode, poles, topology);
    762 	_resonator.setParams(cutoff, bandwidth, resonance * (1.0 / std::sqrt(2.0)), mode, poles, topology);
    763 }
    764 
    765 template<typename M, typename T>
    766 float TestVCF::AddResonanceModel<M, T>::next(float sample) {
    767 	return _model.next(_resonator.next(sample));
    768 }
    769 
    770 void TestVCF::modulate() {
    771 	float cutoff = maxCutoff * clamp(params[CUTOFF_PARAM].getValue(), 0.0f, 1.0f);
    772 	float q = clamp(params[Q_PARAM].getValue(), 0.0f, 1.0f);
    773 	float bandwidth = q * 10000.0f;
    774 	_mode = (Mode)clamp((int)params[MODE_PARAM].getValue(), 0, 3);
    775 	_poles = (Poles)clamp((int)params[POLES_PARAM].getValue(), 0, 5);
    776 	float topology = clamp(params[TOPOLOGY_PARAM].getValue(), 0.0f, 1.0f);
    777 
    778 	if (_model) {
    779 		_model->setParams(
    780 			cutoff,
    781 			bandwidth,
    782 			q,
    783 			_mode,
    784 			_poles,
    785 			topology
    786 		);
    787 	}
    788 	if (_model2) {
    789 		_model2->setParams(
    790 			cutoff,
    791 			bandwidth,
    792 			q,
    793 			_mode,
    794 			_poles,
    795 			topology
    796 		);
    797 	}
    798 
    799 	_amplifier.setLevel(Amplifier::maxDecibels * clamp(params[DRIVE_PARAM].getValue(), 0.0f, 1.0f));
    800 }
    801 
    802 void TestVCF::processAll(const ProcessArgs& args) {
    803 	lights[LOWPASS_LIGHT].value = _mode == LOWPASS_MODE;
    804 	lights[HIGHPASS_LIGHT].value = _mode == HIGHPASS_MODE;
    805 	lights[BANDPASS_LIGHT].value = _mode == BANDPASS_MODE;
    806 	lights[BAND_REJECT_LIGHT].value = _mode == BAND_REJECT_MODE;
    807 
    808 	lights[POLES_2_LIGHT].value = _poles == POLES_2;
    809 	lights[POLES_4_LIGHT].value = _poles == POLES_4;
    810 	lights[POLES_6_LIGHT].value = _poles == POLES_6;
    811 	lights[POLES_8_LIGHT].value = _poles == POLES_8;
    812 	lights[POLES_10_LIGHT].value = _poles == POLES_10;
    813 	lights[POLES_12_LIGHT].value = _poles == POLES_12;
    814 
    815 	float in = inputs[IN_INPUT].getVoltage();
    816 	in = _amplifier.next(in);
    817 	if (_model) {
    818 		float out = _model->next(in);
    819 		out = _saturator.next(out);
    820 		outputs[OUT_OUTPUT].setVoltage(out);
    821 	}
    822 	if (_model2) {
    823 		float out = _model2->next(in);
    824 		out = _saturator2.next(out);
    825 		outputs[OUT_B_OUTPUT].setVoltage(out);
    826 	}
    827 }
    828 
    829 struct TestVCFWidget : BGModuleWidget {
    830 	static constexpr int hp = 12;
    831 
    832 	TestVCFWidget(TestVCF* module) {
    833 		setModule(module);
    834 		box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
    835 		setPanel(box.size, "TestVCF");
    836 		createScrews();
    837 
    838 		// generated by svg_widgets.rb
    839 		auto cutoffParamPosition = Vec(40.0, 50.0);
    840 		auto qParamPosition = Vec(40.0, 120.0);
    841 		auto driveParamPosition = Vec(40.0, 190.0);
    842 		auto topologyParamPosition = Vec(40.0, 260.0);
    843 		auto modeParamPosition = Vec(145.0, 77.0);
    844 		auto polesParamPosition = Vec(145.0, 156.0);
    845 
    846 		auto inInputPosition = Vec(47.5, 318.0);
    847 
    848 		auto outOutputPosition = Vec(76.5, 318.0);
    849 		auto outBOutputPosition = Vec(105.5, 318.0);
    850 
    851 		auto lowpassLightPosition = Vec(130.0, 58.0);
    852 		auto highpassLightPosition = Vec(151.0, 58.0);
    853 		auto bandpassLightPosition = Vec(130.0, 68.0);
    854 		auto bandRejectLightPosition = Vec(151.0, 68.0);
    855 		auto poles2LightPosition = Vec(130.0, 127.0);
    856 		auto poles4LightPosition = Vec(151.0, 127.0);
    857 		auto poles6LightPosition = Vec(130.0, 137.0);
    858 		auto poles8LightPosition = Vec(151.0, 137.0);
    859 		auto poles10LightPosition = Vec(130.0, 147.0);
    860 		auto poles12LightPosition = Vec(151.0, 147.0);
    861 		// end generated by svg_widgets.rb
    862 
    863 		addParam(createParam<Knob38>(cutoffParamPosition, module, TestVCF::CUTOFF_PARAM));
    864 		addParam(createParam<Knob38>(qParamPosition, module, TestVCF::Q_PARAM));
    865 		addParam(createParam<Knob38>(driveParamPosition, module, TestVCF::DRIVE_PARAM));
    866 		addParam(createParam<Knob38>(topologyParamPosition, module, TestVCF::TOPOLOGY_PARAM));
    867 		addParam(createParam<StatefulButton9>(modeParamPosition, module, TestVCF::MODE_PARAM));
    868 		addParam(createParam<StatefulButton9>(polesParamPosition, module, TestVCF::POLES_PARAM));
    869 
    870 		addInput(createInput<Port24>(inInputPosition, module, TestVCF::IN_INPUT));
    871 
    872 		addOutput(createOutput<Port24>(outOutputPosition, module, TestVCF::OUT_OUTPUT));
    873 		addOutput(createOutput<Port24>(outBOutputPosition, module, TestVCF::OUT_B_OUTPUT));
    874 
    875 		addChild(createLight<BGSmallLight<GreenLight>>(lowpassLightPosition, module, TestVCF::LOWPASS_LIGHT));
    876 		addChild(createLight<BGSmallLight<GreenLight>>(highpassLightPosition, module, TestVCF::HIGHPASS_LIGHT));
    877 		addChild(createLight<BGSmallLight<GreenLight>>(bandpassLightPosition, module, TestVCF::BANDPASS_LIGHT));
    878 		addChild(createLight<BGSmallLight<GreenLight>>(bandRejectLightPosition, module, TestVCF::BAND_REJECT_LIGHT));
    879 		addChild(createLight<BGSmallLight<GreenLight>>(poles2LightPosition, module, TestVCF::POLES_2_LIGHT));
    880 		addChild(createLight<BGSmallLight<GreenLight>>(poles4LightPosition, module, TestVCF::POLES_4_LIGHT));
    881 		addChild(createLight<BGSmallLight<GreenLight>>(poles6LightPosition, module, TestVCF::POLES_6_LIGHT));
    882 		addChild(createLight<BGSmallLight<GreenLight>>(poles8LightPosition, module, TestVCF::POLES_8_LIGHT));
    883 		addChild(createLight<BGSmallLight<GreenLight>>(poles10LightPosition, module, TestVCF::POLES_10_LIGHT));
    884 		addChild(createLight<BGSmallLight<GreenLight>>(poles12LightPosition, module, TestVCF::POLES_12_LIGHT));
    885 	}
    886 };
    887 
    888 Model* modelTestVCF = createModel<TestVCF, TestVCFWidget>("Bogaudio-TestVCF", "TestVCF", "Test VCF");