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
25 #include <calf/giface.h>
26 #include <calf/utils.h>
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))
46 key
+= sizeof(automation_key_prefix
) - 1;
47 const char *totoken
= strstr(key
, "_to_");
50 string
from_ctl(key
, totoken
- key
);
51 for (size_t i
= 0; i
< from_ctl
.length(); i
++)
53 if (!isdigit(from_ctl
[i
]))
56 from_controller
= (uint32_t)atoi(from_ctl
.c_str());
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
);
67 return new automation_range(minv
, maxv
, i
);
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
:
83 value
= min
+ (max
- min
) * value01
;
86 value
= min
+ (max
- min
) * value01
* value01
;
89 value
= min
* pow(double(max
/ min
), value01
);
92 if (value01
< 0.00001)
95 float rmin
= std::max(1.0f
/ 1024.0f
, min
);
96 value
= rmin
* pow(double(max
/ rmin
), value01
);
99 case PF_SCALE_LOG_INF
:
101 if (value01
> (step
- 1.0) / step
)
102 value
= FAKE_INFINITY
;
104 value
= min
* pow(double(max
/ min
), value01
* step
/ (step
- 1.0));
107 switch(flags
& PF_TYPEMASK
)
114 value
= (int)(value
+ 0.5);
116 value
= (int)(value
- 0.5);
122 double parameter_properties::to_01(float value
) const
124 switch(flags
& PF_SCALEMASK
)
126 case PF_SCALE_DEFAULT
:
127 case PF_SCALE_LINEAR
:
130 return double(value
- min
) / (max
- min
);
132 return sqrt(double(value
- min
) / (max
- min
));
135 return log((double)value
) / log((double)max
/ min
);
136 case PF_SCALE_LOG_INF
:
137 if (IS_FAKE_INFINITY(value
))
141 return (step
- 1.0) * log((double)value
) / (step
* log((double)max
/ min
));
143 if (value
< 1.0 / 1024.0) // new bottom limit - 60 dB
145 double rmin
= std::max(1.0f
/ 1024.0f
, min
);
147 return log((double)value
) / log(max
/ rmin
);
151 float parameter_properties::get_increment() const
153 float increment
= 0.01;
155 increment
= 1.0 / (step
- 1);
157 if (step
> 0 && step
< 1)
160 if ((flags
& PF_TYPEMASK
) != PF_FLOAT
)
161 increment
= 1.0 / (max
- min
);
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
169 const char *suf
[] = { "", "k", "m", "g", "t", "p", "e" };
171 sprintf(buf
, format
, 0.f
, "");
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
]);
181 int parameter_properties::get_char_count() const
183 if ((flags
& PF_SCALEMASK
) == PF_SCALE_PERC
)
185 if ((flags
& PF_SCALEMASK
) == PF_SCALE_GAIN
) {
188 snprintf(buf
, sizeof(buf
), "%0.0f dB", 6.0 * log(min
) / log(2));
190 snprintf(buf
, sizeof(buf
), "%0.0f dB", 6.0 * log(max
) / log(2));
191 len
= std::max(len
, strlen(buf
)) + 2;
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
204 if ((flags
& PF_SCALEMASK
) == PF_SCALE_PERC
) {
205 snprintf(buf
, sizeof(buf
), "%0.2f%%", 100.0 * value
);
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
));
215 switch(flags
& PF_TYPEMASK
)
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);
227 switch (flags
& PF_DIGITMASK
) {
229 snprintf(buf
, sizeof(buf
), "%.0f", value
);
232 snprintf(buf
, sizeof(buf
), "%.1f", value
);
235 snprintf(buf
, sizeof(buf
), "%.2f", value
);
238 snprintf(buf
, sizeof(buf
), "%.3f", value
);
242 snprintf(buf
, sizeof(buf
), "%g", value
);
247 snprintf(buf
, sizeof(buf
), "%g", value
);
251 if ((flags
& PF_SCALEMASK
) == PF_SCALE_LOG_INF
&& IS_FAKE_INFINITY(value
))
252 snprintf(buf
, sizeof(buf
), "∞"); // XXXKF change to utf-8 infinity
254 switch(flags
& PF_UNITMASK
) {
255 case PF_UNIT_DB
: return string(buf
) + " dB";
256 case PF_UNIT_HZ
: return string(buf
) + " Hz";
257 case PF_UNIT_SEC
: return string(buf
) + " s";
258 case PF_UNIT_MSEC
: return string(buf
) + " ms";
259 case PF_UNIT_CENTS
: return string(buf
) + " ct";
260 case PF_UNIT_SEMITONES
: return string(buf
) + "#";
261 case PF_UNIT_BPM
: return string(buf
) + " bpm";
262 case PF_UNIT_RPM
: return string(buf
) + " rpm";
263 case PF_UNIT_DEG
: return string(buf
) + " deg";
264 case PF_UNIT_SAMPLES
: return string(buf
) + " smpl";
267 static const char *notes
= "C C#D D#E F F#G G#A A#B ";
268 int note
= (int)value
;
269 if (note
< 0 || note
> 127)
271 return string(notes
+ 2 * (note
% 12), 2) + i2s(note
/ 12 - 2);
278 float parameter_properties::string_to_value(const char* string
) const
280 float value
= atof(string
);
281 if ((flags
& PF_SCALEMASK
) == PF_SCALE_PERC
) {
282 return value
/ 100.0;
284 if ((flags
& PF_SCALEMASK
) == PF_SCALE_GAIN
) {
285 return dsp::dB2amp(value
);
290 ////////////////////////////////////////////////////////////////////////
292 void calf_plugins::plugin_ctl_iface::clear_preset() {
293 int param_count
= get_metadata_iface()->get_param_count();
294 for (int i
= 0; i
< param_count
; i
++)
296 const parameter_properties
&pp
= *get_metadata_iface()->get_param_props(i
);
297 set_param_value(i
, pp
.def_value
);
300 get_metadata_iface()->get_configure_vars(vars
);
301 for (size_t i
= 0; i
< vars
.size(); ++i
)
302 configure(vars
[i
].c_str(), NULL
);
305 const char *calf_plugins::load_gui_xml(const std::string
&plugin_id
)
308 return strdup(calf_utils::load_file((std::string(PKGLIBDIR
) + "/gui-" + plugin_id
+ ".xml").c_str()).c_str());
310 catch(file_exception e
)
316 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
)
320 static const double dash
[] = {2.0};
327 if (subindex
== 9) legend
= "100 Hz";
328 if (subindex
== 18) legend
= "1 kHz";
329 if (subindex
== 27) legend
= "10 kHz";
331 float freq
= subindex_to_freq(subindex
);
332 pos
= log(freq
/ 20.0) / log(1000);
334 if (!legend
.empty()) {
335 context
->set_source_rgba(0, 0, 0, 0.1);
336 context
->set_dash(dash
, 0);
338 context
->set_source_rgba(0, 0, 0, 0.1);
339 context
->set_dash(dash
, 1);
350 float gain
= 64.0 / (1 << subindex
);
351 pos
= dB_grid(gain
, res
, ofs
);
356 if (!(subindex
& 1)) {
357 std::stringstream ss
;
358 ss
<< (36 - 6 * subindex
) << " dB";
361 if (!legend
.empty() and subindex
!= 6) {
362 context
->set_source_rgba(0, 0, 0, 0.1);
363 context
->set_dash(dash
, 0);
364 } else if (subindex
!= 6) {
365 context
->set_source_rgba(0, 0, 0, 0.1);
366 context
->set_dash(dash
, 1);
368 context
->set_dash(dash
, 0);
374 void calf_plugins::set_channel_color(cairo_iface
*context
, int channel
, float alpha
)
377 context
->set_source_rgba(0.25, 0.10, 0.0, alpha
);
379 context
->set_source_rgba(0.05, 0.25, 0.0, alpha
);
381 void calf_plugins::set_channel_dash(cairo_iface
*context
, int channel
)
383 double dash
[] = {8,2};
408 context
->set_dash(dash
, length
);
411 void calf_plugins::draw_cairo_label(cairo_iface
*context
, const char *label
, float x
, float y
, int pos
, float margin
, float align
)
413 context
->draw_label(label
, x
, y
, pos
, margin
, align
);
415 ////////////////////////////////////////////////////////////////////////
417 bool frequency_response_line_graph::get_graph(int index
, int subindex
, int phase
, float *data
, int points
, cairo_iface
*context
, int *mode
) const
419 if (phase
or subindex
)
421 return ::get_graph(*this, subindex
, data
, points
);
423 bool frequency_response_line_graph::get_gridline(int index
, int subindex
, int phase
, float &pos
, bool &vertical
, std::string
&legend
, cairo_iface
*context
) const
427 return get_freq_gridline(subindex
, pos
, vertical
, legend
, context
, true);
429 bool frequency_response_line_graph::get_layers(int index
, int generation
, unsigned int &layers
) const
431 redraw_graph
= redraw_graph
|| !generation
;
432 layers
= (generation
? LG_NONE
: LG_CACHE_GRID
) | (redraw_graph
? LG_CACHE_GRAPH
: LG_NONE
);
433 bool r
= redraw_graph
;
434 redraw_graph
= false;
437 std::string
frequency_response_line_graph::get_crosshair_label(int x
, int y
, int sx
, int sy
, cairo_iface
*context
) const
439 return frequency_crosshair_label(x
, y
, sx
, sy
, context
);
441 std::string
calf_plugins::frequency_crosshair_label(int x
, int y
, int sx
, int sy
, cairo_iface
*context
, double res
, double ofs
)
443 std::stringstream ss
;
445 float freq
= exp((float(x
) / float(sx
)) * log(1000)) * 20.0;
446 float db
= dsp::amp2dB(dB_grid_inv(-1 + (2 - float(y
) / float(sy
) * 2), res
, ofs
));
447 dsp::note_desc desc
= dsp::hz_to_note(freq
, 440);
448 sprintf(str
, "%.2f Hz\n %.2f dB\nNote: %s%+d\nCents: %+.2f\nMIDI: %d", freq
, db
, desc
.name
, desc
.octave
, desc
.cents
, desc
.note
);
454 ////////////////////////////////////////////////////////////////////////
456 calf_plugins::plugin_registry
&calf_plugins::plugin_registry::instance()
458 static calf_plugins::plugin_registry registry
;
462 const plugin_metadata_iface
*calf_plugins::plugin_registry::get_by_uri(const char *plugin_uri
)
464 static const char prefix
[] = "http://calf.sourceforge.net/plugins/";
465 if (strncmp(plugin_uri
, prefix
, sizeof(prefix
) - 1))
467 const char *label
= plugin_uri
+ sizeof(prefix
) - 1;
468 for (unsigned int i
= 0; i
< plugins
.size(); i
++)
470 if (!strcmp(plugins
[i
]->get_plugin_info().label
, label
))
476 const plugin_metadata_iface
*calf_plugins::plugin_registry::get_by_id(const char *id
, bool case_sensitive
)
478 typedef int (*comparator
)(const char *, const char *);
479 comparator comp
= case_sensitive
? strcmp
: strcasecmp
;
480 for (unsigned int i
= 0; i
< plugins
.size(); i
++)
482 if (!comp(plugins
[i
]->get_id(), id
))
488 ////////////////////////////////////////////////////////////////////////
490 bool calf_plugins::parse_table_key(const char *key
, const char *prefix
, bool &is_rows
, int &row
, int &column
)
495 if (0 != strncmp(key
, prefix
, strlen(prefix
)))
498 key
+= strlen(prefix
);
500 if (!strcmp(key
, "rows"))
506 const char *comma
= strchr(key
, ',');
509 row
= atoi(string(key
, comma
- key
).c_str());
510 column
= atoi(comma
+ 1);
514 printf("Unknown key %s under prefix %s", key
, prefix
);
519 ////////////////////////////////////////////////////////////////////////
521 const char *mod_mapping_names
[] = { "0..1", "-1..1", "-1..0", "x^2", "2x^2-1", "ASqr", "ASqrBip", "Para", NULL
};
523 mod_matrix_metadata::mod_matrix_metadata(unsigned int _rows
, const char **_src_names
, const char **_dest_names
)
524 : mod_src_names(_src_names
)
525 , mod_dest_names(_dest_names
)
528 table_column_info tci
[6] = {
529 { "Source", TCT_ENUM
, 0, 0, 0, mod_src_names
},
530 { "Mapping", TCT_ENUM
, 0, 0, 0, mod_mapping_names
},
531 { "Modulator", TCT_ENUM
, 0, 0, 0, mod_src_names
},
532 { "Amount", TCT_FLOAT
, 0, 1, 1, NULL
},
533 { "Destination", TCT_ENUM
, 0, 0, 0, mod_dest_names
},
536 assert(sizeof(table_columns
) == sizeof(tci
));
537 memcpy(table_columns
, tci
, sizeof(table_columns
));
540 const table_column_info
*mod_matrix_metadata::get_table_columns() const
542 return table_columns
;
545 uint32_t mod_matrix_metadata::get_table_rows() const
550 /// Return a list of configure variables used by the modulation matrix
551 void mod_matrix_metadata::get_configure_vars(std::vector
<std::string
> &names
) const
553 for (unsigned int i
= 0; i
< matrix_rows
; ++i
)
555 for (int j
= 0; j
< 5; j
++)
558 snprintf(buf
, sizeof(buf
), "mod_matrix:%d,%d", i
, j
);
559 names
.push_back(buf
);
566 ////////////////////////////////////////////////////////////////////////
570 table_via_configure::table_via_configure()
575 table_via_configure::~table_via_configure()