+ Monosynth: Osc Mix modulation destination, use smoothing for xfade value, destinati...
[calf.git] / src / calf / audio_fx.h
blobd4efde7c2debcd1b39f37706e482c8ab506fb9ca
1 /* Calf DSP Library
2 * Reusable audio effect classes.
4 * Copyright (C) 2001-2007 Krzysztof Foltman
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
21 #ifndef __CALF_AUDIOFX_H
22 #define __CALF_AUDIOFX_H
24 #include <complex>
25 #include <iostream>
26 #include <calf/biquad.h>
27 #include <calf/onepole.h>
28 #include "primitives.h"
29 #include "delay.h"
30 #include "fixed_point.h"
31 #include "inertia.h"
33 namespace dsp {
34 #if 0
35 }; to keep editor happy
36 #endif
38 /**
39 * Audio effect base class. Not really useful until it gets more developed.
41 class audio_effect
43 public:
44 virtual void setup(int sample_rate)=0;
45 virtual ~audio_effect() {}
48 class modulation_effect: public audio_effect
50 protected:
51 int sample_rate;
52 float rate, wet, dry, odsr;
53 gain_smoothing gs_wet, gs_dry;
54 public:
55 fixed_point<unsigned int, 20> phase, dphase;
56 float get_rate() {
57 return rate;
59 void set_rate(float rate) {
60 this->rate = rate;
61 dphase = rate/sample_rate*4096;
63 float get_wet() {
64 return wet;
66 void set_wet(float wet) {
67 this->wet = wet;
68 gs_wet.set_inertia(wet);
70 float get_dry() {
71 return dry;
73 void set_dry(float dry) {
74 this->dry = dry;
75 gs_dry.set_inertia(dry);
77 void reset_phase(float req_phase)
79 phase = req_phase * 4096.0;
81 void inc_phase(float req_phase)
83 phase += fixed_point<unsigned int, 20>(req_phase * 4096.0);
85 void setup(int sample_rate)
87 this->sample_rate = sample_rate;
88 this->odsr = 1.0 / sample_rate;
89 phase = 0;
90 set_rate(get_rate());
94 /**
95 * A monophonic phaser. If you want stereo, combine two :)
96 * Also, gave up on using template args for signal type.
98 template<int MaxStages>
99 class simple_phaser: public modulation_effect
101 protected:
102 float base_frq, mod_depth, fb;
103 float state;
104 int cnt, stages;
105 dsp::onepole<float, float> stage1;
106 float x1[MaxStages], y1[MaxStages];
107 public:
108 simple_phaser()
110 set_base_frq(1000);
111 set_mod_depth(1000);
112 set_fb(0);
113 state = 0;
114 cnt = 0;
115 stages = 0;
116 set_stages(6);
118 float get_base_frq() {
119 return base_frq;
121 void set_base_frq(float _base_frq) {
122 base_frq = _base_frq;
124 int get_stages() {
125 return stages;
127 void set_stages(int _stages) {
128 if (_stages > stages)
130 for (int i = stages; i < _stages; i++)
132 x1[i] = x1[stages-1];
133 y1[i] = y1[stages-1];
136 stages = _stages;
138 float get_mod_depth() {
139 return mod_depth;
141 void set_mod_depth(float _mod_depth) {
142 mod_depth = _mod_depth;
144 float get_fb() {
145 return fb;
147 void set_fb(float fb) {
148 this->fb = fb;
150 virtual void setup(int sample_rate) {
151 modulation_effect::setup(sample_rate);
152 reset();
154 void reset()
156 cnt = 0;
157 state = 0;
158 phase.set(0);
159 for (int i = 0; i < MaxStages; i++)
160 x1[i] = y1[i] = 0;
161 control_step();
163 inline void control_step()
165 cnt = 0;
166 int v = phase.get() + 0x40000000;
167 int sign = v >> 31;
168 v ^= sign;
169 // triangle wave, range from 0 to INT_MAX
170 double vf = (double)((v >> 16) * (1.0 / 16384.0) - 1);
172 float freq = base_frq * pow(2.0, vf * mod_depth / 1200.0);
173 freq = dsp::clip<float>(freq, 10.0, 0.49 * sample_rate);
174 stage1.set_ap_w(freq * (M_PI / 2.0) * odsr);
175 phase += dphase * 32;
176 for (int i = 0; i < stages; i++)
178 dsp::sanitize(x1[i]);
179 dsp::sanitize(y1[i]);
181 dsp::sanitize(state);
183 void process(float *buf_out, float *buf_in, int nsamples) {
184 for (int i=0; i<nsamples; i++) {
185 cnt++;
186 if (cnt == 32)
187 control_step();
188 float in = *buf_in++;
189 float fd = in + state * fb;
190 for (int j = 0; j < stages; j++)
191 fd = stage1.process_ap(fd, x1[j], y1[j]);
192 state = fd;
194 float sdry = in * gs_dry.get();
195 float swet = fd * gs_wet.get();
196 *buf_out++ = sdry + swet;
199 float freq_gain(float freq, float sr)
201 typedef std::complex<double> cfloat;
202 freq *= 2.0 * M_PI / sr;
203 cfloat z = 1.0 / exp(cfloat(0.0, freq)); // z^-1
205 cfloat p = cfloat(1.0);
206 cfloat stg = stage1.h_z(z);
208 for (int i = 0; i < stages; i++)
209 p = p * stg;
211 p = p / (cfloat(1.0) - cfloat(fb) * p);
212 return std::abs(cfloat(gs_dry.get_last()) + cfloat(gs_wet.get_last()) * p);
217 * Base class for chorus and flanger. Wouldn't be needed if it wasn't
218 * for odd behaviour of GCC when deriving templates from template
219 * base classes (not seeing fields from base classes!).
221 class chorus_base: public modulation_effect
223 protected:
224 int min_delay_samples, mod_depth_samples;
225 float min_delay, mod_depth;
226 sine_table<int, 4096, 65536> sine;
227 public:
228 float get_min_delay() {
229 return min_delay;
231 void set_min_delay(float min_delay) {
232 this->min_delay = min_delay;
233 this->min_delay_samples = (int)(min_delay * 65536.0 * sample_rate);
235 float get_mod_depth() {
236 return mod_depth;
238 void set_mod_depth(float mod_depth) {
239 this->mod_depth = mod_depth;
240 // 128 because it's then multiplied by (hopefully) a value of 32768..-32767
241 this->mod_depth_samples = (int)(mod_depth * 32.0 * sample_rate);
246 * Single-tap chorus without feedback.
247 * Perhaps MaxDelay should be a bit longer!
249 template<class T, int MaxDelay=512>
250 class simple_chorus: public chorus_base
252 protected:
253 simple_delay<MaxDelay,T> delay;
254 public:
255 simple_chorus() {
256 rate = 0.63f;
257 dry = 0.5f;
258 wet = 0.5f;
259 min_delay = 0.005f;
260 mod_depth = 0.0025f;
261 setup(44100);
263 void reset() {
264 delay.reset();
266 virtual void setup(int sample_rate) {
267 modulation_effect::setup(sample_rate);
268 delay.reset();
269 set_min_delay(get_min_delay());
270 set_mod_depth(get_mod_depth());
272 template<class OutIter, class InIter>
273 void process(OutIter buf_out, InIter buf_in, int nsamples) {
274 int mds = min_delay_samples + mod_depth_samples * 1024 + 2*65536;
275 int mdepth = mod_depth_samples;
276 for (int i=0; i<nsamples; i++) {
277 phase += dphase;
278 unsigned int ipart = phase.ipart();
280 float in = *buf_in++;
281 int lfo = phase.lerp_by_fract_int<int, 14, int>(sine.data[ipart], sine.data[ipart+1]);
282 int v = mds + (mdepth * lfo >> 6);
283 // if (!(i & 7)) printf("%d\n", v);
284 int ifv = v >> 16;
285 delay.put(in);
286 T fd; // signal from delay's output
287 delay.get_interp(fd, ifv, (v & 0xFFFF)*(1.0/65536.0));
288 T sdry = in * gs_dry.get();
289 T swet = fd * gs_wet.get();
290 *buf_out++ = sdry + swet;
296 * Single-tap flanger (chorus plus feedback).
298 template<class T, int MaxDelay=1024>
299 class simple_flanger: public chorus_base
301 protected:
302 simple_delay<MaxDelay,T> delay;
303 float fb;
304 int last_delay_pos, last_actual_delay_pos;
305 int ramp_pos, ramp_delay_pos;
306 public:
307 simple_flanger()
308 : fb(0) {}
309 void reset() {
310 delay.reset();
311 last_delay_pos = 0;
312 ramp_pos = 1024;
314 virtual void setup(int sample_rate) {
315 this->sample_rate = sample_rate;
316 this->odsr = 1.0 / sample_rate;
317 delay.reset();
318 phase = 0;
319 set_rate(get_rate());
320 set_min_delay(get_min_delay());
322 float get_fb() {
323 return fb;
325 void set_fb(float fb) {
326 this->fb = fb;
328 template<class OutIter, class InIter>
329 void process(OutIter buf_out, InIter buf_in, int nsamples) {
330 if (!nsamples)
331 return;
332 int mds = this->min_delay_samples + this->mod_depth_samples * 1024 + 2 * 65536;
333 int mdepth = this->mod_depth_samples;
334 int delay_pos;
335 unsigned int ipart = this->phase.ipart();
336 int lfo = phase.lerp_by_fract_int<int, 14, int>(this->sine.data[ipart], this->sine.data[ipart+1]);
337 delay_pos = mds + (mdepth * lfo >> 6);
339 if (delay_pos != last_delay_pos || ramp_pos < 1024)
341 if (delay_pos != last_delay_pos) {
342 // we need to ramp from what the delay tap length actually was,
343 // not from old (ramp_delay_pos) or desired (delay_pos) tap length
344 ramp_delay_pos = last_actual_delay_pos;
345 ramp_pos = 0;
348 int64_t dp = 0;
349 for (int i=0; i<nsamples; i++) {
350 float in = *buf_in++;
351 T fd; // signal from delay's output
352 dp = (((int64_t)ramp_delay_pos) * (1024 - ramp_pos) + ((int64_t)delay_pos) * ramp_pos) >> 10;
353 ramp_pos++;
354 if (ramp_pos > 1024) ramp_pos = 1024;
355 this->delay.get_interp(fd, dp >> 16, (dp & 0xFFFF)*(1.0/65536.0));
356 sanitize(fd);
357 T sdry = in * this->dry;
358 T swet = fd * this->wet;
359 *buf_out++ = sdry + swet;
360 this->delay.put(in+fb*fd);
362 this->phase += this->dphase;
363 ipart = this->phase.ipart();
364 lfo = phase.lerp_by_fract_int<int, 14, int>(this->sine.data[ipart], this->sine.data[ipart+1]);
365 delay_pos = mds + (mdepth * lfo >> 6);
367 last_actual_delay_pos = dp;
369 else {
370 for (int i=0; i<nsamples; i++) {
371 float in = *buf_in++;
372 T fd; // signal from delay's output
373 this->delay.get_interp(fd, delay_pos >> 16, (delay_pos & 0xFFFF)*(1.0/65536.0));
374 sanitize(fd);
375 T sdry = in * this->gs_dry.get();
376 T swet = fd * this->gs_wet.get();
377 *buf_out++ = sdry + swet;
378 this->delay.put(in+fb*fd);
380 this->phase += this->dphase;
381 ipart = this->phase.ipart();
382 lfo = phase.lerp_by_fract_int<int, 14, int>(this->sine.data[ipart], this->sine.data[ipart+1]);
383 delay_pos = mds + (mdepth * lfo >> 6);
385 last_actual_delay_pos = delay_pos;
387 last_delay_pos = delay_pos;
389 float freq_gain(float freq, float sr)
391 typedef std::complex<double> cfloat;
392 freq *= 2.0 * M_PI / sr;
393 cfloat z = 1.0 / exp(cfloat(0.0, freq)); // z^-1
395 float ldp = last_delay_pos / 65536.0;
396 float fldp = floor(ldp);
397 cfloat zn = std::pow(z, fldp); // z^-N
398 cfloat zn1 = zn * z; // z^-(N+1)
399 // simulate a lerped comb filter - H(z) = 1 / (1 + fb * (lerp(z^-N, z^-(N+1), fracpos))), N = int(pos), fracpos = pos - int(pos)
400 cfloat delayed = zn + (zn1 - zn) * cfloat(ldp - fldp);
401 cfloat h = cfloat(delayed) / (cfloat(1.0) - cfloat(fb) * delayed);
402 // mix with dry signal
403 float v = std::abs(cfloat(gs_dry.get_last()) + cfloat(gs_wet.get_last()) * h);
404 return v;
409 * A classic allpass loop reverb with modulated allpass filter.
410 * Just started implementing it, so there is no control over many
411 * parameters.
413 template<class T>
414 class reverb: public audio_effect
416 simple_delay<2048, T> apL1, apL2, apL3, apL4, apL5, apL6;
417 simple_delay<2048, T> apR1, apR2, apR3, apR4, apR5, apR6;
418 fixed_point<unsigned int, 25> phase, dphase;
419 sine_table<int, 128, 10000> sine;
420 onepole<T> lp_left, lp_right;
421 T old_left, old_right;
422 int type;
423 float time, fb, cutoff, diffusion;
424 int tl[6], tr[6];
425 float ldec[6], rdec[6];
427 int sr;
428 public:
429 reverb()
431 phase = 0.0;
432 time = 1.0;
433 cutoff = 9000;
434 type = 2;
435 diffusion = 1.f;
436 setup(44100);
438 virtual void setup(int sample_rate) {
439 sr = sample_rate;
440 set_time(time);
441 set_cutoff(cutoff);
442 phase = 0.0;
443 dphase = 0.5*128/sr;
444 update_times();
446 void update_times()
448 switch(type)
450 case 0:
451 tl[0] = 397 << 16, tr[0] = 383 << 16;
452 tl[1] = 457 << 16, tr[1] = 429 << 16;
453 tl[2] = 549 << 16, tr[2] = 631 << 16;
454 tl[3] = 649 << 16, tr[3] = 756 << 16;
455 tl[4] = 773 << 16, tr[4] = 803 << 16;
456 tl[5] = 877 << 16, tr[5] = 901 << 16;
457 break;
458 case 1:
459 tl[0] = 697 << 16, tr[0] = 783 << 16;
460 tl[1] = 957 << 16, tr[1] = 929 << 16;
461 tl[2] = 649 << 16, tr[2] = 531 << 16;
462 tl[3] = 1049 << 16, tr[3] = 1177 << 16;
463 tl[4] = 473 << 16, tr[4] = 501 << 16;
464 tl[5] = 587 << 16, tr[5] = 681 << 16;
465 break;
466 case 2:
467 default:
468 tl[0] = 697 << 16, tr[0] = 783 << 16;
469 tl[1] = 957 << 16, tr[1] = 929 << 16;
470 tl[2] = 649 << 16, tr[2] = 531 << 16;
471 tl[3] = 1249 << 16, tr[3] = 1377 << 16;
472 tl[4] = 1573 << 16, tr[4] = 1671 << 16;
473 tl[5] = 1877 << 16, tr[5] = 1781 << 16;
474 break;
475 case 3:
476 tl[0] = 1097 << 16, tr[0] = 1087 << 16;
477 tl[1] = 1057 << 16, tr[1] = 1031 << 16;
478 tl[2] = 1049 << 16, tr[2] = 1039 << 16;
479 tl[3] = 1083 << 16, tr[3] = 1055 << 16;
480 tl[4] = 1075 << 16, tr[4] = 1099 << 16;
481 tl[5] = 1003 << 16, tr[5] = 1073 << 16;
482 break;
483 case 4:
484 tl[0] = 197 << 16, tr[0] = 133 << 16;
485 tl[1] = 357 << 16, tr[1] = 229 << 16;
486 tl[2] = 549 << 16, tr[2] = 431 << 16;
487 tl[3] = 949 << 16, tr[3] = 1277 << 16;
488 tl[4] = 1173 << 16, tr[4] = 1671 << 16;
489 tl[5] = 1477 << 16, tr[5] = 1881 << 16;
490 break;
491 case 5:
492 tl[0] = 197 << 16, tr[0] = 133 << 16;
493 tl[1] = 257 << 16, tr[1] = 179 << 16;
494 tl[2] = 549 << 16, tr[2] = 431 << 16;
495 tl[3] = 619 << 16, tr[3] = 497 << 16;
496 tl[4] = 1173 << 16, tr[4] = 1371 << 16;
497 tl[5] = 1577 << 16, tr[5] = 1881 << 16;
498 break;
501 float fDec=1000 + 2400.f * diffusion;
502 for (int i = 0 ; i < 6; i++) {
503 ldec[i]=exp(-float(tl[i] >> 16) / fDec),
504 rdec[i]=exp(-float(tr[i] >> 16) / fDec);
507 float get_time() {
508 return time;
510 void set_time(float time) {
511 this->time = time;
512 // fb = pow(1.0f/4096.0f, (float)(1700/(time*sr)));
513 fb = 1.0 - 0.3 / (time * sr / 44100.0);
515 float get_type() {
516 return type;
518 void set_type(int type) {
519 this->type = type;
520 update_times();
522 float get_diffusion() {
523 return diffusion;
525 void set_diffusion(float diffusion) {
526 this->diffusion = diffusion;
527 update_times();
529 void set_type_and_diffusion(int type, float diffusion) {
530 this->type = type;
531 this->diffusion = diffusion;
532 update_times();
534 float get_fb()
536 return this->fb;
538 void set_fb(float fb)
540 this->fb = fb;
542 float get_cutoff() {
543 return cutoff;
545 void set_cutoff(float cutoff) {
546 this->cutoff = cutoff;
547 lp_left.set_lp(cutoff,sr);
548 lp_right.set_lp(cutoff,sr);
550 void reset()
552 apL1.reset();apR1.reset();
553 apL2.reset();apR2.reset();
554 apL3.reset();apR3.reset();
555 apL4.reset();apR4.reset();
556 apL5.reset();apR5.reset();
557 apL6.reset();apR6.reset();
558 lp_left.reset();lp_right.reset();
559 old_left = 0; old_right = 0;
561 void process(T &left, T &right)
563 unsigned int ipart = phase.ipart();
565 // the interpolated LFO might be an overkill here
566 int lfo = phase.lerp_by_fract_int<int, 14, int>(sine.data[ipart], sine.data[ipart+1]) >> 2;
567 phase += dphase;
569 left += old_right;
570 left = apL1.process_allpass_comb_lerp16(left, tl[0] - 45*lfo, ldec[0]);
571 left = apL2.process_allpass_comb_lerp16(left, tl[1] + 47*lfo, ldec[1]);
572 float out_left = left;
573 left = apL3.process_allpass_comb_lerp16(left, tl[2] + 54*lfo, ldec[2]);
574 left = apL4.process_allpass_comb_lerp16(left, tl[3] - 69*lfo, ldec[3]);
575 left = apL5.process_allpass_comb_lerp16(left, tl[4] + 69*lfo, ldec[4]);
576 left = apL6.process_allpass_comb_lerp16(left, tl[5] - 46*lfo, ldec[5]);
577 old_left = lp_left.process(left * fb);
578 sanitize(old_left);
580 right += old_left;
581 right = apR1.process_allpass_comb_lerp16(right, tr[0] - 45*lfo, rdec[0]);
582 right = apR2.process_allpass_comb_lerp16(right, tr[1] + 47*lfo, rdec[1]);
583 float out_right = right;
584 right = apR3.process_allpass_comb_lerp16(right, tr[2] + 54*lfo, rdec[2]);
585 right = apR4.process_allpass_comb_lerp16(right, tr[3] - 69*lfo, rdec[3]);
586 right = apR5.process_allpass_comb_lerp16(right, tr[4] + 69*lfo, rdec[4]);
587 right = apR6.process_allpass_comb_lerp16(right, tr[5] - 46*lfo, rdec[5]);
588 old_right = lp_right.process(right * fb);
589 sanitize(old_right);
591 left = out_left, right = out_right;
593 void extra_sanitize()
595 lp_left.sanitize();
596 lp_right.sanitize();
600 class filter_module_iface
602 public:
603 virtual void calculate_filter(float freq, float q, int mode, float gain = 1.0) = 0;
604 virtual void filter_activate() = 0;
605 virtual void sanitize() = 0;
606 virtual int process_channel(uint16_t channel_no, float *in, float *out, uint32_t numsamples, int inmask) = 0;
607 virtual float freq_gain(int subindex, float freq, float srate) = 0;
609 virtual ~filter_module_iface() {}
613 class biquad_filter_module: public filter_module_iface
615 private:
616 dsp::biquad_d1<float> left[3], right[3];
617 int order;
619 public:
620 uint32_t srate;
622 enum { mode_12db_lp = 0, mode_24db_lp = 1, mode_36db_lp = 2,
623 mode_12db_hp = 3, mode_24db_hp = 4, mode_36db_hp = 5,
624 mode_6db_bp = 6, mode_12db_bp = 7, mode_18db_bp = 8,
625 mode_6db_br = 9, mode_12db_br = 10, mode_18db_br = 11,
626 mode_count
629 public:
630 biquad_filter_module() : order(0) {}
632 void calculate_filter(float freq, float q, int mode, float gain = 1.0)
634 if (mode <= mode_36db_lp) {
635 order = mode + 1;
636 left[0].set_lp_rbj(freq, pow(q, 1.0 / order), srate, gain);
637 } else if ( mode_12db_hp <= mode && mode <= mode_36db_hp ) {
638 order = mode - mode_12db_hp + 1;
639 left[0].set_hp_rbj(freq, pow(q, 1.0 / order), srate, gain);
640 } else if ( mode_6db_bp <= mode && mode <= mode_18db_bp ) {
641 order = mode - mode_6db_bp + 1;
642 left[0].set_bp_rbj(freq, pow(q, 1.0 / order), srate, gain);
643 } else { // mode_6db_br <= mode <= mode_18db_br
644 order = mode - mode_6db_br + 1;
645 left[0].set_br_rbj(freq, order * 0.1 * q, srate, gain);
648 right[0].copy_coeffs(left[0]);
649 for (int i = 1; i < order; i++) {
650 left[i].copy_coeffs(left[0]);
651 right[i].copy_coeffs(left[0]);
655 void filter_activate()
657 for (int i=0; i < order; i++) {
658 left[i].reset();
659 right[i].reset();
663 void sanitize()
665 for (int i=0; i < order; i++) {
666 left[i].sanitize();
667 right[i].sanitize();
671 inline int process_channel(uint16_t channel_no, float *in, float *out, uint32_t numsamples, int inmask) {
672 dsp::biquad_d1<float> *filter;
673 switch (channel_no) {
674 case 0:
675 filter = left;
676 break;
678 case 1:
679 filter = right;
680 break;
682 default:
683 assert(false);
684 return 0;
687 if (inmask) {
688 switch(order) {
689 case 1:
690 for (uint32_t i = 0; i < numsamples; i++)
691 out[i] = filter[0].process(in[i]);
692 break;
693 case 2:
694 for (uint32_t i = 0; i < numsamples; i++)
695 out[i] = filter[1].process(filter[0].process(in[i]));
696 break;
697 case 3:
698 for (uint32_t i = 0; i < numsamples; i++)
699 out[i] = filter[2].process(filter[1].process(filter[0].process(in[i])));
700 break;
702 } else {
703 if (filter[order - 1].empty())
704 return 0;
705 switch(order) {
706 case 1:
707 for (uint32_t i = 0; i < numsamples; i++)
708 out[i] = filter[0].process_zeroin();
709 break;
710 case 2:
711 if (filter[0].empty())
712 for (uint32_t i = 0; i < numsamples; i++)
713 out[i] = filter[1].process_zeroin();
714 else
715 for (uint32_t i = 0; i < numsamples; i++)
716 out[i] = filter[1].process(filter[0].process_zeroin());
717 break;
718 case 3:
719 if (filter[1].empty())
720 for (uint32_t i = 0; i < numsamples; i++)
721 out[i] = filter[2].process_zeroin();
722 else
723 for (uint32_t i = 0; i < numsamples; i++)
724 out[i] = filter[2].process(filter[1].process(filter[0].process_zeroin()));
725 break;
728 for (int i = 0; i < order; i++)
729 filter[i].sanitize();
730 return filter[order - 1].empty() ? 0 : inmask;
733 float freq_gain(int subindex, float freq, float srate)
735 float level = 1.0;
736 for (int j = 0; j < order; j++)
737 level *= left[j].freq_gain(freq, srate);
738 return level;
742 class two_band_eq
744 private:
745 dsp::onepole<float> lowcut, highcut;
746 float low_gain, high_gain;
748 public:
749 void reset()
751 lowcut.reset();
752 highcut.reset();
755 inline float process(float v)
757 v = dsp::lerp(lowcut.process_hp(v), v, low_gain);
758 v = dsp::lerp(highcut.process_lp(v), v, high_gain);
759 return v;
762 inline void copy_coeffs(const two_band_eq &src)
764 lowcut.copy_coeffs(src.lowcut);
765 highcut.copy_coeffs(src.highcut);
766 low_gain = src.low_gain;
767 high_gain = src.high_gain;
770 void sanitize()
772 lowcut.sanitize();
773 highcut.sanitize();
776 void set(float _low_freq, float _low_gain, float _high_freq, float _high_gain, float sr)
778 lowcut.set_hp(_low_freq, sr);
779 highcut.set_lp(_high_freq, sr);
780 low_gain = _low_gain;
781 high_gain = _high_gain;
785 #if 0
786 { to keep editor happy
787 #endif
790 #endif