Rack: Menu with custom icons
[calf.git] / src / giface.cpp
blob093b24558c1d3938e9f8c552c43c3278d72908d9
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 <calf/giface.h>
24 #include <calf/utils.h>
26 using namespace std;
27 using namespace calf_utils;
28 using namespace calf_plugins;
30 static const char automation_key_prefix[] = "automation_v1_";
32 void automation_range::send_configure(const plugin_metadata_iface *metadata, uint32_t from_controller, send_configure_iface *sci)
34 std::stringstream ss1, ss2;
35 ss1 << automation_key_prefix << from_controller << "_to_" << metadata->get_param_props(param_no)->short_name;
36 ss2 << min_value << " " << max_value;
37 sci->send_configure(ss1.str().c_str(), ss2.str().c_str());
40 automation_range *automation_range::new_from_configure(const plugin_metadata_iface *metadata, const char *key, const char *value, uint32_t &from_controller)
42 if (0 != strncmp(key, automation_key_prefix, sizeof(automation_key_prefix) - 1))
43 return NULL;
44 key += sizeof(automation_key_prefix) - 1;
45 const char *totoken = strstr(key, "_to_");
46 if (!totoken)
47 return NULL;
48 string from_ctl(key, totoken - key);
49 for (size_t i = 0; i < from_ctl.length(); i++)
51 if (!isdigit(from_ctl[i]))
52 return NULL;
54 from_controller = (uint32_t)atoi(from_ctl.c_str());
55 key = totoken + 4;
57 size_t pcount = metadata->get_param_count();
58 for (size_t i = 0; i < pcount; ++i) {
59 const parameter_properties *props = metadata->get_param_props(i);
60 if (!strcmp(key, props->short_name))
62 std::stringstream ss(value);
63 double minv, maxv;
64 ss >> minv >> maxv;
65 return new automation_range(minv, maxv, i);
69 return NULL;
72 float parameter_properties::from_01(double value01) const
74 double value = dsp::clip(value01, 0., 1.);
75 switch(flags & PF_SCALEMASK)
77 case PF_SCALE_DEFAULT:
78 case PF_SCALE_LINEAR:
79 case PF_SCALE_PERC:
80 default:
81 value = min + (max - min) * value01;
82 break;
83 case PF_SCALE_QUAD:
84 value = min + (max - min) * value01 * value01;
85 break;
86 case PF_SCALE_LOG:
87 value = min * pow(double(max / min), value01);
88 break;
89 case PF_SCALE_GAIN:
90 if (value01 < 0.00001)
91 value = min;
92 else {
93 float rmin = std::max(1.0f / 1024.0f, min);
94 value = rmin * pow(double(max / rmin), value01);
96 break;
97 case PF_SCALE_LOG_INF:
98 assert(step);
99 if (value01 > (step - 1.0) / step)
100 value = FAKE_INFINITY;
101 else
102 value = min * pow(double(max / min), value01 * step / (step - 1.0));
103 break;
105 switch(flags & PF_TYPEMASK)
107 case PF_INT:
108 case PF_BOOL:
109 case PF_ENUM:
110 case PF_ENUM_MULTI:
111 if (value > 0)
112 value = (int)(value + 0.5);
113 else
114 value = (int)(value - 0.5);
115 break;
117 return value;
120 double parameter_properties::to_01(float value) const
122 switch(flags & PF_SCALEMASK)
124 case PF_SCALE_DEFAULT:
125 case PF_SCALE_LINEAR:
126 case PF_SCALE_PERC:
127 default:
128 return double(value - min) / (max - min);
129 case PF_SCALE_QUAD:
130 return sqrt(double(value - min) / (max - min));
131 case PF_SCALE_LOG:
132 value /= min;
133 return log((double)value) / log((double)max / min);
134 case PF_SCALE_LOG_INF:
135 if (IS_FAKE_INFINITY(value))
136 return max;
137 value /= min;
138 assert(step);
139 return (step - 1.0) * log((double)value) / (step * log((double)max / min));
140 case PF_SCALE_GAIN:
141 if (value < 1.0 / 1024.0) // new bottom limit - 60 dB
142 return 0;
143 double rmin = std::max(1.0f / 1024.0f, min);
144 value /= rmin;
145 return log((double)value) / log(max / rmin);
149 float parameter_properties::get_increment() const
151 float increment = 0.01;
152 if (step > 1)
153 increment = 1.0 / (step - 1);
154 else
155 if (step > 0 && step < 1)
156 increment = step;
157 else
158 if ((flags & PF_TYPEMASK) != PF_FLOAT)
159 increment = 1.0 / (max - min);
160 return increment;
163 int parameter_properties::get_char_count() const
165 if ((flags & PF_SCALEMASK) == PF_SCALE_PERC)
166 return 6;
167 if ((flags & PF_SCALEMASK) == PF_SCALE_GAIN) {
168 char buf[256];
169 size_t len = 0;
170 snprintf(buf, sizeof(buf), "%0.0f dB", 6.0 * log(min) / log(2));
171 len = strlen(buf);
172 snprintf(buf, sizeof(buf), "%0.0f dB", 6.0 * log(max) / log(2));
173 len = std::max(len, strlen(buf)) + 2;
174 return (int)len;
176 return std::max(to_string(min).length(), std::max(to_string(max).length(), to_string(min + (max-min) * 0.987654).length()));
179 std::string parameter_properties::to_string(float value) const
181 char buf[32];
182 if ((flags & PF_SCALEMASK) == PF_SCALE_PERC) {
183 snprintf(buf, sizeof(buf), "%0.f%%", 100.0 * value);
184 return string(buf);
186 if ((flags & PF_SCALEMASK) == PF_SCALE_GAIN) {
187 if (value < 1.0 / 1024.0) // new bottom limit - 60 dB
188 return "-inf dB"; // XXXKF change to utf-8 infinity
189 snprintf(buf, sizeof(buf), "%0.1f dB", dsp::amp2dB(value));
190 return string(buf);
192 switch(flags & PF_TYPEMASK)
194 case PF_INT:
195 case PF_BOOL:
196 case PF_ENUM:
197 case PF_ENUM_MULTI:
198 value = (int)value;
199 break;
202 if ((flags & PF_SCALEMASK) == PF_SCALE_LOG_INF && IS_FAKE_INFINITY(value))
203 snprintf(buf, sizeof(buf), "+inf"); // XXXKF change to utf-8 infinity
204 else
205 snprintf(buf, sizeof(buf), "%g", value);
206 switch(flags & PF_UNITMASK) {
207 case PF_UNIT_DB: return string(buf) + " dB";
208 case PF_UNIT_HZ: return string(buf) + " Hz";
209 case PF_UNIT_SEC: return string(buf) + " s";
210 case PF_UNIT_MSEC: return string(buf) + " ms";
211 case PF_UNIT_CENTS: return string(buf) + " ct";
212 case PF_UNIT_SEMITONES: return string(buf) + "#";
213 case PF_UNIT_BPM: return string(buf) + " bpm";
214 case PF_UNIT_RPM: return string(buf) + " rpm";
215 case PF_UNIT_DEG: return string(buf) + " deg";
216 case PF_UNIT_SAMPLES: return string(buf) + " smpl";
217 case PF_UNIT_NOTE:
219 static const char *notes = "C C#D D#E F F#G G#A A#B ";
220 int note = (int)value;
221 if (note < 0 || note > 127)
222 return "---";
223 return string(notes + 2 * (note % 12), 2) + i2s(note / 12 - 2);
227 return string(buf);
230 float parameter_properties::string_to_value(const char* string) const
232 float value = atof(string);
233 if ((flags & PF_SCALEMASK) == PF_SCALE_PERC) {
234 return value / 100.0;
236 if ((flags & PF_SCALEMASK) == PF_SCALE_GAIN) {
237 return dsp::dB2amp(value);
239 return value;
242 ////////////////////////////////////////////////////////////////////////
244 void calf_plugins::plugin_ctl_iface::clear_preset() {
245 int param_count = get_metadata_iface()->get_param_count();
246 for (int i = 0; i < param_count; i++)
248 const parameter_properties &pp = *get_metadata_iface()->get_param_props(i);
249 set_param_value(i, pp.def_value);
251 vector<string> vars;
252 get_metadata_iface()->get_configure_vars(vars);
253 for (size_t i = 0; i < vars.size(); ++i)
254 configure(vars[i].c_str(), NULL);
257 const char *calf_plugins::load_gui_xml(const std::string &plugin_id)
259 try {
260 return strdup(calf_utils::load_file((std::string(PKGLIBDIR) + "/gui-" + plugin_id + ".xml").c_str()).c_str());
262 catch(file_exception e)
264 return NULL;
268 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)
270 if (subindex < 0)
271 return false;
272 static const double dash[] = {2.0};
273 // frequency grid
274 if (use_frequencies)
276 if (subindex < 28)
278 vertical = true;
279 if (subindex == 9) legend = "100 Hz";
280 if (subindex == 18) legend = "1 kHz";
281 if (subindex == 27) legend = "10 kHz";
283 float freq = subindex_to_freq(subindex);
284 pos = log(freq / 20.0) / log(1000);
286 if (!legend.empty()) {
287 context->set_source_rgba(0, 0, 0, 0.1);
288 context->set_dash(dash, 0);
289 } else {
290 context->set_source_rgba(0, 0, 0, 0.1);
291 context->set_dash(dash, 1);
293 return true;
295 subindex -= 28;
298 if (subindex >= 32)
299 return false;
301 // gain/dB grid
302 float gain = 64.0 / (1 << subindex);
303 pos = dB_grid(gain, res, ofs);
305 if (pos < -1)
306 return false;
308 if (!(subindex & 1)) {
309 std::stringstream ss;
310 ss << (36 - 6 * subindex) << " dB";
311 legend = ss.str();
313 if (!legend.empty() and subindex != 6) {
314 context->set_source_rgba(0, 0, 0, 0.1);
315 context->set_dash(dash, 0);
316 } else if (subindex != 6) {
317 context->set_source_rgba(0, 0, 0, 0.1);
318 context->set_dash(dash, 1);
319 } else {
320 context->set_dash(dash, 0);
322 vertical = false;
323 return true;
326 void calf_plugins::set_channel_color(cairo_iface *context, int channel, float alpha)
328 if (channel & 1)
329 context->set_source_rgba(0.25, 0.10, 0.0, alpha);
330 else
331 context->set_source_rgba(0.05, 0.25, 0.0, alpha);
333 void calf_plugins::set_channel_dash(cairo_iface *context, int channel)
335 double dash[] = {8,2};
336 int length = 2;
337 switch (channel) {
338 case 0:
339 default:
340 dash[0] = 6;
341 dash[1] = 1.5;
342 length = 2;
343 break;
344 case 1:
345 dash[0] = 4.5;
346 dash[1] = 1.5;
347 length = 2;
348 break;
349 case 2:
350 dash[0] = 3;
351 dash[1] = 1.5;
352 length = 2;
353 break;
354 case 3:
355 dash[0] = 1.5;
356 dash[1] = 1.5;
357 length = 2;
358 break;
360 context->set_dash(dash, length);
363 void calf_plugins::draw_cairo_label(cairo_iface *context, const char *label, float x, float y, int pos, float margin, float align)
365 context->draw_label(label, x, y, pos, margin, align);
367 ////////////////////////////////////////////////////////////////////////
369 bool frequency_response_line_graph::get_graph(int index, int subindex, int phase, float *data, int points, cairo_iface *context, int *mode) const
371 if (phase or subindex)
372 return false;
373 return ::get_graph(*this, subindex, data, points);
375 bool frequency_response_line_graph::get_gridline(int index, int subindex, int phase, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
377 if (phase)
378 return false;
379 return get_freq_gridline(subindex, pos, vertical, legend, context, true);
381 bool frequency_response_line_graph::get_layers(int index, int generation, unsigned int &layers) const
383 redraw_graph = redraw_graph || !generation;
384 layers = (generation ? LG_NONE : LG_CACHE_GRID) | (redraw_graph ? LG_CACHE_GRAPH : LG_NONE);
385 bool r = redraw_graph;
386 redraw_graph = false;
387 return r;
389 std::string frequency_response_line_graph::get_crosshair_label(int x, int y, int sx, int sy, cairo_iface *context) const
391 std::stringstream ss;
392 float freq = exp((float(x) / float(sx)) * log(1000)) * 20.0;
393 ss << int(freq) << " Hz";
394 return ss.str();
399 ////////////////////////////////////////////////////////////////////////
401 calf_plugins::plugin_registry &calf_plugins::plugin_registry::instance()
403 static calf_plugins::plugin_registry registry;
404 return registry;
407 const plugin_metadata_iface *calf_plugins::plugin_registry::get_by_uri(const char *plugin_uri)
409 static const char prefix[] = "http://calf.sourceforge.net/plugins/";
410 if (strncmp(plugin_uri, prefix, sizeof(prefix) - 1))
411 return NULL;
412 const char *label = plugin_uri + sizeof(prefix) - 1;
413 for (unsigned int i = 0; i < plugins.size(); i++)
415 if (!strcmp(plugins[i]->get_plugin_info().label, label))
416 return plugins[i];
418 return NULL;
421 const plugin_metadata_iface *calf_plugins::plugin_registry::get_by_id(const char *id, bool case_sensitive)
423 typedef int (*comparator)(const char *, const char *);
424 comparator comp = case_sensitive ? strcmp : strcasecmp;
425 for (unsigned int i = 0; i < plugins.size(); i++)
427 if (!comp(plugins[i]->get_id(), id))
428 return plugins[i];
430 return NULL;
433 ////////////////////////////////////////////////////////////////////////
435 bool calf_plugins::parse_table_key(const char *key, const char *prefix, bool &is_rows, int &row, int &column)
437 is_rows = false;
438 row = -1;
439 column = -1;
440 if (0 != strncmp(key, prefix, strlen(prefix)))
441 return false;
443 key += strlen(prefix);
445 if (!strcmp(key, "rows"))
447 is_rows = true;
448 return true;
451 const char *comma = strchr(key, ',');
452 if (comma)
454 row = atoi(string(key, comma - key).c_str());
455 column = atoi(comma + 1);
456 return true;
459 printf("Unknown key %s under prefix %s", key, prefix);
461 return false;
464 ////////////////////////////////////////////////////////////////////////
466 const char *mod_mapping_names[] = { "0..1", "-1..1", "-1..0", "x^2", "2x^2-1", "ASqr", "ASqrBip", "Para", NULL };
468 mod_matrix_metadata::mod_matrix_metadata(unsigned int _rows, const char **_src_names, const char **_dest_names)
469 : mod_src_names(_src_names)
470 , mod_dest_names(_dest_names)
471 , matrix_rows(_rows)
473 table_column_info tci[6] = {
474 { "Source", TCT_ENUM, 0, 0, 0, mod_src_names },
475 { "Mapping", TCT_ENUM, 0, 0, 0, mod_mapping_names },
476 { "Modulator", TCT_ENUM, 0, 0, 0, mod_src_names },
477 { "Amount", TCT_FLOAT, 0, 1, 1, NULL},
478 { "Destination", TCT_ENUM, 0, 0, 0, mod_dest_names },
479 { NULL }
481 assert(sizeof(table_columns) == sizeof(tci));
482 memcpy(table_columns, tci, sizeof(table_columns));
485 const table_column_info *mod_matrix_metadata::get_table_columns() const
487 return table_columns;
490 uint32_t mod_matrix_metadata::get_table_rows() const
492 return matrix_rows;
495 /// Return a list of configure variables used by the modulation matrix
496 void mod_matrix_metadata::get_configure_vars(std::vector<std::string> &names) const
498 for (unsigned int i = 0; i < matrix_rows; ++i)
500 for (int j = 0; j < 5; j++)
502 char buf[40];
503 snprintf(buf, sizeof(buf), "mod_matrix:%d,%d", i, j);
504 names.push_back(buf);
511 ////////////////////////////////////////////////////////////////////////
513 #if USE_EXEC_GUI
515 table_via_configure::table_via_configure()
517 rows = 0;
520 table_via_configure::~table_via_configure()
524 #endif