Merge branch 'filterclavier' of ssh://repo.or.cz/srv/git/calf
[calf.git] / src / modules_dsp.cpp
blobeec00566170b12e1d450cb94eb80d1a38fbaeed7
1 /* Calf DSP Library
2 * Example audio modules - DSP code
4 * Copyright (C) 2001-2008 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., 59 Temple Place, Suite 330,
19 * Boston, MA 02111-1307, USA.
21 #include <config.h>
22 #include <assert.h>
23 #include <memory.h>
24 #if USE_JACK
25 #include <jack/jack.h>
26 #endif
27 #include <calf/giface.h>
28 #include <calf/modules.h>
29 #include <calf/modules_dev.h>
31 using namespace dsp;
32 using namespace calf_plugins;
34 /// convert amplitude value to normalized grid-ish value (0dB = 0.5, 30dB = 1.0, -30 dB = 0.0, -60dB = -0.5, -90dB = -1.0)
35 static inline float dB_grid(float amp)
37 return log(amp) / log(256.0) + 0.4;
40 template<class Fx>
41 static bool get_graph(Fx &fx, int subindex, float *data, int points)
43 for (int i = 0; i < points; i++)
45 typedef std::complex<double> cfloat;
46 double freq = 20.0 * pow (20000.0 / 20.0, i * 1.0 / points);
47 data[i] = dB_grid(fx.freq_gain(subindex, freq, fx.srate));
49 return true;
52 /// convert normalized grid-ish value back to amplitude value
53 static inline float dB_grid_inv(float pos)
55 return pow(256.0, pos - 0.4);
58 static void set_channel_color(cairo_iface *context, int channel)
60 if (channel & 1)
61 context->set_source_rgba(0.75, 1, 0);
62 else
63 context->set_source_rgba(0, 1, 0.75);
64 context->set_line_width(1.5);
67 static bool get_freq_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
69 if (subindex < 28)
71 vertical = true;
72 if (subindex == 9) legend = "100 Hz";
73 if (subindex == 18) legend = "1 kHz";
74 if (subindex == 27) legend = "10 kHz";
75 float freq = 100;
76 if (subindex < 9)
77 freq = 10 * (subindex + 1);
78 else if (subindex < 18)
79 freq = 100 * (subindex - 9 + 1);
80 else if (subindex < 27)
81 freq = 1000 * (subindex - 18 + 1);
82 else
83 freq = 10000 * (subindex - 27 + 1);
84 pos = log(freq / 20.0) / log(1000);
85 if (!legend.empty())
86 context->set_source_rgba(0.25, 0.25, 0.25, 0.75);
87 else
88 context->set_source_rgba(0.25, 0.25, 0.25, 0.5);
89 return true;
91 subindex -= 28;
92 float gain = 16.0 / (1 << subindex);
93 pos = dB_grid(gain);
94 if (pos < -1)
95 return false;
96 if (subindex != 4)
97 context->set_source_rgba(0.25, 0.25, 0.25, subindex & 1 ? 0.5 : 0.75);
98 if (!(subindex & 1))
100 std::stringstream ss;
101 ss << (24 - 6 * subindex) << " dB";
102 legend = ss.str();
104 vertical = false;
105 return true;
109 void flanger_audio_module::activate() {
110 left.reset();
111 right.reset();
112 last_r_phase = *params[par_stereo] * (1.f / 360.f);
113 left.reset_phase(0.f);
114 right.reset_phase(last_r_phase);
115 is_active = true;
118 void flanger_audio_module::set_sample_rate(uint32_t sr) {
119 srate = sr;
120 left.setup(sr);
121 right.setup(sr);
124 void flanger_audio_module::deactivate() {
125 is_active = false;
128 bool flanger_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
130 if (!is_active)
131 return false;
132 if (index == par_delay && subindex < 2)
134 set_channel_color(context, subindex);
135 return ::get_graph(*this, subindex, data, points);
137 return false;
140 float flanger_audio_module::freq_gain(int subindex, float freq, float srate)
142 return (subindex ? right : left).freq_gain(freq, srate);
145 bool flanger_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
147 if (index == par_delay)
148 return get_freq_gridline(subindex, pos, vertical, legend, context);
149 return false;
152 ///////////////////////////////////////////////////////////////////////////////////////////////
154 void phaser_audio_module::set_sample_rate(uint32_t sr)
156 srate = sr;
157 left.setup(sr);
158 right.setup(sr);
161 void phaser_audio_module::activate()
163 is_active = true;
164 left.reset();
165 right.reset();
166 last_r_phase = *params[par_stereo] * (1.f / 360.f);
167 left.reset_phase(0.f);
168 right.reset_phase(last_r_phase);
171 void phaser_audio_module::deactivate()
173 is_active = false;
176 bool phaser_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
178 if (!is_active)
179 return false;
180 if (subindex < 2)
182 set_channel_color(context, subindex);
183 return ::get_graph(*this, subindex, data, points);
185 return false;
188 float phaser_audio_module::freq_gain(int subindex, float freq, float srate)
190 return (subindex ? right : left).freq_gain(freq, srate);
193 bool phaser_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
195 return get_freq_gridline(subindex, pos, vertical, legend, context);
198 ///////////////////////////////////////////////////////////////////////////////////////////////
200 void reverb_audio_module::activate()
202 reverb.reset();
205 void reverb_audio_module::deactivate()
209 void reverb_audio_module::set_sample_rate(uint32_t sr)
211 srate = sr;
212 reverb.setup(sr);
213 amount.set_sample_rate(sr);
216 ///////////////////////////////////////////////////////////////////////////////////////////////
218 bool filter_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
220 if (!is_active)
221 return false;
222 if (index == par_cutoff && !subindex) {
223 context->set_line_width(1.5);
224 return ::get_graph(*this, subindex, data, points);
226 return false;
229 bool filter_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
231 if (index == par_cutoff)
232 return get_freq_gridline(subindex, pos, vertical, legend, context);
233 return false;
236 ///////////////////////////////////////////////////////////////////////////////////////////////
238 rotary_speaker_audio_module::rotary_speaker_audio_module()
240 mwhl_value = hold_value = 0.f;
241 phase_h = phase_l = 0.f;
242 aspeed_l = 1.f;
243 aspeed_h = 1.f;
244 dspeed = 0.f;
247 void rotary_speaker_audio_module::set_sample_rate(uint32_t sr)
249 srate = sr;
250 setup();
253 void rotary_speaker_audio_module::setup()
255 crossover1l.set_lp_rbj(800.f, 0.7, (float)srate);
256 crossover1r.set_lp_rbj(800.f, 0.7, (float)srate);
257 crossover2l.set_hp_rbj(800.f, 0.7, (float)srate);
258 crossover2r.set_hp_rbj(800.f, 0.7, (float)srate);
259 set_vibrato();
262 void rotary_speaker_audio_module::activate()
264 phase_h = phase_l = 0.f;
265 maspeed_h = maspeed_l = 0.f;
266 setup();
269 void rotary_speaker_audio_module::deactivate()
273 void rotary_speaker_audio_module::control_change(int ctl, int val)
275 if (vibrato_mode == 3 && ctl == 64)
277 hold_value = val / 127.f;
278 set_vibrato();
279 return;
281 if (vibrato_mode == 4 && ctl == 1)
283 mwhl_value = val / 127.f;
284 set_vibrato();
285 return;
289 ///////////////////////////////////////////////////////////////////////////////////////////////
291 void multichorus_audio_module::activate()
293 is_active = true;
294 params_changed();
297 void multichorus_audio_module::deactivate()
299 is_active = false;
302 void multichorus_audio_module::set_sample_rate(uint32_t sr) {
303 srate = sr;
304 left.setup(sr);
305 right.setup(sr);
308 bool multichorus_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
310 if (!is_active)
311 return false;
312 if (index == par_delay && subindex < 3)
314 if (subindex < 2)
315 set_channel_color(context, subindex);
316 else {
317 context->set_source_rgba(0, 1, 0);
318 context->set_line_width(1.0);
320 return ::get_graph(*this, subindex, data, points);
322 if (index == par_rate && !subindex) {
323 for (int i = 0; i < points; i++)
324 data[i] = 0.95 * sin(i * 2 * M_PI / points);
325 return true;
327 return false;
330 bool multichorus_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context)
332 if ((index != par_rate && index != par_depth) || subindex >= 2 * (int)*params[par_voices])
333 return false;
335 set_channel_color(context, subindex);
336 sine_multi_lfo<float, 8> &lfo = (subindex & 1 ? right : left).lfo;
337 if (index == par_rate)
339 x = (double)(lfo.phase + lfo.vphase * (subindex >> 1)) / 4096.0;
340 y = 0.95 * sin(x * 2 * M_PI);
342 else
344 double ph = (double)(lfo.phase + lfo.vphase * (subindex >> 1)) / 4096.0;
345 x = 0.5 + 0.5 * sin(ph * 2 * M_PI);
346 y = subindex & 1 ? -0.75 : 0.75;
348 return true;
351 bool multichorus_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
353 if (index == par_rate && !subindex)
355 pos = 0;
356 vertical = false;
357 return true;
359 if (index == par_delay)
360 return get_freq_gridline(subindex, pos, vertical, legend, context);
361 return false;
364 float multichorus_audio_module::freq_gain(int subindex, float freq, float srate)
366 if (subindex == 2)
367 return *params[par_amount] * left.post.freq_gain(freq, srate);
368 return (subindex ? right : left).freq_gain(freq, srate);
371 ///////////////////////////////////////////////////////////////////////////////////////////////
373 compressor_audio_module::compressor_audio_module()
375 is_active = false;
376 srate = 0;
379 void compressor_audio_module::activate()
381 is_active = true;
382 linSlope = 0.f;
383 peak = 0.f;
384 clip = 0.f;
387 void compressor_audio_module::deactivate()
389 is_active = false;
392 void compressor_audio_module::set_sample_rate(uint32_t sr)
394 srate = sr;
395 awL.set(sr);
396 awR.set(sr);
399 bool compressor_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
401 if (!is_active)
402 return false;
403 if (subindex > 1) // 1
404 return false;
405 for (int i = 0; i < points; i++)
407 float input = dB_grid_inv(-1.0 + i * 2.0 / (points - 1));
408 float output = output_level(input);
409 if (subindex == 0)
410 data[i] = dB_grid(input);
411 else
412 data[i] = dB_grid(output);
414 if (subindex == (*params[param_bypass] > 0.5f ? 1 : 0))
415 context->set_source_rgba(0.5, 0.5, 0.5, 0.5);
416 else {
417 context->set_source_rgba(0, 1, 0, 1);
418 context->set_line_width(2);
420 return true;
423 bool compressor_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context)
425 if (!is_active)
426 return false;
427 if (!subindex)
429 x = 0.5 + 0.5 * dB_grid(detected);
430 y = dB_grid(*params[param_bypass] > 0.5f ? detected : output_level(detected));
431 return *params[param_bypass] > 0.5f ? false : true;
433 return false;
436 bool compressor_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
438 bool tmp;
439 vertical = (subindex & 1) != 0;
440 bool result = get_freq_gridline(subindex >> 1, pos, tmp, legend, context);
441 if (result && vertical) {
442 if ((subindex & 4) && !legend.empty()) {
443 legend = "";
445 else {
446 size_t pos = legend.find(" dB");
447 if (pos != std::string::npos)
448 legend.erase(pos);
450 pos = 0.5 + 0.5 * pos;
452 return result;
455 // In case of doubt: this function is written by Thor. I just moved it to this file, damaging
456 // the output of "git annotate" in the process.
457 uint32_t compressor_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
459 bool bypass = *params[param_bypass] > 0.5f;
461 if(bypass) {
462 int count = numsamples * sizeof(float);
463 memcpy(outs[0], ins[0], count);
464 memcpy(outs[1], ins[1], count);
466 if(params[param_compression] != NULL) {
467 *params[param_compression] = 1.f;
470 if(params[param_clip] != NULL) {
471 *params[param_clip] = 0.f;
474 if(params[param_peak] != NULL) {
475 *params[param_peak] = 0.f;
478 return inputs_mask;
481 bool rms = *params[param_detection] == 0;
482 bool average = *params[param_stereo_link] == 0;
483 bool aweighting = *params[param_aweighting] > 0.5f;
484 float linThreshold = *params[param_threshold];
485 ratio = *params[param_ratio];
486 float attack = *params[param_attack];
487 float attack_coeff = std::min(1.f, 1.f / (attack * srate / 4000.f));
488 float release = *params[param_release];
489 float release_coeff = std::min(1.f, 1.f / (release * srate / 4000.f));
490 makeup = *params[param_makeup];
491 knee = *params[param_knee];
493 float linKneeSqrt = sqrt(knee);
494 linKneeStart = linThreshold / linKneeSqrt;
495 adjKneeStart = linKneeStart*linKneeStart;
496 float linKneeStop = linThreshold * linKneeSqrt;
498 threshold = log(linThreshold);
499 kneeStart = log(linKneeStart);
500 kneeStop = log(linKneeStop);
501 compressedKneeStop = (kneeStop - threshold) / ratio + threshold;
503 numsamples += offset;
505 float compression = 1.f;
507 peak -= peak * 5.f * numsamples / srate;
509 clip -= std::min(clip, numsamples);
511 while(offset < numsamples) {
512 float left = ins[0][offset];
513 float right = ins[1][offset];
515 if(aweighting) {
516 left = awL.process(left);
517 right = awR.process(right);
520 float absample = average ? (fabs(left) + fabs(right)) * 0.5f : std::max(fabs(left), fabs(right));
521 if(rms) absample *= absample;
523 linSlope += (absample - linSlope) * (absample > linSlope ? attack_coeff : release_coeff);
525 float gain = 1.f;
527 if(linSlope > 0.f) {
528 gain = output_gain(linSlope, rms);
531 compression = gain;
532 gain *= makeup;
534 float outL = ins[0][offset] * gain;
535 float outR = ins[1][offset] * gain;
537 outs[0][offset] = outL;
538 outs[1][offset] = outR;
540 ++offset;
542 float maxLR = std::max(fabs(outL), fabs(outR));
544 if(maxLR > 1.f) clip = srate >> 3; /* blink clip LED for 125 ms */
546 if(maxLR > peak) {
547 peak = maxLR;
551 detected = rms ? sqrt(linSlope) : linSlope;
553 if(params[param_compression] != NULL) {
554 *params[param_compression] = compression;
557 if(params[param_clip] != NULL) {
558 *params[param_clip] = clip;
561 if(params[param_peak] != NULL) {
562 *params[param_peak] = peak;
565 return inputs_mask;