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 value
= round(value
* 1000) / 1000;
228 switch (flags
& PF_DIGITMASK
) {
230 snprintf(buf
, sizeof(buf
), "%.0f", value
);
233 snprintf(buf
, sizeof(buf
), "%.1f", value
);
236 snprintf(buf
, sizeof(buf
), "%.2f", value
);
239 snprintf(buf
, sizeof(buf
), "%.3f", value
);
243 snprintf(buf
, sizeof(buf
), "%g", value
);
248 snprintf(buf
, sizeof(buf
), "%g", value
);
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";
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)
272 return string(notes
+ 2 * (note
% 12), 2) + i2s(note
/ 12 - 2);
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
);
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
);
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
)
309 return strdup(calf_utils::load_file((std::string(PKGLIBDIR
) + "/gui-" + plugin_id
+ ".xml").c_str()).c_str());
311 catch(file_exception e
)
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
)
321 static const double dash
[] = {2.0};
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);
339 context
->set_source_rgba(0, 0, 0, 0.1);
340 context
->set_dash(dash
, 1);
351 float gain
= 64.0 / (1 << subindex
);
352 pos
= dB_grid(gain
, res
, ofs
);
357 if (!(subindex
& 1)) {
358 std::stringstream ss
;
359 ss
<< (36 - 6 * subindex
) << " dB";
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);
369 context
->set_dash(dash
, 0);
375 void calf_plugins::set_channel_color(cairo_iface
*context
, int channel
, float alpha
)
378 context
->set_source_rgba(0.25, 0.10, 0.0, alpha
);
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};
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
)
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
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;
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
;
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
);
452 sprintf(tmp
, "%s\n%.2f dB", str
, db
);
456 sprintf(tmp
, "%s\nNote: %s%+d", str
, desc
.name
, desc
.octave
);
460 sprintf(tmp
, "%s\nCents: %+.2f", str
, desc
.cents
);
464 sprintf(tmp
, "%s\nMIDI: %d", str
, desc
.note
);
472 ////////////////////////////////////////////////////////////////////////
474 calf_plugins::plugin_registry
&calf_plugins::plugin_registry::instance()
476 static calf_plugins::plugin_registry 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))
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
))
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
))
506 ////////////////////////////////////////////////////////////////////////
508 bool calf_plugins::parse_table_key(const char *key
, const char *prefix
, bool &is_rows
, int &row
, int &column
)
513 if (0 != strncmp(key
, prefix
, strlen(prefix
)))
516 key
+= strlen(prefix
);
518 if (!strcmp(key
, "rows"))
524 const char *comma
= strchr(key
, ',');
527 row
= atoi(string(key
, comma
- key
).c_str());
528 column
= atoi(comma
+ 1);
532 printf("Unknown key %s under prefix %s", key
, prefix
);
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
)
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
},
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
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
++)
576 snprintf(buf
, sizeof(buf
), "mod_matrix:%d,%d", i
, j
);
577 names
.push_back(buf
);
584 ////////////////////////////////////////////////////////////////////////
588 table_via_configure::table_via_configure()
593 table_via_configure::~table_via_configure()