+ Framework: handle gridlines with absurd indices properly
[calf.git] / src / modules_dsp.cpp
bloba7e5387812b5b0060e1457f6212ad1bd5465460e
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 <limits.h>
24 #include <memory.h>
25 #if USE_JACK
26 #include <jack/jack.h>
27 #endif
28 #include <calf/giface.h>
29 #include <calf/modules.h>
30 #include <calf/modules_dev.h>
32 using namespace dsp;
33 using namespace calf_plugins;
35 /// convert amplitude value to normalized grid-ish value (0dB = 0.5, 30dB = 1.0, -30 dB = 0.0, -60dB = -0.5, -90dB = -1.0)
36 static inline float dB_grid(float amp)
38 return log(amp) / log(256.0) + 0.4;
41 template<class Fx>
42 static bool get_graph(Fx &fx, int subindex, float *data, int points)
44 for (int i = 0; i < points; i++)
46 typedef std::complex<double> cfloat;
47 double freq = 20.0 * pow (20000.0 / 20.0, i * 1.0 / points);
48 data[i] = dB_grid(fx.freq_gain(subindex, freq, fx.srate));
50 return true;
53 /// convert normalized grid-ish value back to amplitude value
54 static inline float dB_grid_inv(float pos)
56 return pow(256.0, pos - 0.4);
59 static void set_channel_color(cairo_iface *context, int channel)
61 if (channel & 1)
62 context->set_source_rgba(0.75, 1, 0);
63 else
64 context->set_source_rgba(0, 1, 0.75);
65 context->set_line_width(1.5);
68 static bool get_freq_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context, bool use_frequencies = true)
70 if (subindex < 0 )
71 return false;
72 if (use_frequencies)
74 if (subindex < 28)
76 vertical = true;
77 if (subindex == 9) legend = "100 Hz";
78 if (subindex == 18) legend = "1 kHz";
79 if (subindex == 27) legend = "10 kHz";
80 float freq = 100;
81 if (subindex < 9)
82 freq = 10 * (subindex + 1);
83 else if (subindex < 18)
84 freq = 100 * (subindex - 9 + 1);
85 else if (subindex < 27)
86 freq = 1000 * (subindex - 18 + 1);
87 else
88 freq = 10000 * (subindex - 27 + 1);
89 pos = log(freq / 20.0) / log(1000);
90 if (!legend.empty())
91 context->set_source_rgba(0.25, 0.25, 0.25, 0.75);
92 else
93 context->set_source_rgba(0.25, 0.25, 0.25, 0.5);
94 return true;
96 subindex -= 28;
98 if (subindex >= 32)
99 return false;
100 float gain = 16.0 / (1 << subindex);
101 pos = dB_grid(gain);
102 if (pos < -1)
103 return false;
104 if (subindex != 4)
105 context->set_source_rgba(0.25, 0.25, 0.25, subindex & 1 ? 0.5 : 0.75);
106 if (!(subindex & 1))
108 std::stringstream ss;
109 ss << (24 - 6 * subindex) << " dB";
110 legend = ss.str();
112 vertical = false;
113 return true;
116 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
118 bool frequency_response_line_graph::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
120 return get_freq_gridline(subindex, pos, vertical, legend, context);
123 int frequency_response_line_graph::get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
125 subindex_graph = 0;
126 subindex_dot = 0;
127 subindex_gridline = generation ? INT_MAX : 0;
128 return 1;
131 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
133 void flanger_audio_module::activate() {
134 left.reset();
135 right.reset();
136 last_r_phase = *params[par_stereo] * (1.f / 360.f);
137 left.reset_phase(0.f);
138 right.reset_phase(last_r_phase);
139 is_active = true;
142 void flanger_audio_module::set_sample_rate(uint32_t sr) {
143 srate = sr;
144 left.setup(sr);
145 right.setup(sr);
148 void flanger_audio_module::deactivate() {
149 is_active = false;
152 bool flanger_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
154 if (!is_active)
155 return false;
156 if (index == par_delay && subindex < 2)
158 set_channel_color(context, subindex);
159 return ::get_graph(*this, subindex, data, points);
161 return false;
164 float flanger_audio_module::freq_gain(int subindex, float freq, float srate)
166 return (subindex ? right : left).freq_gain(freq, srate);
169 ///////////////////////////////////////////////////////////////////////////////////////////////
171 void phaser_audio_module::set_sample_rate(uint32_t sr)
173 srate = sr;
174 left.setup(sr);
175 right.setup(sr);
178 void phaser_audio_module::activate()
180 is_active = true;
181 left.reset();
182 right.reset();
183 last_r_phase = *params[par_stereo] * (1.f / 360.f);
184 left.reset_phase(0.f);
185 right.reset_phase(last_r_phase);
188 void phaser_audio_module::deactivate()
190 is_active = false;
193 bool phaser_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
195 if (!is_active)
196 return false;
197 if (subindex < 2)
199 set_channel_color(context, subindex);
200 return ::get_graph(*this, subindex, data, points);
202 return false;
205 float phaser_audio_module::freq_gain(int subindex, float freq, float srate)
207 return (subindex ? right : left).freq_gain(freq, srate);
210 bool phaser_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
212 return get_freq_gridline(subindex, pos, vertical, legend, context);
215 ///////////////////////////////////////////////////////////////////////////////////////////////
217 void reverb_audio_module::activate()
219 reverb.reset();
222 void reverb_audio_module::deactivate()
226 void reverb_audio_module::set_sample_rate(uint32_t sr)
228 srate = sr;
229 reverb.setup(sr);
230 amount.set_sample_rate(sr);
233 ///////////////////////////////////////////////////////////////////////////////////////////////
235 bool filter_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
237 if (!is_active)
238 return false;
239 if (index == par_cutoff && !subindex) {
240 context->set_line_width(1.5);
241 return ::get_graph(*this, subindex, data, points);
243 return false;
246 ///////////////////////////////////////////////////////////////////////////////////////////////
248 bool filterclavier_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
250 if (!is_active || index != par_mode) {
251 return false;
253 if (!subindex) {
254 context->set_line_width(1.5);
255 return ::get_graph(*this, subindex, data, points);
257 return false;
260 ///////////////////////////////////////////////////////////////////////////////////////////////
262 rotary_speaker_audio_module::rotary_speaker_audio_module()
264 mwhl_value = hold_value = 0.f;
265 phase_h = phase_l = 0.f;
266 aspeed_l = 1.f;
267 aspeed_h = 1.f;
268 dspeed = 0.f;
271 void rotary_speaker_audio_module::set_sample_rate(uint32_t sr)
273 srate = sr;
274 setup();
277 void rotary_speaker_audio_module::setup()
279 crossover1l.set_lp_rbj(800.f, 0.7, (float)srate);
280 crossover1r.set_lp_rbj(800.f, 0.7, (float)srate);
281 crossover2l.set_hp_rbj(800.f, 0.7, (float)srate);
282 crossover2r.set_hp_rbj(800.f, 0.7, (float)srate);
283 set_vibrato();
286 void rotary_speaker_audio_module::activate()
288 phase_h = phase_l = 0.f;
289 maspeed_h = maspeed_l = 0.f;
290 setup();
293 void rotary_speaker_audio_module::deactivate()
297 void rotary_speaker_audio_module::control_change(int ctl, int val)
299 if (vibrato_mode == 3 && ctl == 64)
301 hold_value = val / 127.f;
302 set_vibrato();
303 return;
305 if (vibrato_mode == 4 && ctl == 1)
307 mwhl_value = val / 127.f;
308 set_vibrato();
309 return;
313 ///////////////////////////////////////////////////////////////////////////////////////////////
315 void multichorus_audio_module::activate()
317 is_active = true;
318 params_changed();
321 void multichorus_audio_module::deactivate()
323 is_active = false;
326 void multichorus_audio_module::set_sample_rate(uint32_t sr) {
327 srate = sr;
328 left.setup(sr);
329 right.setup(sr);
332 bool multichorus_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
334 if (!is_active)
335 return false;
336 if (index == par_delay && subindex < 3)
338 if (subindex < 2)
339 set_channel_color(context, subindex);
340 else {
341 context->set_source_rgba(0, 1, 0);
342 context->set_line_width(1.0);
344 return ::get_graph(*this, subindex, data, points);
346 if (index == par_rate && !subindex) {
347 for (int i = 0; i < points; i++)
348 data[i] = 0.95 * sin(i * 2 * M_PI / points);
349 return true;
351 return false;
354 bool multichorus_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context)
356 if ((index != par_rate && index != par_depth) || subindex >= 2 * (int)*params[par_voices])
357 return false;
359 set_channel_color(context, subindex);
360 sine_multi_lfo<float, 8> &lfo = (subindex & 1 ? right : left).lfo;
361 if (index == par_rate)
363 x = (double)(lfo.phase + lfo.vphase * (subindex >> 1)) / 4096.0;
364 y = 0.95 * sin(x * 2 * M_PI);
366 else
368 double ph = (double)(lfo.phase + lfo.vphase * (subindex >> 1)) / 4096.0;
369 x = 0.5 + 0.5 * sin(ph * 2 * M_PI);
370 y = subindex & 1 ? -0.75 : 0.75;
372 return true;
375 bool multichorus_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
377 if (index == par_rate && !subindex)
379 pos = 0;
380 vertical = false;
381 return true;
383 if (index == par_delay)
384 return get_freq_gridline(subindex, pos, vertical, legend, context);
385 return false;
388 float multichorus_audio_module::freq_gain(int subindex, float freq, float srate)
390 if (subindex == 2)
391 return *params[par_amount] * left.post.freq_gain(freq, srate);
392 return (subindex ? right : left).freq_gain(freq, srate);
395 ///////////////////////////////////////////////////////////////////////////////////////////////
397 compressor_audio_module::compressor_audio_module()
399 is_active = false;
400 srate = 0;
403 void compressor_audio_module::activate()
405 is_active = true;
406 linSlope = 0.f;
407 peak = 0.f;
408 clip = 0.f;
411 void compressor_audio_module::deactivate()
413 is_active = false;
416 void compressor_audio_module::set_sample_rate(uint32_t sr)
418 srate = sr;
419 awL.set(sr);
420 awR.set(sr);
423 bool compressor_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
425 if (!is_active)
426 return false;
427 if (subindex > 1) // 1
428 return false;
429 for (int i = 0; i < points; i++)
431 float input = dB_grid_inv(-1.0 + i * 2.0 / (points - 1));
432 float output = output_level(input);
433 if (subindex == 0)
434 data[i] = dB_grid(input);
435 else
436 data[i] = dB_grid(output);
438 if (subindex == (*params[param_bypass] > 0.5f ? 1 : 0))
439 context->set_source_rgba(0.5, 0.5, 0.5, 0.5);
440 else {
441 context->set_source_rgba(0, 1, 0, 1);
442 context->set_line_width(2);
444 return true;
447 bool compressor_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context)
449 if (!is_active)
450 return false;
451 if (!subindex)
453 x = 0.5 + 0.5 * dB_grid(detected);
454 y = dB_grid(*params[param_bypass] > 0.5f ? detected : output_level(detected));
455 return *params[param_bypass] > 0.5f ? false : true;
457 return false;
460 bool compressor_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
462 bool tmp;
463 vertical = (subindex & 1) != 0;
464 bool result = get_freq_gridline(subindex >> 1, pos, tmp, legend, context, false);
465 if (result && vertical) {
466 if ((subindex & 4) && !legend.empty()) {
467 legend = "";
469 else {
470 size_t pos = legend.find(" dB");
471 if (pos != std::string::npos)
472 legend.erase(pos);
474 pos = 0.5 + 0.5 * pos;
476 return result;
479 // In case of doubt: this function is written by Thor. I just moved it to this file, damaging
480 // the output of "git annotate" in the process.
481 uint32_t compressor_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
483 bool bypass = *params[param_bypass] > 0.5f;
485 if(bypass) {
486 int count = numsamples * sizeof(float);
487 memcpy(outs[0], ins[0], count);
488 memcpy(outs[1], ins[1], count);
490 if(params[param_compression] != NULL) {
491 *params[param_compression] = 1.f;
494 if(params[param_clip] != NULL) {
495 *params[param_clip] = 0.f;
498 if(params[param_peak] != NULL) {
499 *params[param_peak] = 0.f;
502 return inputs_mask;
505 bool rms = *params[param_detection] == 0;
506 bool average = *params[param_stereo_link] == 0;
507 bool aweighting = *params[param_aweighting] > 0.5f;
508 float linThreshold = *params[param_threshold];
509 ratio = *params[param_ratio];
510 float attack = *params[param_attack];
511 float attack_coeff = std::min(1.f, 1.f / (attack * srate / 4000.f));
512 float release = *params[param_release];
513 float release_coeff = std::min(1.f, 1.f / (release * srate / 4000.f));
514 makeup = *params[param_makeup];
515 knee = *params[param_knee];
517 float linKneeSqrt = sqrt(knee);
518 linKneeStart = linThreshold / linKneeSqrt;
519 adjKneeStart = linKneeStart*linKneeStart;
520 float linKneeStop = linThreshold * linKneeSqrt;
522 threshold = log(linThreshold);
523 kneeStart = log(linKneeStart);
524 kneeStop = log(linKneeStop);
525 compressedKneeStop = (kneeStop - threshold) / ratio + threshold;
527 numsamples += offset;
529 float compression = 1.f;
531 peak -= peak * 5.f * numsamples / srate;
533 clip -= std::min(clip, numsamples);
535 while(offset < numsamples) {
536 float left = ins[0][offset];
537 float right = ins[1][offset];
539 if(aweighting) {
540 left = awL.process(left);
541 right = awR.process(right);
544 float absample = average ? (fabs(left) + fabs(right)) * 0.5f : std::max(fabs(left), fabs(right));
545 if(rms) absample *= absample;
547 linSlope += (absample - linSlope) * (absample > linSlope ? attack_coeff : release_coeff);
549 float gain = 1.f;
551 if(linSlope > 0.f) {
552 gain = output_gain(linSlope, rms);
555 compression = gain;
556 gain *= makeup;
558 float outL = ins[0][offset] * gain;
559 float outR = ins[1][offset] * gain;
561 outs[0][offset] = outL;
562 outs[1][offset] = outR;
564 ++offset;
566 float maxLR = std::max(fabs(outL), fabs(outR));
568 if(maxLR > 1.f) clip = srate >> 3; /* blink clip LED for 125 ms */
570 if(maxLR > peak) {
571 peak = maxLR;
575 detected = rms ? sqrt(linSlope) : linSlope;
577 if(params[param_compression] != NULL) {
578 *params[param_compression] = compression;
581 if(params[param_clip] != NULL) {
582 *params[param_clip] = clip;
585 if(params[param_peak] != NULL) {
586 *params[param_peak] = peak;
589 return inputs_mask;