EQ8/EQ12: High pass and low pass with Q
[calf.git] / src / giface.cpp
blob9bf1489c49629c951eb800dcfb76ab60cb87ba5c
1 /* Calf DSP Library
2 * Implementation of various helpers for the plugin interface.
4 * Copyright (C) 2001-2010 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 #include <config.h>
22 #include <limits.h>
23 #include <stdlib.h>
24 #include <math.h>
25 #include <calf/giface.h>
26 #include <calf/utils.h>
28 using namespace std;
29 using namespace calf_utils;
30 using namespace calf_plugins;
32 static const char automation_key_prefix[] = "automation_v1_";
34 void automation_range::send_configure(const plugin_metadata_iface *metadata, uint32_t from_controller, send_configure_iface *sci)
36 std::stringstream ss1, ss2;
37 ss1 << automation_key_prefix << from_controller << "_to_" << metadata->get_param_props(param_no)->short_name;
38 ss2 << min_value << " " << max_value;
39 sci->send_configure(ss1.str().c_str(), ss2.str().c_str());
42 automation_range *automation_range::new_from_configure(const plugin_metadata_iface *metadata, const char *key, const char *value, uint32_t &from_controller)
44 if (0 != strncmp(key, automation_key_prefix, sizeof(automation_key_prefix) - 1))
45 return NULL;
46 key += sizeof(automation_key_prefix) - 1;
47 const char *totoken = strstr(key, "_to_");
48 if (!totoken)
49 return NULL;
50 string from_ctl(key, totoken - key);
51 for (size_t i = 0; i < from_ctl.length(); i++)
53 if (!isdigit(from_ctl[i]))
54 return NULL;
56 from_controller = (uint32_t)atoi(from_ctl.c_str());
57 key = totoken + 4;
59 size_t pcount = metadata->get_param_count();
60 for (size_t i = 0; i < pcount; ++i) {
61 const parameter_properties *props = metadata->get_param_props(i);
62 if (!strcmp(key, props->short_name))
64 std::stringstream ss(value);
65 double minv, maxv;
66 ss >> minv >> maxv;
67 return new automation_range(minv, maxv, i);
71 return NULL;
74 float parameter_properties::from_01(double value01) const
76 double value = dsp::clip(value01, 0., 1.);
77 switch(flags & PF_SCALEMASK)
79 case PF_SCALE_DEFAULT:
80 case PF_SCALE_LINEAR:
81 case PF_SCALE_PERC:
82 default:
83 value = min + (max - min) * value01;
84 break;
85 case PF_SCALE_QUAD:
86 value = min + (max - min) * value01 * value01;
87 break;
88 case PF_SCALE_LOG:
89 value = min * pow(double(max / min), value01);
90 break;
91 case PF_SCALE_GAIN:
92 if (value01 < 0.00001)
93 value = min;
94 else {
95 float rmin = std::max(1.0f / 1024.0f, min);
96 value = rmin * pow(double(max / rmin), value01);
98 break;
99 case PF_SCALE_LOG_INF:
100 assert(step);
101 if (value01 > (step - 1.0) / step)
102 value = FAKE_INFINITY;
103 else
104 value = min * pow(double(max / min), value01 * step / (step - 1.0));
105 break;
107 switch(flags & PF_TYPEMASK)
109 case PF_INT:
110 case PF_BOOL:
111 case PF_ENUM:
112 case PF_ENUM_MULTI:
113 if (value > 0)
114 value = (int)(value + 0.5);
115 else
116 value = (int)(value - 0.5);
117 break;
119 return value;
122 double parameter_properties::to_01(float value) const
124 switch(flags & PF_SCALEMASK)
126 case PF_SCALE_DEFAULT:
127 case PF_SCALE_LINEAR:
128 case PF_SCALE_PERC:
129 default:
130 return double(value - min) / (max - min);
131 case PF_SCALE_QUAD:
132 return sqrt(double(value - min) / (max - min));
133 case PF_SCALE_LOG:
134 value /= min;
135 return log((double)value) / log((double)max / min);
136 case PF_SCALE_LOG_INF:
137 if (IS_FAKE_INFINITY(value))
138 return max;
139 value /= min;
140 assert(step);
141 return (step - 1.0) * log((double)value) / (step * log((double)max / min));
142 case PF_SCALE_GAIN:
143 if (value < 1.0 / 1024.0) // new bottom limit - 60 dB
144 return 0;
145 double rmin = std::max(1.0f / 1024.0f, min);
146 value /= rmin;
147 return log((double)value) / log(max / rmin);
151 float parameter_properties::get_increment() const
153 float increment = 0.01;
154 if (step > 1)
155 increment = 1.0 / (step - 1);
156 else
157 if (step > 0 && step < 1)
158 increment = step;
159 else
160 if ((flags & PF_TYPEMASK) != PF_FLOAT)
161 increment = 1.0 / (max - min);
162 return increment;
164 std::string human_readable(float value, uint32_t base, char *format)
166 // format is something like "%.2f%sB" for e.g. "1.23kB"
167 // base is something like 1000 or 1024
168 char buf[32];
169 const char *suf[] = { "", "k", "m", "g", "t", "p", "e" };
170 if (value == 0) {
171 sprintf(buf, format, 0.f, "");
172 return string(buf);
174 double val = abs(value);
175 int place = (int)(log(val) / log(base));
176 double num = val / pow(base, place);
177 sprintf(buf, format, (float)((value > 0) - (value < 0)) * num, suf[place]);
178 return string(buf);
181 int parameter_properties::get_char_count() const
183 if ((flags & PF_SCALEMASK) == PF_SCALE_PERC)
184 return 6;
185 if ((flags & PF_SCALEMASK) == PF_SCALE_GAIN) {
186 char buf[256];
187 size_t len = 0;
188 snprintf(buf, sizeof(buf), "%0.0f dB", 6.0 * log(min) / log(2));
189 len = strlen(buf);
190 snprintf(buf, sizeof(buf), "%0.0f dB", 6.0 * log(max) / log(2));
191 len = std::max(len, strlen(buf)) + 2;
192 return (int)len;
194 std::string min_ = to_string(min);
195 std::string max_ = to_string(max);
196 std::string mid_ = to_string(min + (max - min) * 1. / 3);
197 return std::max((int)min_.length(), std::max((int)max_.length(), std::max((int)mid_.length(), 3)));
201 std::string parameter_properties::to_string(float value) const
203 char buf[32];
204 if ((flags & PF_SCALEMASK) == PF_SCALE_PERC) {
205 snprintf(buf, sizeof(buf), "%0.2f%%", 100.0 * value);
206 return string(buf);
208 if ((flags & PF_SCALEMASK) == PF_SCALE_GAIN) {
209 if (value < 1.0 / 1024.0) // new bottom limit - 60 dB
210 return "-inf dB"; // XXXKF change to utf-8 infinity
211 snprintf(buf, sizeof(buf), "%0.1f dB", dsp::amp2dB(value));
212 return string(buf);
214 std::string s_;
215 switch(flags & PF_TYPEMASK)
217 case PF_INT:
218 case PF_BOOL:
219 case PF_ENUM:
220 case PF_ENUM_MULTI:
221 value = (int)value;
222 s_ = human_readable(value, 1000, (char*)"%g%s");
223 snprintf(buf, sizeof(buf), "%s", s_.c_str());
224 //printf("%.2f %s\n", value, buf);
225 break;
226 case PF_FLOAT:
227 value = round(value * 1000) / 1000;
228 switch (flags & PF_DIGITMASK) {
229 case PF_DIGIT_0:
230 snprintf(buf, sizeof(buf), "%.0f", value);
231 break;
232 case PF_DIGIT_1:
233 snprintf(buf, sizeof(buf), "%.1f", value);
234 break;
235 case PF_DIGIT_2:
236 snprintf(buf, sizeof(buf), "%.2f", value);
237 break;
238 case PF_DIGIT_3:
239 snprintf(buf, sizeof(buf), "%.3f", value);
240 break;
241 case PF_DIGIT_ALL:
242 default:
243 snprintf(buf, sizeof(buf), "%g", value);
244 break;
246 break;
247 default:
248 snprintf(buf, sizeof(buf), "%g", value);
249 break;
252 if ((flags & PF_SCALEMASK) == PF_SCALE_LOG_INF && IS_FAKE_INFINITY(value))
253 snprintf(buf, sizeof(buf), "∞"); // XXXKF change to utf-8 infinity
255 switch(flags & PF_UNITMASK) {
256 case PF_UNIT_DB: return string(buf) + " dB";
257 case PF_UNIT_HZ: return string(buf) + " Hz";
258 case PF_UNIT_SEC: return string(buf) + " s";
259 case PF_UNIT_MSEC: return string(buf) + " ms";
260 case PF_UNIT_CENTS: return string(buf) + " ct";
261 case PF_UNIT_SEMITONES: return string(buf) + "#";
262 case PF_UNIT_BPM: return string(buf) + " bpm";
263 case PF_UNIT_RPM: return string(buf) + " rpm";
264 case PF_UNIT_DEG: return string(buf) + " deg";
265 case PF_UNIT_SAMPLES: return string(buf) + " smpl";
266 case PF_UNIT_NOTE:
268 static const char *notes = "C C#D D#E F F#G G#A A#B ";
269 int note = (int)value;
270 if (note < 0 || note > 127)
271 return "---";
272 return string(notes + 2 * (note % 12), 2) + i2s(note / 12 - 2);
276 return string(buf);
279 float parameter_properties::string_to_value(const char* string) const
281 float value = atof(string);
282 if ((flags & PF_SCALEMASK) == PF_SCALE_PERC) {
283 return value / 100.0;
285 if ((flags & PF_SCALEMASK) == PF_SCALE_GAIN) {
286 return dsp::dB2amp(value);
288 return value;
291 ////////////////////////////////////////////////////////////////////////
293 void calf_plugins::plugin_ctl_iface::clear_preset() {
294 int param_count = get_metadata_iface()->get_param_count();
295 for (int i = 0; i < param_count; i++)
297 const parameter_properties &pp = *get_metadata_iface()->get_param_props(i);
298 set_param_value(i, pp.def_value);
300 vector<string> vars;
301 get_metadata_iface()->get_configure_vars(vars);
302 for (size_t i = 0; i < vars.size(); ++i)
303 configure(vars[i].c_str(), NULL);
306 const char *calf_plugins::load_gui_xml(const std::string &plugin_id)
308 try {
309 return strdup(calf_utils::load_file((std::string(PKGLIBDIR) + "/gui-" + plugin_id + ".xml").c_str()).c_str());
311 catch(file_exception e)
313 return NULL;
317 bool calf_plugins::get_freq_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context, bool use_frequencies, float res, float ofs)
319 if (subindex < 0)
320 return false;
321 static const double dash[] = {2.0};
322 // frequency grid
323 if (use_frequencies)
325 if (subindex < 28)
327 vertical = true;
328 if (subindex == 9) legend = "100 Hz";
329 if (subindex == 18) legend = "1 kHz";
330 if (subindex == 27) legend = "10 kHz";
332 float freq = subindex_to_freq(subindex);
333 pos = log(freq / 20.0) / log(1000);
335 if (!legend.empty()) {
336 context->set_source_rgba(0, 0, 0, 0.1);
337 context->set_dash(dash, 0);
338 } else {
339 context->set_source_rgba(0, 0, 0, 0.1);
340 context->set_dash(dash, 1);
342 return true;
344 subindex -= 28;
347 if (subindex >= 32)
348 return false;
350 // gain/dB grid
351 float gain = 64.0 / (1 << subindex);
352 pos = dB_grid(gain, res, ofs);
354 if (pos < -1)
355 return false;
357 if (!(subindex & 1)) {
358 std::stringstream ss;
359 ss << (36 - 6 * subindex) << " dB";
360 legend = ss.str();
362 if (!legend.empty() and subindex != 6) {
363 context->set_source_rgba(0, 0, 0, 0.1);
364 context->set_dash(dash, 0);
365 } else if (subindex != 6) {
366 context->set_source_rgba(0, 0, 0, 0.1);
367 context->set_dash(dash, 1);
368 } else {
369 context->set_dash(dash, 0);
371 vertical = false;
372 return true;
375 void calf_plugins::set_channel_color(cairo_iface *context, int channel, float alpha)
377 if (channel & 1)
378 context->set_source_rgba(0.25, 0.10, 0.0, alpha);
379 else
380 context->set_source_rgba(0.05, 0.25, 0.0, alpha);
382 void calf_plugins::set_channel_dash(cairo_iface *context, int channel)
384 double dash[] = {8,2};
385 int length = 2;
386 switch (channel) {
387 case 0:
388 default:
389 dash[0] = 6;
390 dash[1] = 1.5;
391 length = 2;
392 break;
393 case 1:
394 dash[0] = 4.5;
395 dash[1] = 1.5;
396 length = 2;
397 break;
398 case 2:
399 dash[0] = 3;
400 dash[1] = 1.5;
401 length = 2;
402 break;
403 case 3:
404 dash[0] = 1.5;
405 dash[1] = 1.5;
406 length = 2;
407 break;
409 context->set_dash(dash, length);
412 void calf_plugins::draw_cairo_label(cairo_iface *context, const char *label, float x, float y, int pos, float margin, float align)
414 context->draw_label(label, x, y, pos, margin, align);
416 ////////////////////////////////////////////////////////////////////////
418 bool frequency_response_line_graph::get_graph(int index, int subindex, int phase, float *data, int points, cairo_iface *context, int *mode) const
420 if (phase or subindex)
421 return false;
422 return ::get_graph(*this, subindex, data, points);
424 bool frequency_response_line_graph::get_gridline(int index, int subindex, int phase, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
426 if (phase)
427 return false;
428 return get_freq_gridline(subindex, pos, vertical, legend, context, true);
430 bool frequency_response_line_graph::get_layers(int index, int generation, unsigned int &layers) const
432 redraw_graph = redraw_graph || !generation;
433 layers = (generation ? LG_NONE : LG_CACHE_GRID) | (redraw_graph ? LG_CACHE_GRAPH : LG_NONE);
434 bool r = redraw_graph;
435 redraw_graph = false;
436 return r;
438 std::string frequency_response_line_graph::get_crosshair_label(int x, int y, int sx, int sy, int dB, int name, int note, int cents) const
440 return frequency_crosshair_label(x, y, sx, sy, dB, name, note, cents);
442 std::string calf_plugins::frequency_crosshair_label(int x, int y, int sx, int sy, int dB, int name, int note, int cents, double res, double ofs)
444 std::stringstream ss;
445 char str[1024];
446 char tmp[1024];
447 float f = exp((float(x) / float(sx)) * log(1000)) * 20.0;
448 float db = dsp::amp2dB(dB_grid_inv(-1 + (2 - float(y) / float(sy) * 2), res, ofs));
449 dsp::note_desc desc = dsp::hz_to_note(f, 440);
450 sprintf(str, "%.2f Hz", f);
451 if (dB) {
452 sprintf(tmp, "%s\n%.2f dB", str, db);
453 strcpy(str, tmp);
455 if (name) {
456 sprintf(tmp, "%s\nNote: %s%+d", str, desc.name, desc.octave);
457 strcpy(str, tmp);
459 if (cents) {
460 sprintf(tmp, "%s\nCents: %+.2f", str, desc.cents);
461 strcpy(str, tmp);
463 if (note) {
464 sprintf(tmp, "%s\nMIDI: %d", str, desc.note);
465 strcpy(str, tmp);
467 return string(str);
472 ////////////////////////////////////////////////////////////////////////
474 calf_plugins::plugin_registry &calf_plugins::plugin_registry::instance()
476 static calf_plugins::plugin_registry registry;
477 return registry;
480 const plugin_metadata_iface *calf_plugins::plugin_registry::get_by_uri(const char *plugin_uri)
482 static const char prefix[] = "http://calf.sourceforge.net/plugins/";
483 if (strncmp(plugin_uri, prefix, sizeof(prefix) - 1))
484 return NULL;
485 const char *label = plugin_uri + sizeof(prefix) - 1;
486 for (unsigned int i = 0; i < plugins.size(); i++)
488 if (!strcmp(plugins[i]->get_plugin_info().label, label))
489 return plugins[i];
491 return NULL;
494 const plugin_metadata_iface *calf_plugins::plugin_registry::get_by_id(const char *id, bool case_sensitive)
496 typedef int (*comparator)(const char *, const char *);
497 comparator comp = case_sensitive ? strcmp : strcasecmp;
498 for (unsigned int i = 0; i < plugins.size(); i++)
500 if (!comp(plugins[i]->get_id(), id))
501 return plugins[i];
503 return NULL;
506 ////////////////////////////////////////////////////////////////////////
508 bool calf_plugins::parse_table_key(const char *key, const char *prefix, bool &is_rows, int &row, int &column)
510 is_rows = false;
511 row = -1;
512 column = -1;
513 if (0 != strncmp(key, prefix, strlen(prefix)))
514 return false;
516 key += strlen(prefix);
518 if (!strcmp(key, "rows"))
520 is_rows = true;
521 return true;
524 const char *comma = strchr(key, ',');
525 if (comma)
527 row = atoi(string(key, comma - key).c_str());
528 column = atoi(comma + 1);
529 return true;
532 printf("Unknown key %s under prefix %s", key, prefix);
534 return false;
537 ////////////////////////////////////////////////////////////////////////
539 const char *mod_mapping_names[] = { "0..1", "-1..1", "-1..0", "x^2", "2x^2-1", "ASqr", "ASqrBip", "Para", NULL };
541 mod_matrix_metadata::mod_matrix_metadata(unsigned int _rows, const char **_src_names, const char **_dest_names)
542 : mod_src_names(_src_names)
543 , mod_dest_names(_dest_names)
544 , matrix_rows(_rows)
546 table_column_info tci[6] = {
547 { "Source", TCT_ENUM, 0, 0, 0, mod_src_names },
548 { "Mapping", TCT_ENUM, 0, 0, 0, mod_mapping_names },
549 { "Modulator", TCT_ENUM, 0, 0, 0, mod_src_names },
550 { "Amount", TCT_FLOAT, 0, 1, 1, NULL},
551 { "Destination", TCT_ENUM, 0, 0, 0, mod_dest_names },
552 { NULL }
554 assert(sizeof(table_columns) == sizeof(tci));
555 memcpy(table_columns, tci, sizeof(table_columns));
558 const table_column_info *mod_matrix_metadata::get_table_columns() const
560 return table_columns;
563 uint32_t mod_matrix_metadata::get_table_rows() const
565 return matrix_rows;
568 /// Return a list of configure variables used by the modulation matrix
569 void mod_matrix_metadata::get_configure_vars(std::vector<std::string> &names) const
571 for (unsigned int i = 0; i < matrix_rows; ++i)
573 for (int j = 0; j < 5; j++)
575 char buf[40];
576 snprintf(buf, sizeof(buf), "mod_matrix:%d,%d", i, j);
577 names.push_back(buf);
584 ////////////////////////////////////////////////////////////////////////
586 #if USE_EXEC_GUI
588 table_via_configure::table_via_configure()
590 rows = 0;
593 table_via_configure::~table_via_configure()
597 #endif