+ GUI: remove more unused control pointers
[calf.git] / src / makerdf.cpp
blob7b30a0559783a9150ed72e5f1fb1237c0ea6044e
1 /* Calf DSP Library
2 * RDF file generator for LADSPA plugins.
3 * Copyright (C) 2007 Krzysztof Foltman
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
20 #include <getopt.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <config.h>
24 #include <calf/giface.h>
25 #include <calf/plugininfo.h>
26 #include <calf/utils.h>
27 #if USE_LV2
28 #include <calf/lv2_contexts.h>
29 #include <calf/lv2_event.h>
30 #include <calf/lv2_uri_map.h>
31 #endif
33 using namespace std;
34 using namespace calf_utils;
35 using namespace calf_plugins;
37 static struct option long_options[] = {
38 {"help", 0, 0, 'h'},
39 {"version", 0, 0, 'v'},
40 {"mode", 0, 0, 'm'},
41 {"path", 0, 0, 'p'},
42 {0,0,0,0},
45 #if USE_LADSPA
47 static std::string unit_to_string(parameter_properties &props)
49 uint32_t flags = props.flags & PF_UNITMASK;
51 switch(flags) {
52 case PF_UNIT_DB:
53 return "ladspa:hasUnit=\"&ladspa;dB\" ";
54 case PF_UNIT_COEF:
55 return "ladspa:hasUnit=\"&ladspa;coef\" ";
56 case PF_UNIT_HZ:
57 return "ladspa:hasUnit=\"&ladspa;Hz\" ";
58 case PF_UNIT_SEC:
59 return "ladspa:hasUnit=\"&ladspa;seconds\" ";
60 case PF_UNIT_MSEC:
61 return "ladspa:hasUnit=\"&ladspa;milliseconds\" ";
62 default:
63 return string();
67 static std::string scale_to_string(parameter_properties &props)
69 if ((props.flags & PF_TYPEMASK) != PF_ENUM) {
70 return "/";
72 string tmp = "><ladspa:hasScale><ladspa:Scale>\n";
73 for (int i = (int)props.min; i <= (int)props.max; i++) {
74 tmp += " <ladspa:hasPoint><ladspa:Point rdf:value=\""+i2s(i)+"\" ladspa:hasLabel=\""+props.choices[(int)(i - props.min)]+"\" /></ladspa:hasPoint>\n";
76 return tmp+" </ladspa:Scale></ladspa:hasScale></ladspa:InputControlPort";
79 std::string generate_ladspa_rdf(const ladspa_plugin_info &info, parameter_properties *params, const char *param_names[], unsigned int count,
80 unsigned int ctl_ofs)
82 string rdf;
83 string plugin_id = "&ladspa;" + i2s(info.unique_id);
84 string plugin_type = string(info.plugin_type);
86 rdf += " <ladspa:" + plugin_type + " rdf:about=\"" + plugin_id + "\">\n";
87 rdf += " <dc:creator>" + xml_escape(info.maker) + "</dc:creator>\n";
88 rdf += " <dc:title>" + xml_escape(info.name) + "</dc:title>\n";
90 for (unsigned int i = 0; i < count; i++) {
91 rdf +=
92 " <ladspa:hasPort>\n"
93 " <ladspa:" + string(params[i].flags & PF_PROP_OUTPUT ? "Output" : "Input")
94 + "ControlPort rdf:about=\"" + plugin_id + "."+i2s(ctl_ofs + i)+"\" "
95 + unit_to_string(params[i]) +
96 "ladspa:hasLabel=\"" + params[i].short_name + "\" "
97 + scale_to_string(params[i]) +
98 ">\n"
99 " </ladspa:hasPort>\n";
101 rdf += " <ladspa:hasSetting>\n"
102 " <ladspa:Default>\n";
103 for (unsigned int i = 0; i < count; i++) {
104 rdf +=
105 " <ladspa:hasPortValue>\n"
106 " <ladspa:PortValue rdf:value=\"" + f2s(params[i].def_value) + "\">\n"
107 " <ladspa:forPort rdf:resource=\"" + plugin_id + "." + i2s(ctl_ofs + i) + "\"/>\n"
108 " </ladspa:PortValue>\n"
109 " </ladspa:hasPortValue>\n";
111 rdf += " </ladspa:Default>\n"
112 " </ladspa:hasSetting>\n";
114 rdf += " </ladspa:" + plugin_type + ">\n";
115 return rdf;
118 void make_rdf()
120 string rdf;
121 rdf =
122 "<?xml version='1.0' encoding='ISO-8859-1'?>\n"
123 "<!DOCTYPE rdf:RDF [\n"
124 " <!ENTITY rdf 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'>\n"
125 " <!ENTITY rdfs 'http://www.w3.org/2000/01/rdf-schema#'>\n"
126 " <!ENTITY dc 'http://purl.org/dc/elements/1.1/'>\n"
127 " <!ENTITY ladspa 'http://ladspa.org/ontology#'>\n"
128 "]>\n";
130 rdf += "<rdf:RDF xmlns:rdf=\"&rdf;\" xmlns:rdfs=\"&rdfs;\" xmlns:dc=\"&dc;\" xmlns:ladspa=\"&ladspa;\">\n";
132 vector<calf_plugins::plugin_metadata_iface *> plugins;
133 calf_plugins::get_all_plugins(plugins);
134 for (unsigned int i = 0; i < plugins.size(); i++)
136 plugin_metadata_iface *p = plugins[i];
137 if (!p->requires_midi()) {
138 rdf += generate_ladspa_rdf(p->get_plugin_info(), p->get_param_props(0), p->get_port_names(), p->get_param_count(), p->get_param_port_offset());
140 delete p;
142 plugins.clear();
143 rdf += "</rdf:RDF>\n";
145 printf("%s\n", rdf.c_str());
147 #endif
149 #if USE_LV2
150 static void add_port(string &ports, const char *symbol, const char *name, const char *direction, int pidx, const char *type = "lv2:AudioPort", bool optional = false)
152 stringstream ss;
153 const char *ind = " ";
156 if (ports != "") ports += " , ";
157 ss << "[\n";
158 if (direction) ss << ind << "a lv2:" << direction << "Port ;\n";
159 ss << ind << "a " << type << " ;\n";
160 ss << ind << "lv2:index " << pidx << " ;\n";
161 ss << ind << "lv2:symbol \"" << symbol << "\" ;\n";
162 ss << ind << "lv2:name \"" << name << "\" ;\n";
163 if (optional)
164 ss << ind << "lv2:portProperty lv2:connectionOptional ;\n";
165 if (!strcmp(type, "lv2ev:EventPort")) {
166 ss << ind << "lv2ev:supportsEvent lv2midi:MidiEvent ;\n";
167 // XXXKF add a correct timestamp type here
168 ss << ind << "lv2ev:supportsTimestamp <lv2ev:TimeStamp> ;\n";
170 if (!strcmp(symbol, "in_l"))
171 ss << ind << "pg:membership [ pg:group <#stereoIn>; pg:role pg:leftChannel ] ;" << endl;
172 else
173 if (!strcmp(symbol, "in_r"))
174 ss << ind << "pg:membership [ pg:group <#stereoIn>; pg:role pg:rightChannel ] ;" << endl;
175 else
176 if (!strcmp(symbol, "out_l"))
177 ss << ind << "pg:membership [ pg:group <#stereoOut>; pg:role pg:leftChannel ] ;" << endl;
178 else
179 if (!strcmp(symbol, "out_r"))
180 ss << ind << "pg:membership [ pg:group <#stereoOut>; pg:role pg:rightChannel ] ;" << endl;
181 ss << " ]";
182 ports += ss.str();
185 static const char *units[] = {
186 "ue:db",
187 "ue:coef",
188 "ue:hz",
189 "ue:s",
190 "ue:ms",
191 "ue:cent", // - ask SWH (or maybe ue2: and write the extension by myself)
192 "ue:semitone12TET", // - ask SWH
193 "ue:bpm",
194 "ue:degree",
195 "ue:midiNote", // question to SWH: the extension says midiNode, but that must be a typo
196 NULL // rotations per minute
199 //////////////// To all haters: calm down, I'll rewrite it to use the new interface one day
201 static void add_ctl_port(string &ports, parameter_properties &pp, int pidx, plugin_metadata_iface *pmi, int param)
203 stringstream ss;
204 const char *ind = " ";
206 parameter_flags type = (parameter_flags)(pp.flags & PF_TYPEMASK);
207 uint8_t unit = (pp.flags & PF_UNITMASK) >> 24;
209 if (ports != "") ports += " , ";
210 ss << "[\n";
211 if (pp.flags & PF_PROP_OUTPUT)
212 ss << ind << "a lv2:OutputPort ;\n";
213 else
214 ss << ind << "a lv2:InputPort ;\n";
215 if (type == PF_STRING)
216 ss << ind << "a strport:StringPort ;\n";
217 else
218 ss << ind << "a lv2:ControlPort ;\n";
219 ss << ind << "lv2:index " << pidx << " ;\n";
220 ss << ind << "lv2:symbol \"" << pp.short_name << "\" ;\n";
221 ss << ind << "lv2:name \"" << pp.name << "\" ;\n";
222 if ((pp.flags & PF_CTLMASK) == PF_CTL_BUTTON)
223 ss << ind << "lv2:portProperty epp:trigger ;\n";
224 if (!(pp.flags & PF_PROP_NOBOUNDS))
225 ss << ind << "lv2:portProperty epp:hasStrictBounds ;\n";
226 if (pp.flags & PF_PROP_EXPENSIVE)
227 ss << ind << "lv2:portProperty epp:expensive ;\n";
228 if (pp.flags & PF_PROP_OPTIONAL)
229 ss << ind << "lv2:portProperty lv2:connectionOptional ;\n";
230 if (pmi->is_noisy(param))
231 ss << ind << "lv2:portProperty epp:causesArtifacts ;\n";
232 if (!pmi->is_cv(param))
233 ss << ind << "lv2:portProperty epp:notAutomatic ;\n";
234 if (pp.flags & PF_PROP_OUTPUT_GAIN)
235 ss << ind << "lv2:portProperty epp:outputGain ;\n";
236 if (pp.flags & PF_PROP_MSGCONTEXT)
237 ss << ind << "lv2ctx:context lv2ctx:MessageContext ;\n";
238 if (type == PF_STRING)
240 ss << ind << "strport:default \"\"\"" << pp.choices[0] << "\"\"\" ;\n";
242 else if (type == PF_BOOL)
243 ss << ind << "lv2:portProperty lv2:toggled ;\n";
244 else if (type == PF_ENUM)
246 ss << ind << "lv2:portProperty lv2:integer ;\n";
247 for (int i = (int)pp.min; i <= (int)pp.max; i++)
248 ss << ind << "lv2:scalePoint [ rdfs:label \"" << pp.choices[i - (int)pp.min] << "\"; rdf:value " << i <<" ] ;\n";
250 else if (type == PF_INT || type == PF_ENUM_MULTI)
251 ss << ind << "lv2:portProperty lv2:integer ;\n";
252 else if ((pp.flags & PF_SCALEMASK) == PF_SCALE_LOG)
253 ss << ind << "lv2:portProperty epp:logarithmic ;\n";
254 ss << showpoint;
255 if (type != PF_STRING)
257 ss << ind << "lv2:default " << pp.def_value << " ;\n";
258 ss << ind << "lv2:minimum " << pp.min << " ;\n";
259 ss << ind << "lv2:maximum " << pp.max << " ;\n";
260 if (pp.step > 1)
261 ss << ind << "epp:rangeSteps " << pp.step << " ;\n";
262 if (unit > 0 && unit < (sizeof(units) / sizeof(char *)) && units[unit - 1] != NULL)
263 ss << ind << "ue:unit " << units[unit - 1] << " ;\n";
266 // for now I assume that the only tempo passed is the tempo the plugin should operate with
267 // this may change as more complex plugins are added
268 if (unit == (PF_UNIT_BPM >> 24))
269 ss << ind << "lv2:portProperty epp:reportsBpm ;\n";
271 ss << " ]";
272 ports += ss.str();
275 struct lv2_port_base {
276 int index;
277 std::string symbol, name, extras, microname;
278 bool is_input;
280 virtual std::string to_string() = 0;
281 virtual ~lv2_port_base() {}
282 void to_stream_base(stringstream &ss, string ind, string port_class)
284 ss << ind << (is_input ? "a lv2:InputPort ;\n" : "a lv2:OutputPort ;\n");
285 ss << ind << "a " << port_class << " ;\n";
286 ss << ind << "lv2:index " << index << " ;\n";
287 ss << ind << "lv2:symbol \"" << symbol << "\" ;\n";
288 ss << ind << "lv2:name \"" << name << "\" ;\n";
289 if (!extras.empty()) {
290 ss << calf_utils::indent(extras, ind);
292 if (microname != "N/A")
293 ss << ind << "<http://lv2plug.in/ns/dev/tiny-name> \"" << microname << "\" ;\n";
297 struct lv2_audio_port_base: public lv2_port_base
299 std::string to_string() {
300 stringstream ss;
301 const char *ind = " ";
302 ss << "[\n";
303 to_stream_base(ss, ind, "lv2:AudioPort");
304 ss << " ]\n";
306 return ss.str();
310 struct lv2_event_port_base: public lv2_port_base
312 std::string to_string() {
313 stringstream ss;
314 const char *ind = " ";
315 ss << "[\n";
316 to_stream_base(ss, ind, "lv2ev:EventPort");
317 ss << " ]\n";
319 return ss.str();
323 template<class base_iface, class base_data>
324 struct lv2_port_impl: public base_iface, public base_data
326 lv2_port_impl(int _index, const std::string &_symbol, const std::string &_name, const std::string &_microname)
328 this->index = _index;
329 this->symbol = _symbol;
330 this->name = _name;
331 this->microname = _microname;
332 this->is_input = true;
334 /// Called if it's an input port
335 virtual base_iface& input() { this->is_input = true; return *this; }
336 /// Called if it's an output port
337 virtual base_iface& output() { this->is_input = false; return *this; }
338 virtual base_iface& lv2_ttl(const std::string &text) { this->extras += text + "\n"; return *this; }
341 struct lv2_control_port_base: public lv2_port_base
343 bool is_log, is_toggle, is_trigger, is_integer;
344 double min, max, def_value;
345 bool has_min, has_max;
347 lv2_control_port_base()
349 has_min = has_max = is_log = is_toggle = is_trigger = is_integer = false;
350 def_value = 0.f;
354 typedef lv2_port_impl<plain_port_info_iface, lv2_audio_port_base> lv2_audio_port_info;
355 typedef lv2_port_impl<plain_port_info_iface, lv2_event_port_base> lv2_event_port_info;
357 struct lv2_control_port_info: public lv2_port_impl<control_port_info_iface, lv2_control_port_base>
359 lv2_control_port_info(int _index, const std::string &_symbol, const std::string &_name, const std::string &_microname)
360 : lv2_port_impl<control_port_info_iface, lv2_control_port_base>(_index, _symbol, _name, _microname)
363 /// Called to mark the port as using linear range [from, to]
364 virtual control_port_info_iface& lin_range(double from, double to) { min = from, max = to, has_min = true, has_max = true, is_log = false; return *this; }
365 /// Called to mark the port as using log range [from, to]
366 virtual control_port_info_iface& log_range(double from, double to) { min = from, max = to, has_min = true, has_max = true, is_log = true; return *this; }
367 virtual control_port_info_iface& toggle() { is_toggle = true; return *this; }
368 virtual control_port_info_iface& trigger() { is_trigger = true; return *this; }
369 virtual control_port_info_iface& integer() { is_integer = true; return *this; }
370 virtual control_port_info_iface& lv2_ttl(const std::string &text) { extras += text + "\n"; return *this; }
371 std::string to_string() {
372 stringstream ss;
373 const char *ind = " ";
374 ss << "[\n";
375 to_stream_base(ss, ind, "lv2:ControlPort");
376 if (is_toggle)
377 ss << ind << "lv2:portProperty lv2:toggled ;\n";
378 if (is_integer)
379 ss << ind << "lv2:portProperty lv2:integer ;\n";
380 if (is_input)
381 ss << ind << "lv2:default " << def_value << " ;\n";
382 if (has_min)
383 ss << ind << "lv2:minimum " << min << " ;\n";
384 if (has_max)
385 ss << ind << "lv2:maximum " << max << " ;\n";
386 ss << " ]\n";
388 return ss.str();
392 struct lv2_plugin_info: public plugin_info_iface
394 /// Plugin id
395 std::string id;
396 /// Plugin name
397 std::string name;
398 /// Plugin label (short name)
399 std::string label;
400 /// Plugin class (category)
401 std::string category;
402 /// Plugin micro-name
403 std::string microname;
404 /// Extra declarations for LV2
405 std::string extras;
406 /// Vector of ports
407 vector<lv2_port_base *> ports;
408 /// Set plugin names (ID, name and label)
409 virtual void names(const std::string &_name, const std::string &_label, const std::string &_category, const std::string &_microname) {
410 name = _name;
411 label = _label;
412 category = _category;
413 microname = _microname;
415 /// Add an audio port (returns a sink which accepts further description)
416 virtual plain_port_info_iface &audio_port(const std::string &id, const std::string &name, const std::string &_microname) {
417 lv2_audio_port_info *port = new lv2_audio_port_info(ports.size(), id, name, _microname);
418 ports.push_back(port);
419 return *port;
421 /// Add an eventport (returns a sink which accepts further description)
422 virtual plain_port_info_iface &event_port(const std::string &id, const std::string &name, const std::string &_microname) {
423 lv2_event_port_info *port = new lv2_event_port_info(ports.size(), id, name, _microname);
424 ports.push_back(port);
425 return *port;
427 /// Add a control port (returns a sink which accepts further description)
428 virtual control_port_info_iface &control_port(const std::string &id, const std::string &name, double def_value, const std::string &_microname) {
429 lv2_control_port_info *port = new lv2_control_port_info(ports.size(), id, name, _microname);
430 port->def_value = def_value;
431 ports.push_back(port);
432 return *port;
434 /// Add extra TTL to plugin declaration
435 virtual void lv2_ttl(const std::string &text) { this->extras += " " + text + "\n"; }
436 /// Called after plugin has reported all the information
437 virtual void finalize() {
441 struct lv2_plugin_list: public plugin_list_info_iface, public vector<lv2_plugin_info *>
443 virtual plugin_info_iface &plugin(const std::string &id) {
444 lv2_plugin_info *pi = new lv2_plugin_info;
445 pi->id = id;
446 push_back(pi);
447 return *pi;
451 void make_ttl(string path_prefix)
453 if (path_prefix.empty())
455 fprintf(stderr, "Path parameter is required for TTL mode\n");
456 exit(1);
458 string header;
460 header =
461 "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
462 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
463 "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
464 "@prefix dc: <http://dublincore.org/documents/dcmi-namespace/> .\n"
465 "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"
466 "@prefix uiext: <http://lv2plug.in/ns/extensions/ui#> .\n"
467 "@prefix lv2ev: <http://lv2plug.in/ns/ext/event#> .\n"
468 "@prefix lv2midi: <http://lv2plug.in/ns/ext/midi#> .\n"
469 "@prefix lv2ctx: <http://lv2plug.in/ns/dev/contexts#> .\n"
470 "@prefix strport: <http://lv2plug.in/ns/dev/string-port#> .\n"
471 "@prefix pg: <http://ll-plugins.nongnu.org/lv2/ext/portgroups#> .\n"
472 "@prefix ue: <http://lv2plug.in/ns/extensions/units#> .\n"
473 "@prefix epp: <http://lv2plug.in/ns/dev/extportinfo#> .\n"
474 "@prefix kf: <http://foltman.com/ns/> .\n"
475 "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"
476 "@prefix poly: <http://lv2plug.in/ns/dev/polymorphic-port#> .\n"
478 "\n"
481 vector<plugin_metadata_iface *> plugins;
482 calf_plugins::get_all_plugins(plugins);
484 map<string, string> classes;
486 const char *ptypes[] = {
487 "Flanger", "Reverb", "Generator", "Instrument", "Oscillator",
488 "Utility", "Converter", "Analyser", "Mixer", "Simulator",
489 "Delay", "Modulator", "Phaser", "Chorus", "Filter",
490 "Lowpass", "Highpass", "Bandpass", "Comb", "Allpass",
491 "Amplifier", "Distortion", "Waveshaper", "Dynamics", "Compressor",
492 "Expander", "Limiter", "Gate", NULL
495 for(const char **p = ptypes; *p; p++) {
496 string name = string(*p) + "Plugin";
497 classes[name] = "lv2:" + name;
499 classes["SynthesizerPlugin"] = "lv2:InstrumentPlugin";
501 string plugin_uri_prefix = "http://calf.sourceforge.net/plugins/";
503 string gui_header;
505 #if USE_LV2_GUI
506 string gui_uri = "<http://calf.sourceforge.net/plugins/gui/gtk2-gui>";
507 gui_header = gui_uri + "\n"
508 " a uiext:GtkUI ;\n"
509 " uiext:binary <calflv2gui.so> ;\n"
510 " uiext:requiredFeature uiext:makeResident .\n"
511 " \n"
512 #ifdef ENABLE_EXPERIMENTAL
513 "<http://calf.sourceforge.net/small_plugins/gui/gtk2-gui>\n"
514 " a uiext:GtkUI ;\n"
515 " uiext:binary <calflv2gui.so> ;\n"
516 " uiext:requiredFeature uiext:makeResident .\n"
517 " \n"
518 #endif
520 #endif
522 map<string, string> id_to_label;
524 for (unsigned int i = 0; i < plugins.size(); i++) {
525 plugin_metadata_iface *pi = plugins[i];
526 const ladspa_plugin_info &lpi = pi->get_plugin_info();
527 id_to_label[pi->get_id()] = pi->get_label();
528 string uri = string("<" + plugin_uri_prefix) + string(lpi.label) + ">";
529 string ttl;
530 ttl = "@prefix : " + uri + " .\n" + header + gui_header;
532 #if USE_LV2_GUI
533 for (int j = 0; j < pi->get_param_count(); j++)
535 parameter_properties &props = *pi->get_param_props(j);
536 if (props.flags & PF_PROP_OUTPUT)
537 ttl += gui_uri + " uiext:portNotification [\n uiext:plugin " + uri + " ;\n uiext:portIndex " + i2s(j) + "\n] .\n\n";
539 #endif
541 ttl += uri + " a lv2:Plugin ;\n";
543 if (classes.count(lpi.plugin_type))
544 ttl += " a " + classes[lpi.plugin_type]+" ;\n";
547 ttl += " doap:name \""+string(lpi.name)+"\" ;\n";
548 ttl += " doap:maintainer [ foaf:name \""+string(lpi.maker)+"\" ; ] ;\n";
550 #if USE_LV2_GUI
551 ttl += " uiext:ui <http://calf.sourceforge.net/plugins/gui/gtk2-gui> ;\n";
552 ttl += " lv2:optionalFeature <http://lv2plug.in/ns/ext/instance-access> ;\n";
553 ttl += " lv2:optionalFeature <http://lv2plug.in/ns/ext/data-access> ;\n";
554 #endif
556 ttl += " doap:license <http://usefulinc.com/doap/licenses/lgpl> ;\n";
557 ttl += " dc:replaces <ladspa:" + i2s(lpi.unique_id) + "> ;\n";
558 // XXXKF not really optional for now, to be honest
559 ttl += " lv2:optionalFeature epp:supportsStrictBounds ;\n";
560 if (pi->is_rt_capable())
561 ttl += " lv2:optionalFeature lv2:hardRtCapable ;\n";
562 if (pi->get_midi())
564 if (pi->requires_midi()) {
565 ttl += " lv2:requiredFeature <" LV2_EVENT_URI "> ;\n";
566 ttl += " lv2:requiredFeature <" LV2_URI_MAP_URI "> ;\n";
568 else {
569 ttl += " lv2:optionalFeature <" LV2_EVENT_URI "> ;\n";
570 ttl += " lv2:optionalFeature <" LV2_URI_MAP_URI "> ;\n";
574 if (pi->requires_message_context())
576 ttl += " lv2:requiredFeature <http://lv2plug.in/ns/dev/contexts> ;\n";
577 ttl += " lv2:requiredFeature <" LV2_CONTEXT_MESSAGE "> ;\n";
578 ttl += " lv2ctx:requiredContext lv2ctx:MessageContext ;\n";
580 if (pi->requires_string_ports())
581 ttl += " lv2:requiredFeature <http://lv2plug.in/ns/dev/string-port#StringTransfer> ;\n";
583 string ports = "";
584 int pn = 0;
585 const char *in_names[] = { "in_l", "in_r" };
586 const char *out_names[] = { "out_l", "out_r" };
587 for (int i = 0; i < pi->get_input_count(); i++)
588 add_port(ports, in_names[i], in_names[i], "Input", pn++);
589 for (int i = 0; i < pi->get_output_count(); i++)
590 add_port(ports, out_names[i], out_names[i], "Output", pn++);
591 for (int i = 0; i < pi->get_param_count(); i++)
592 add_ctl_port(ports, *pi->get_param_props(i), pn++, pi, i);
593 if (pi->get_midi()) {
594 add_port(ports, "event_in", "Event", "Input", pn++, "lv2ev:EventPort", true);
596 if (!ports.empty())
597 ttl += " lv2:port " + ports + "\n";
598 ttl += ".\n\n";
600 FILE *f = fopen((path_prefix+string(lpi.label)+".ttl").c_str(), "w");
601 fprintf(f, "%s\n", ttl.c_str());
602 fclose(f);
604 lv2_plugin_list lpl;
605 calf_plugins::get_all_small_plugins(&lpl);
606 for (unsigned int i = 0; i < lpl.size(); i++)
608 lv2_plugin_info *pi = lpl[i];
609 // Copy-pasted code is the root of all evil, I know!
610 string uri = string("<http://calf.sourceforge.net/small_plugins/") + string(pi->id) + ">";
611 string ttl;
612 ttl = "@prefix : " + uri + " .\n" + header + gui_header;
614 ttl += uri + " a lv2:Plugin ;\n";
616 if (!pi->category.empty())
617 ttl += " a " + pi->category+" ;\n";
619 ttl += " doap:name \""+string(pi->label)+"\" ;\n";
620 ttl += " doap:license <http://usefulinc.com/doap/licenses/lgpl> ;\n";
621 if (!pi->microname.empty())
622 ttl += " <http://lv2plug.in/ns/dev/tiny-name> \"" + pi->microname + "\" ;\n";
623 ttl += pi->extras;
625 if (!pi->ports.empty())
627 ttl += " lv2:port ";
628 for (unsigned int i = 0; i < pi->ports.size(); i++)
630 if (i)
631 ttl += " ,\n ";
632 ttl += pi->ports[i]->to_string();
634 ttl += ".\n\n";
636 FILE *f = fopen((path_prefix+string(pi->id)+".ttl").c_str(), "w");
637 fprintf(f, "%s\n", ttl.c_str());
638 fclose(f);
641 // Generate a manifest
643 string ttl =
644 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
645 "@prefix lv2p: <http://lv2plug.in/ns/dev/presets#> .\n"
646 "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
647 "@prefix kf: <http://foltman.com/ns/> .\n"
648 "\n"
649 "kf:BooleanPlugin a rdfs:Class ; rdfs:subClassOf lv2:UtilityPlugin ;\n"
650 " rdfs:label \"Boolean-oriented\" ;\n rdfs:comment \"Modules heavily inspired by digital electronics (gates, flip-flops, etc.)\" .\n"
651 "kf:MathOperatorPlugin a rdfs:Class ; rdfs:subClassOf lv2:UtilityPlugin ;\n"
652 " rdfs:label \"Math operators\" ;\n rdfs:comment \"Mathematical operators and utility functions\" .\n"
653 "kf:IntegerPlugin a rdfs:Class ; rdfs:subClassOf lv2:UtilityPlugin ;\n"
654 " rdfs:label \"Integer-oriented\" ;\n rdfs:comment \"Operations on integer values (counters, multiplexers, etc.)\" .\n"
655 "kf:MIDIPlugin a rdfs:Class ; rdfs:subClassOf lv2:UtilityPlugin ;\n"
656 " rdfs:label \"MIDI\" ;\n rdfs:comment \"Operations on MIDI streams (filters, transposers, mappers, etc.)\" .\n"
657 "\n"
660 string presets_ttl =
661 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
662 "@prefix lv2p: <http://lv2plug.in/ns/dev/presets#> .\n"
663 "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
664 "@prefix dc: <http://dublincore.org/documents/dcmi-namespace/> .\n"
665 "\n"
668 for (unsigned int i = 0; i < plugins.size(); i++)
669 ttl += string("<" + plugin_uri_prefix)
670 + string(plugins[i]->get_plugin_info().label)
671 + "> a lv2:Plugin ;\n lv2:binary <calf.so> ; rdfs:seeAlso <" + string(plugins[i]->get_plugin_info().label) + ".ttl> , <presets.ttl> .\n";
673 for (unsigned int i = 0; i < lpl.size(); i++)
674 ttl += string("<http://calf.sourceforge.net/small_plugins/")
675 + string(lpl[i]->id)
676 + "> a lv2:Plugin ;\n lv2:binary <calf.so> ; rdfs:seeAlso <" + string(lpl[i]->id) + ".ttl> .\n";
678 calf_plugins::get_builtin_presets().load_defaults(true);
679 calf_plugins::preset_vector &factory_presets = calf_plugins::get_builtin_presets().presets;
681 ttl += "\n";
683 for (unsigned int i = 0; i < factory_presets.size(); i++)
685 plugin_preset &pr = factory_presets[i];
686 map<string, string>::iterator ilm = id_to_label.find(pr.plugin);
687 if (ilm == id_to_label.end())
688 continue;
689 string uri = "<http://calf.sourceforge.net/factory_presets#"
690 + pr.plugin + "_" + pr.get_safe_name()
691 + ">";
692 ttl += string("<" + plugin_uri_prefix + ilm->second + "> lv2p:hasPreset\n " + uri + " .\n");
694 presets_ttl += uri +
695 " a lv2p:Preset ;\n"
696 " dc:title \"" + pr.name + "\" ;\n"
697 " lv2p:appliesTo <" + plugin_uri_prefix + ilm->second + "> ;\n"
698 " lv2:port \n"
701 unsigned int count = min(pr.param_names.size(), pr.values.size());
702 for (unsigned int j = 0; j < count; j++)
704 presets_ttl += " [ lv2:symbol \"" + pr.param_names[j] + "\" ; lv2p:value " + ff2s(pr.values[j]) + " ] ";
705 if (j < count - 1)
706 presets_ttl += ',';
707 presets_ttl += '\n';
709 presets_ttl += ".\n\n";
711 FILE *f = fopen((path_prefix+"manifest.ttl").c_str(), "w");
712 fprintf(f, "%s\n", ttl.c_str());
713 fclose(f);
714 f = fopen((path_prefix+"presets.ttl").c_str(), "w");
715 fprintf(f, "%s\n", presets_ttl.c_str());
716 fclose(f);
719 #else
720 void make_ttl(string tmp)
722 fprintf(stderr, "LV2 not supported.\n");
725 #endif
727 void make_gui(string path_prefix)
729 if (path_prefix.empty())
731 fprintf(stderr, "Path parameter is required for GUI mode\n");
732 exit(1);
734 vector<plugin_metadata_iface *> plugins;
735 calf_plugins::get_all_plugins(plugins);
736 path_prefix += "/gui-";
737 for (unsigned int i = 0; i < plugins.size(); i++)
739 plugin_metadata_iface *pi = plugins[i];
741 stringstream xml;
742 int graphs = 0;
743 for (int j = 0; j < pi->get_param_count(); j++)
745 parameter_properties &props = *pi->get_param_props(j);
746 if (props.flags & PF_PROP_GRAPH)
747 graphs++;
749 xml << "<table rows=\"" << pi->get_param_count() << "\" cols=\"" << (graphs ? "4" : "3") << "\">\n";
750 for (int j = 0; j < pi->get_param_count(); j++)
752 if (j)
753 xml << "\n <!-- -->\n\n";
754 parameter_properties &props = *pi->get_param_props(j);
755 string expand_x = "expand-x=\"1\" ";
756 string fill_x = "fill-x=\"1\" ";
757 string shrink_x = "shrink-x=\"1\" ";
758 string pad_x = "pad-x=\"10\" ";
759 string attach_x = "attach-x=\"1\" attach-w=\"2\" ";
760 string attach_y = "attach-y=\"" + i2s(j) + "\" ";
761 string param = "param=\"" + string(props.short_name) + "\" ";
762 string label = " <align attach-x=\"0\" " + attach_y + " " + " align-x=\"1\"><label " + param + " /></align>\n";
763 string value = " <value " + param + " " + "attach-x=\"2\" " + attach_y + pad_x + "/>\n";
764 string attach_xv = "attach-x=\"1\" attach-w=\"1\" ";
765 string ctl;
766 if ((props.flags & PF_TYPEMASK) == PF_ENUM &&
767 (props.flags & PF_CTLMASK) == PF_CTL_COMBO)
769 ctl = " <combo " + attach_x + attach_y + param + expand_x + pad_x + " />\n";
771 else if ((props.flags & PF_TYPEMASK) == PF_STRING)
773 ctl = " <entry " + attach_x + attach_y + "key=\"" + props.short_name + "\" " + expand_x + pad_x + " />\n";
775 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
776 (props.flags & PF_CTLMASK) == PF_CTL_TOGGLE)
778 ctl = " <align " + attach_x + attach_y + expand_x + fill_x + pad_x + "><toggle " + param + "/></align>\n";
780 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
781 (props.flags & PF_CTLMASK) == PF_CTL_BUTTON)
783 ctl = " <button attach-x=\"0\" attach-w=\"3\" " + expand_x + attach_y + param + pad_x + "/>\n";
784 label.clear();
786 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
787 (props.flags & PF_CTLMASK) == PF_CTL_LED)
789 ctl = " <led " + attach_x + attach_y + param + shrink_x + pad_x + " />\n";
791 else if ((props.flags & PF_CTLMASK) == PF_CTL_METER)
793 if (props.flags & PF_CTLO_LABEL) {
794 attach_x = attach_xv;
795 pad_x.clear();
797 if (props.flags & PF_CTLO_REVERSE)
798 ctl = " <vumeter " + attach_x + attach_y + expand_x + fill_x + param + pad_x + "mode=\"2\"/>\n";
799 else
800 ctl = " <vumeter " + attach_x + attach_y + expand_x + fill_x + param + pad_x + "/>\n";
801 if (props.flags & PF_CTLO_LABEL)
802 ctl += value;
804 else if ((props.flags & PF_CTLMASK) != PF_CTL_FADER)
806 if ((props.flags & PF_UNITMASK) == PF_UNIT_DEG)
807 ctl = " <knob " + attach_xv + attach_y + shrink_x + param + " type=\"3\"/>\n";
808 else
809 ctl = " <knob " + attach_xv + attach_y + shrink_x + param + " />\n";
810 ctl += value;
812 else
814 ctl += " <hscale " + param + " " + attach_x + attach_y + " pad-x=\"10\"/>";
816 if (!label.empty())
817 xml << label;
818 if (!ctl.empty())
819 xml << ctl;
821 if (graphs)
823 xml << " <if cond=\"directlink\">" << endl;
824 xml << " <vbox expand-x=\"1\" fill-x=\"1\" attach-x=\"3\" attach-y=\"0\" attach-h=\"" << pi->get_param_count() << "\">" << endl;
825 for (int j = 0; j < pi->get_param_count(); j++)
827 parameter_properties &props = *pi->get_param_props(j);
828 if (props.flags & PF_PROP_GRAPH)
830 xml << " <line-graph refresh=\"1\" width=\"160\" param=\"" << props.short_name << "\"/>\n" << endl;
833 xml << " </vbox>" << endl;
834 xml << " </if>" << endl;
836 xml << "</table>\n";
837 FILE *f = fopen((path_prefix+string(pi->get_id())+".xml").c_str(), "w");
838 fprintf(f, "%s\n", xml.str().c_str());
839 fclose(f);
843 int main(int argc, char *argv[])
845 string mode = "rdf";
846 string path_prefix;
847 while(1) {
848 int option_index;
849 int c = getopt_long(argc, argv, "hvm:p:", long_options, &option_index);
850 if (c == -1)
851 break;
852 switch(c) {
853 case 'h':
854 case '?':
855 printf("LADSPA RDF / LV2 TTL / XML GUI generator for Calf plugin pack\nSyntax: %s [--help] [--version] [--mode rdf|ttl|gui] [--path <path>]\n", argv[0]);
856 return 0;
857 case 'v':
858 printf("%s\n", PACKAGE_STRING);
859 return 0;
860 case 'm':
861 mode = optarg;
862 if (mode != "rdf" && mode != "ttl" && mode != "gui") {
863 fprintf(stderr, "calfmakerdf: Invalid mode %s\n", optarg);
864 return 1;
866 break;
867 case 'p':
868 path_prefix = optarg;
869 break;
872 if (false)
875 #if USE_LADSPA
876 else if (mode == "rdf")
877 make_rdf();
878 #endif
879 #if USE_LV2
880 else if (mode == "ttl")
881 make_ttl(path_prefix);
882 #endif
883 else if (mode == "gui")
884 make_gui(path_prefix);
885 else
887 fprintf(stderr, "calfmakerdf: Mode %s unsupported in this version\n", optarg);
888 return 1;
890 return 0;