+ JACK host: add open/save dialogs (still no implementation of the actual open/save...
[calf.git] / src / makerdf.cpp
blobe239850bc38fc6f6ae3ddccb87f9c79a6752830f
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., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA
20 #include <assert.h>
21 #include <getopt.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <config.h>
25 #include <calf/giface.h>
26 #include <calf/plugininfo.h>
27 #include <calf/utils.h>
28 #if USE_LV2
29 #include <calf/lv2_contexts.h>
30 #include <calf/lv2_event.h>
31 #include <calf/lv2_uri_map.h>
32 #endif
33 #include <set>
35 using namespace std;
36 using namespace calf_utils;
37 using namespace calf_plugins;
39 static struct option long_options[] = {
40 {"help", 0, 0, 'h'},
41 {"version", 0, 0, 'v'},
42 {"mode", 0, 0, 'm'},
43 {"path", 0, 0, 'p'},
44 {0,0,0,0},
47 #if USE_LADSPA
49 static std::string unit_to_string(parameter_properties &props)
51 uint32_t flags = props.flags & PF_UNITMASK;
53 switch(flags) {
54 case PF_UNIT_DB:
55 return "ladspa:hasUnit=\"&ladspa;dB\" ";
56 case PF_UNIT_COEF:
57 return "ladspa:hasUnit=\"&ladspa;coef\" ";
58 case PF_UNIT_HZ:
59 return "ladspa:hasUnit=\"&ladspa;Hz\" ";
60 case PF_UNIT_SEC:
61 return "ladspa:hasUnit=\"&ladspa;seconds\" ";
62 case PF_UNIT_MSEC:
63 return "ladspa:hasUnit=\"&ladspa;milliseconds\" ";
64 default:
65 return string();
69 static std::string scale_to_string(parameter_properties &props)
71 if ((props.flags & PF_TYPEMASK) != PF_ENUM) {
72 return "/";
74 string tmp = "><ladspa:hasScale><ladspa:Scale>\n";
75 for (int i = (int)props.min; i <= (int)props.max; i++) {
76 tmp += " <ladspa:hasPoint><ladspa:Point rdf:value=\""+i2s(i)+"\" ladspa:hasLabel=\""+props.choices[(int)(i - props.min)]+"\" /></ladspa:hasPoint>\n";
78 return tmp+" </ladspa:Scale></ladspa:hasScale></ladspa:InputControlPort";
81 std::string generate_ladspa_rdf(const ladspa_plugin_info &info, parameter_properties *params, const char *param_names[], unsigned int count,
82 unsigned int ctl_ofs)
84 string rdf;
85 string plugin_id = "&ladspa;" + i2s(info.unique_id);
86 string plugin_type = string(info.plugin_type);
88 rdf += " <ladspa:" + plugin_type + " rdf:about=\"" + plugin_id + "\">\n";
89 rdf += " <dc:creator>" + xml_escape(info.maker) + "</dc:creator>\n";
90 rdf += " <dc:title>" + xml_escape(info.name) + "</dc:title>\n";
92 for (unsigned int i = 0; i < count; i++) {
93 rdf +=
94 " <ladspa:hasPort>\n"
95 " <ladspa:" + string(params[i].flags & PF_PROP_OUTPUT ? "Output" : "Input")
96 + "ControlPort rdf:about=\"" + plugin_id + "."+i2s(ctl_ofs + i)+"\" "
97 + unit_to_string(params[i]) +
98 "ladspa:hasLabel=\"" + params[i].short_name + "\" "
99 + scale_to_string(params[i]) +
100 ">\n"
101 " </ladspa:hasPort>\n";
103 rdf += " <ladspa:hasSetting>\n"
104 " <ladspa:Default>\n";
105 for (unsigned int i = 0; i < count; i++) {
106 rdf +=
107 " <ladspa:hasPortValue>\n"
108 " <ladspa:PortValue rdf:value=\"" + f2s(params[i].def_value) + "\">\n"
109 " <ladspa:forPort rdf:resource=\"" + plugin_id + "." + i2s(ctl_ofs + i) + "\"/>\n"
110 " </ladspa:PortValue>\n"
111 " </ladspa:hasPortValue>\n";
113 rdf += " </ladspa:Default>\n"
114 " </ladspa:hasSetting>\n";
116 rdf += " </ladspa:" + plugin_type + ">\n";
117 return rdf;
120 void make_rdf()
122 string rdf;
123 rdf =
124 "<?xml version='1.0' encoding='ISO-8859-1'?>\n"
125 "<!DOCTYPE rdf:RDF [\n"
126 " <!ENTITY rdf 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'>\n"
127 " <!ENTITY rdfs 'http://www.w3.org/2000/01/rdf-schema#'>\n"
128 " <!ENTITY dc 'http://purl.org/dc/elements/1.1/'>\n"
129 " <!ENTITY ladspa 'http://ladspa.org/ontology#'>\n"
130 "]>\n";
132 rdf += "<rdf:RDF xmlns:rdf=\"&rdf;\" xmlns:rdfs=\"&rdfs;\" xmlns:dc=\"&dc;\" xmlns:ladspa=\"&ladspa;\">\n";
134 vector<calf_plugins::plugin_metadata_iface *> plugins;
135 calf_plugins::get_all_plugins(plugins);
136 set<int> used_ids;
137 for (unsigned int i = 0; i < plugins.size(); i++)
139 plugin_metadata_iface *p = plugins[i];
140 const ladspa_plugin_info &info = p->get_plugin_info();
142 if(used_ids.count(info.unique_id))
144 fprintf(stderr, "ERROR: Duplicate ID %d in plugin %s\n", info.unique_id, info.name);
145 assert(0);
147 used_ids.insert(info.unique_id);
149 if (!p->requires_midi()) {
150 rdf += generate_ladspa_rdf(info, p->get_param_props(0), p->get_port_names(), p->get_param_count(), p->get_param_port_offset());
152 delete p;
154 plugins.clear();
155 rdf += "</rdf:RDF>\n";
157 printf("%s\n", rdf.c_str());
159 #endif
161 #if USE_LV2
162 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)
164 stringstream ss;
165 const char *ind = " ";
168 if (ports != "") ports += " , ";
169 ss << "[\n";
170 if (direction) ss << ind << "a lv2:" << direction << "Port ;\n";
171 ss << ind << "a " << type << " ;\n";
172 ss << ind << "lv2:index " << pidx << " ;\n";
173 ss << ind << "lv2:symbol \"" << symbol << "\" ;\n";
174 ss << ind << "lv2:name \"" << name << "\" ;\n";
175 if (optional)
176 ss << ind << "lv2:portProperty lv2:connectionOptional ;\n";
177 if (!strcmp(type, "lv2ev:EventPort")) {
178 ss << ind << "lv2ev:supportsEvent lv2midi:MidiEvent ;\n";
179 // XXXKF add a correct timestamp type here
180 ss << ind << "lv2ev:supportsTimestamp <lv2ev:TimeStamp> ;\n";
182 if (!strcmp(symbol, "in_l"))
183 ss << ind << "pg:membership [ pg:group <#stereoIn>; pg:role pg:leftChannel ] ;" << endl;
184 else
185 if (!strcmp(symbol, "in_r"))
186 ss << ind << "pg:membership [ pg:group <#stereoIn>; pg:role pg:rightChannel ] ;" << endl;
187 else
188 if (!strcmp(symbol, "out_l"))
189 ss << ind << "pg:membership [ pg:group <#stereoOut>; pg:role pg:leftChannel ] ;" << endl;
190 else
191 if (!strcmp(symbol, "out_r"))
192 ss << ind << "pg:membership [ pg:group <#stereoOut>; pg:role pg:rightChannel ] ;" << endl;
193 ss << " ]";
194 ports += ss.str();
197 static const char *units[] = {
198 "ue:db",
199 "ue:coef",
200 "ue:hz",
201 "ue:s",
202 "ue:ms",
203 "ue:cent", // - ask SWH (or maybe ue2: and write the extension by myself)
204 "ue:semitone12TET", // - ask SWH
205 "ue:bpm",
206 "ue:degree",
207 "ue:midiNote", // question to SWH: the extension says midiNode, but that must be a typo
208 NULL // rotations per minute
211 //////////////// To all haters: calm down, I'll rewrite it to use the new interface one day
213 static void add_ctl_port(string &ports, parameter_properties &pp, int pidx, plugin_metadata_iface *pmi, int param)
215 stringstream ss;
216 const char *ind = " ";
218 parameter_flags type = (parameter_flags)(pp.flags & PF_TYPEMASK);
219 uint8_t unit = (pp.flags & PF_UNITMASK) >> 24;
221 if (ports != "") ports += " , ";
222 ss << "[\n";
223 if (pp.flags & PF_PROP_OUTPUT)
224 ss << ind << "a lv2:OutputPort ;\n";
225 else
226 ss << ind << "a lv2:InputPort ;\n";
227 if (type == PF_STRING)
228 ss << ind << "a strport:StringPort ;\n";
229 else
230 ss << ind << "a lv2:ControlPort ;\n";
231 ss << ind << "lv2:index " << pidx << " ;\n";
232 ss << ind << "lv2:symbol \"" << pp.short_name << "\" ;\n";
233 ss << ind << "lv2:name \"" << pp.name << "\" ;\n";
234 if ((pp.flags & PF_CTLMASK) == PF_CTL_BUTTON)
235 ss << ind << "lv2:portProperty epp:trigger ;\n";
236 if (!(pp.flags & PF_PROP_NOBOUNDS))
237 ss << ind << "lv2:portProperty epp:hasStrictBounds ;\n";
238 if (pp.flags & PF_PROP_EXPENSIVE)
239 ss << ind << "lv2:portProperty epp:expensive ;\n";
240 if (pp.flags & PF_PROP_OPTIONAL)
241 ss << ind << "lv2:portProperty lv2:connectionOptional ;\n";
242 if (pmi->is_noisy(param))
243 ss << ind << "lv2:portProperty epp:causesArtifacts ;\n";
244 if (!pmi->is_cv(param))
245 ss << ind << "lv2:portProperty epp:notAutomatic ;\n";
246 if (pp.flags & PF_PROP_OUTPUT_GAIN)
247 ss << ind << "lv2:portProperty epp:outputGain ;\n";
248 if (pp.flags & PF_PROP_MSGCONTEXT)
249 ss << ind << "lv2ctx:context lv2ctx:MessageContext ;\n";
250 if (type == PF_STRING)
252 ss << ind << "strport:default \"\"\"" << pp.choices[0] << "\"\"\" ;\n";
254 else if (type == PF_BOOL)
255 ss << ind << "lv2:portProperty lv2:toggled ;\n";
256 else if (type == PF_ENUM)
258 ss << ind << "lv2:portProperty lv2:integer ;\n";
259 for (int i = (int)pp.min; i <= (int)pp.max; i++)
260 ss << ind << "lv2:scalePoint [ rdfs:label \"" << pp.choices[i - (int)pp.min] << "\"; rdf:value " << i <<" ] ;\n";
262 else if (type == PF_INT || type == PF_ENUM_MULTI)
263 ss << ind << "lv2:portProperty lv2:integer ;\n";
264 else if ((pp.flags & PF_SCALEMASK) == PF_SCALE_LOG)
265 ss << ind << "lv2:portProperty epp:logarithmic ;\n";
266 ss << showpoint;
267 if (type != PF_STRING)
269 if (!(pp.flags & PF_PROP_OUTPUT))
270 ss << ind << "lv2:default " << pp.def_value << " ;\n";
271 ss << ind << "lv2:minimum " << pp.min << " ;\n";
272 ss << ind << "lv2:maximum " << pp.max << " ;\n";
273 if (pp.step > 1)
274 ss << ind << "epp:rangeSteps " << pp.step << " ;\n";
275 if (unit > 0 && unit < (sizeof(units) / sizeof(char *)) && units[unit - 1] != NULL)
276 ss << ind << "ue:unit " << units[unit - 1] << " ;\n";
279 // for now I assume that the only tempo passed is the tempo the plugin should operate with
280 // this may change as more complex plugins are added
281 if (unit == (PF_UNIT_BPM >> 24))
282 ss << ind << "lv2:portProperty epp:reportsBpm ;\n";
284 ss << " ]";
285 ports += ss.str();
288 struct lv2_port_base {
289 int index;
290 std::string symbol, name, extras, microname;
291 bool is_input;
293 virtual std::string to_string() = 0;
294 virtual ~lv2_port_base() {}
295 void to_stream_base(stringstream &ss, string ind, string port_class)
297 ss << ind << (is_input ? "a lv2:InputPort ;\n" : "a lv2:OutputPort ;\n");
298 ss << ind << "a " << port_class << " ;\n";
299 ss << ind << "lv2:index " << index << " ;\n";
300 ss << ind << "lv2:symbol \"" << symbol << "\" ;\n";
301 ss << ind << "lv2:name \"" << name << "\" ;\n";
302 if (!extras.empty()) {
303 ss << calf_utils::indent(extras, ind);
305 if (microname != "N/A")
306 ss << ind << "<http://lv2plug.in/ns/dev/tiny-name> \"" << microname << "\" ;\n";
310 struct lv2_audio_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, "lv2:AudioPort");
317 ss << " ]\n";
319 return ss.str();
323 struct lv2_event_port_base: public lv2_port_base
325 std::string to_string() {
326 stringstream ss;
327 const char *ind = " ";
328 ss << "[\n";
329 to_stream_base(ss, ind, "lv2ev:EventPort");
330 ss << " ]\n";
332 return ss.str();
336 template<class base_iface, class base_data>
337 struct lv2_port_impl: public base_iface, public base_data
339 lv2_port_impl(int _index, const std::string &_symbol, const std::string &_name, const std::string &_microname)
341 this->index = _index;
342 this->symbol = _symbol;
343 this->name = _name;
344 this->microname = _microname;
345 this->is_input = true;
347 /// Called if it's an input port
348 virtual base_iface& input() { this->is_input = true; return *this; }
349 /// Called if it's an output port
350 virtual base_iface& output() { this->is_input = false; return *this; }
351 virtual base_iface& lv2_ttl(const std::string &text) { this->extras += text + "\n"; return *this; }
354 struct lv2_control_port_base: public lv2_port_base
356 bool is_log, is_toggle, is_trigger, is_integer;
357 double min, max, def_value;
358 bool has_min, has_max;
360 lv2_control_port_base()
362 has_min = has_max = is_log = is_toggle = is_trigger = is_integer = false;
363 def_value = 0.f;
367 typedef lv2_port_impl<plain_port_info_iface, lv2_audio_port_base> lv2_audio_port_info;
368 typedef lv2_port_impl<plain_port_info_iface, lv2_event_port_base> lv2_event_port_info;
370 struct lv2_control_port_info: public lv2_port_impl<control_port_info_iface, lv2_control_port_base>
372 lv2_control_port_info(int _index, const std::string &_symbol, const std::string &_name, const std::string &_microname)
373 : lv2_port_impl<control_port_info_iface, lv2_control_port_base>(_index, _symbol, _name, _microname)
376 /// Called to mark the port as using linear range [from, to]
377 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; }
378 /// Called to mark the port as using log range [from, to]
379 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; }
380 virtual control_port_info_iface& toggle() { is_toggle = true; return *this; }
381 virtual control_port_info_iface& trigger() { is_trigger = true; return *this; }
382 virtual control_port_info_iface& integer() { is_integer = true; return *this; }
383 virtual control_port_info_iface& lv2_ttl(const std::string &text) { extras += text + "\n"; return *this; }
384 std::string to_string() {
385 stringstream ss;
386 const char *ind = " ";
387 ss << "[\n";
388 to_stream_base(ss, ind, "lv2:ControlPort");
389 if (is_toggle)
390 ss << ind << "lv2:portProperty lv2:toggled ;\n";
391 if (is_integer)
392 ss << ind << "lv2:portProperty lv2:integer ;\n";
393 if (is_input)
394 ss << ind << "lv2:default " << def_value << " ;\n";
395 if (has_min)
396 ss << ind << "lv2:minimum " << min << " ;\n";
397 if (has_max)
398 ss << ind << "lv2:maximum " << max << " ;\n";
399 ss << " ]\n";
401 return ss.str();
405 struct lv2_plugin_info: public plugin_info_iface
407 /// Plugin id
408 std::string id;
409 /// Plugin name
410 std::string name;
411 /// Plugin label (short name)
412 std::string label;
413 /// Plugin class (category)
414 std::string category;
415 /// Plugin micro-name
416 std::string microname;
417 /// Extra declarations for LV2
418 std::string extras;
419 /// Vector of ports
420 vector<lv2_port_base *> ports;
421 /// Set plugin names (ID, name and label)
422 virtual void names(const std::string &_name, const std::string &_label, const std::string &_category, const std::string &_microname) {
423 name = _name;
424 label = _label;
425 category = _category;
426 microname = _microname;
428 /// Add an audio port (returns a sink which accepts further description)
429 virtual plain_port_info_iface &audio_port(const std::string &id, const std::string &name, const std::string &_microname) {
430 lv2_audio_port_info *port = new lv2_audio_port_info(ports.size(), id, name, _microname);
431 ports.push_back(port);
432 return *port;
434 /// Add an eventport (returns a sink which accepts further description)
435 virtual plain_port_info_iface &event_port(const std::string &id, const std::string &name, const std::string &_microname) {
436 lv2_event_port_info *port = new lv2_event_port_info(ports.size(), id, name, _microname);
437 ports.push_back(port);
438 return *port;
440 /// Add a control port (returns a sink which accepts further description)
441 virtual control_port_info_iface &control_port(const std::string &id, const std::string &name, double def_value, const std::string &_microname) {
442 lv2_control_port_info *port = new lv2_control_port_info(ports.size(), id, name, _microname);
443 port->def_value = def_value;
444 ports.push_back(port);
445 return *port;
447 /// Add extra TTL to plugin declaration
448 virtual void lv2_ttl(const std::string &text) { this->extras += " " + text + "\n"; }
449 /// Called after plugin has reported all the information
450 virtual void finalize() {
454 struct lv2_plugin_list: public plugin_list_info_iface, public vector<lv2_plugin_info *>
456 virtual plugin_info_iface &plugin(const std::string &id) {
457 lv2_plugin_info *pi = new lv2_plugin_info;
458 pi->id = id;
459 push_back(pi);
460 return *pi;
464 void make_ttl(string path_prefix)
466 if (path_prefix.empty())
468 fprintf(stderr, "Path parameter is required for TTL mode\n");
469 exit(1);
471 string header;
473 header =
474 "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
475 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
476 "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
477 "@prefix dc: <http://dublincore.org/documents/dcmi-namespace/> .\n"
478 "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"
479 "@prefix uiext: <http://lv2plug.in/ns/extensions/ui#> .\n"
480 "@prefix lv2ev: <http://lv2plug.in/ns/ext/event#> .\n"
481 "@prefix lv2midi: <http://lv2plug.in/ns/ext/midi#> .\n"
482 "@prefix lv2ctx: <http://lv2plug.in/ns/dev/contexts#> .\n"
483 "@prefix strport: <http://lv2plug.in/ns/dev/string-port#> .\n"
484 "@prefix pg: <http://ll-plugins.nongnu.org/lv2/ext/portgroups#> .\n"
485 "@prefix ue: <http://lv2plug.in/ns/extensions/units#> .\n"
486 "@prefix epp: <http://lv2plug.in/ns/dev/extportinfo#> .\n"
487 "@prefix kf: <http://foltman.com/ns/> .\n"
488 "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"
489 "@prefix poly: <http://lv2plug.in/ns/dev/polymorphic-port#> .\n"
491 "\n"
494 vector<plugin_metadata_iface *> plugins;
495 calf_plugins::get_all_plugins(plugins);
497 map<string, string> classes;
499 const char *ptypes[] = {
500 "Flanger", "Reverb", "Generator", "Instrument", "Oscillator",
501 "Utility", "Converter", "Analyser", "Mixer", "Simulator",
502 "Delay", "Modulator", "Phaser", "Chorus", "Filter",
503 "Lowpass", "Highpass", "Bandpass", "Comb", "Allpass",
504 "Amplifier", "Distortion", "Waveshaper", "Dynamics", "Compressor",
505 "Expander", "Limiter", "Gate", NULL
508 for(const char **p = ptypes; *p; p++) {
509 string name = string(*p) + "Plugin";
510 classes[name] = "lv2:" + name;
512 classes["SynthesizerPlugin"] = "lv2:InstrumentPlugin";
514 string plugin_uri_prefix = "http://calf.sourceforge.net/plugins/";
516 string gui_header;
518 #if USE_LV2_GUI
519 string gui_uri = "<http://calf.sourceforge.net/plugins/gui/gtk2-gui>";
520 gui_header = gui_uri + "\n"
521 " a uiext:GtkUI ;\n"
522 " uiext:binary <calflv2gui.so> ;\n"
523 " uiext:requiredFeature uiext:makeResident .\n"
524 " \n"
525 #ifdef ENABLE_EXPERIMENTAL
526 "<http://calf.sourceforge.net/small_plugins/gui/gtk2-gui>\n"
527 " a uiext:GtkUI ;\n"
528 " uiext:binary <calflv2gui.so> ;\n"
529 " uiext:requiredFeature uiext:makeResident .\n"
530 " \n"
531 #endif
533 #endif
535 map<string, string> id_to_label;
537 for (unsigned int i = 0; i < plugins.size(); i++) {
538 plugin_metadata_iface *pi = plugins[i];
539 const ladspa_plugin_info &lpi = pi->get_plugin_info();
540 id_to_label[pi->get_id()] = pi->get_label();
541 string uri = string("<" + plugin_uri_prefix) + string(lpi.label) + ">";
542 string ttl;
543 ttl = "@prefix : " + uri + " .\n" + header + gui_header;
545 #if USE_LV2_GUI
546 for (int j = 0; j < pi->get_param_count(); j++)
548 parameter_properties &props = *pi->get_param_props(j);
549 if (props.flags & PF_PROP_OUTPUT)
550 ttl += gui_uri + " uiext:portNotification [\n uiext:plugin " + uri + " ;\n uiext:portIndex " + i2s(j) + "\n] .\n\n";
552 #endif
554 ttl += uri + " a lv2:Plugin ;\n";
556 if (classes.count(lpi.plugin_type))
557 ttl += " a " + classes[lpi.plugin_type]+" ;\n";
560 ttl += " doap:name \""+string(lpi.name)+"\" ;\n";
561 ttl += " doap:maintainer [ foaf:name \""+string(lpi.maker)+"\" ; ] ;\n";
563 #if USE_LV2_GUI
564 ttl += " uiext:ui <http://calf.sourceforge.net/plugins/gui/gtk2-gui> ;\n";
565 ttl += " lv2:optionalFeature <http://lv2plug.in/ns/ext/instance-access> ;\n";
566 ttl += " lv2:optionalFeature <http://lv2plug.in/ns/ext/data-access> ;\n";
567 #endif
569 ttl += " doap:license <http://usefulinc.com/doap/licenses/lgpl> ;\n";
570 ttl += " dc:replaces <urn:ladspa:" + i2s(lpi.unique_id) + "> ;\n";
571 // XXXKF not really optional for now, to be honest
572 ttl += " lv2:optionalFeature epp:supportsStrictBounds ;\n";
573 if (pi->is_rt_capable())
574 ttl += " lv2:optionalFeature lv2:hardRtCapable ;\n";
575 if (pi->get_midi())
577 if (pi->requires_midi()) {
578 ttl += " lv2:requiredFeature <" LV2_EVENT_URI "> ;\n";
579 ttl += " lv2:requiredFeature <" LV2_URI_MAP_URI "> ;\n";
581 else {
582 ttl += " lv2:optionalFeature <" LV2_EVENT_URI "> ;\n";
583 ttl += " lv2:optionalFeature <" LV2_URI_MAP_URI "> ;\n";
587 if (pi->requires_message_context())
589 ttl += " lv2:requiredFeature <http://lv2plug.in/ns/dev/contexts> ;\n";
590 ttl += " lv2:requiredFeature <" LV2_CONTEXT_MESSAGE "> ;\n";
591 ttl += " lv2ctx:requiredContext lv2ctx:MessageContext ;\n";
593 if (pi->requires_string_ports())
594 ttl += " lv2:requiredFeature <http://lv2plug.in/ns/dev/string-port#StringTransfer> ;\n";
596 string ports = "";
597 int pn = 0;
598 const char *in_names[] = { "in_l", "in_r" };
599 const char *out_names[] = { "out_l", "out_r" };
600 for (int i = 0; i < pi->get_input_count(); i++)
601 if(i <= pi->get_input_count() - pi->get_inputs_optional() - 1)
602 add_port(ports, in_names[i], in_names[i], "Input", pn++);
603 else
604 add_port(ports, in_names[i], in_names[i], "Input", pn++, "lv2:AudioPort", true);
605 for (int i = 0; i < pi->get_output_count(); i++)
606 if(i <= pi->get_output_count() - pi->get_outputs_optional() - 1)
607 add_port(ports, out_names[i], out_names[i], "Output", pn++);
608 else
609 add_port(ports, out_names[i], out_names[i], "Output", pn++, "lv2:AudioPort", true);
610 for (int i = 0; i < pi->get_param_count(); i++)
611 add_ctl_port(ports, *pi->get_param_props(i), pn++, pi, i);
612 if (pi->get_midi()) {
613 add_port(ports, "event_in", "Event", "Input", pn++, "lv2ev:EventPort", true);
615 if (!ports.empty())
616 ttl += " lv2:port " + ports + "\n";
617 ttl += ".\n\n";
619 FILE *f = fopen((path_prefix+string(lpi.label)+".ttl").c_str(), "w");
620 fprintf(f, "%s\n", ttl.c_str());
621 fclose(f);
623 lv2_plugin_list lpl;
624 calf_plugins::get_all_small_plugins(&lpl);
625 for (unsigned int i = 0; i < lpl.size(); i++)
627 lv2_plugin_info *pi = lpl[i];
628 // Copy-pasted code is the root of all evil, I know!
629 string uri = string("<http://calf.sourceforge.net/small_plugins/") + string(pi->id) + ">";
630 string ttl;
631 ttl = "@prefix : " + uri + " .\n" + header + gui_header;
633 ttl += uri + " a lv2:Plugin ;\n";
635 if (!pi->category.empty())
636 ttl += " a " + pi->category+" ;\n";
638 ttl += " doap:name \""+string(pi->label)+"\" ;\n";
639 ttl += " doap:license <http://usefulinc.com/doap/licenses/lgpl> ;\n";
640 if (!pi->microname.empty())
641 ttl += " <http://lv2plug.in/ns/dev/tiny-name> \"" + pi->microname + "\" ;\n";
642 ttl += pi->extras;
644 if (!pi->ports.empty())
646 ttl += " lv2:port ";
647 for (unsigned int i = 0; i < pi->ports.size(); i++)
649 if (i)
650 ttl += " ,\n ";
651 ttl += pi->ports[i]->to_string();
653 ttl += ".\n\n";
655 FILE *f = fopen((path_prefix+string(pi->id)+".ttl").c_str(), "w");
656 fprintf(f, "%s\n", ttl.c_str());
657 fclose(f);
660 // Generate a manifest
662 string ttl =
663 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
664 "@prefix lv2p: <http://lv2plug.in/ns/dev/presets#> .\n"
665 "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
666 "@prefix kf: <http://foltman.com/ns/> .\n"
667 "\n"
668 "kf:BooleanPlugin a rdfs:Class ; rdfs:subClassOf lv2:UtilityPlugin ;\n"
669 " rdfs:label \"Boolean-oriented\" ;\n rdfs:comment \"Modules heavily inspired by digital electronics (gates, flip-flops, etc.)\" .\n"
670 "kf:MathOperatorPlugin a rdfs:Class ; rdfs:subClassOf lv2:UtilityPlugin ;\n"
671 " rdfs:label \"Math operators\" ;\n rdfs:comment \"Mathematical operators and utility functions\" .\n"
672 "kf:IntegerPlugin a rdfs:Class ; rdfs:subClassOf lv2:UtilityPlugin ;\n"
673 " rdfs:label \"Integer-oriented\" ;\n rdfs:comment \"Operations on integer values (counters, multiplexers, etc.)\" .\n"
674 "kf:MIDIPlugin a rdfs:Class ; rdfs:subClassOf lv2:UtilityPlugin ;\n"
675 " rdfs:label \"MIDI\" ;\n rdfs:comment \"Operations on MIDI streams (filters, transposers, mappers, etc.)\" .\n"
676 "\n"
679 string presets_ttl =
680 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
681 "@prefix lv2p: <http://lv2plug.in/ns/dev/presets#> .\n"
682 "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
683 "@prefix dc: <http://dublincore.org/documents/dcmi-namespace/> .\n"
684 "\n"
687 for (unsigned int i = 0; i < plugins.size(); i++)
688 ttl += string("<" + plugin_uri_prefix)
689 + string(plugins[i]->get_plugin_info().label)
690 + "> a lv2:Plugin ;\n lv2:binary <calf.so> ; rdfs:seeAlso <" + string(plugins[i]->get_plugin_info().label) + ".ttl> , <presets.ttl> .\n";
692 for (unsigned int i = 0; i < lpl.size(); i++)
693 ttl += string("<http://calf.sourceforge.net/small_plugins/")
694 + string(lpl[i]->id)
695 + "> a lv2:Plugin ;\n lv2:binary <calf.so> ; rdfs:seeAlso <" + string(lpl[i]->id) + ".ttl> .\n";
697 calf_plugins::get_builtin_presets().load_defaults(true);
698 calf_plugins::preset_vector &factory_presets = calf_plugins::get_builtin_presets().presets;
700 ttl += "\n";
702 for (unsigned int i = 0; i < factory_presets.size(); i++)
704 plugin_preset &pr = factory_presets[i];
705 map<string, string>::iterator ilm = id_to_label.find(pr.plugin);
706 if (ilm == id_to_label.end())
707 continue;
708 string uri = "<http://calf.sourceforge.net/factory_presets#"
709 + pr.plugin + "_" + pr.get_safe_name()
710 + ">";
711 ttl += string("<" + plugin_uri_prefix + ilm->second + "> lv2p:hasPreset\n " + uri + " .\n");
713 presets_ttl += uri +
714 " a lv2p:Preset ;\n"
715 " dc:title \"" + pr.name + "\" ;\n"
716 " lv2p:appliesTo <" + plugin_uri_prefix + ilm->second + "> ;\n"
717 " lv2:port \n"
720 unsigned int count = min(pr.param_names.size(), pr.values.size());
721 for (unsigned int j = 0; j < count; j++)
723 presets_ttl += " [ lv2:symbol \"" + pr.param_names[j] + "\" ; lv2p:value " + ff2s(pr.values[j]) + " ] ";
724 if (j < count - 1)
725 presets_ttl += ',';
726 presets_ttl += '\n';
728 presets_ttl += ".\n\n";
730 FILE *f = fopen((path_prefix+"manifest.ttl").c_str(), "w");
731 fprintf(f, "%s\n", ttl.c_str());
732 fclose(f);
733 f = fopen((path_prefix+"presets.ttl").c_str(), "w");
734 fprintf(f, "%s\n", presets_ttl.c_str());
735 fclose(f);
738 #else
739 void make_ttl(string tmp)
741 fprintf(stderr, "LV2 not supported.\n");
744 #endif
746 void make_gui(string path_prefix)
748 if (path_prefix.empty())
750 fprintf(stderr, "Path parameter is required for GUI mode\n");
751 exit(1);
753 vector<plugin_metadata_iface *> plugins;
754 calf_plugins::get_all_plugins(plugins);
755 path_prefix += "/gui-";
756 for (unsigned int i = 0; i < plugins.size(); i++)
758 plugin_metadata_iface *pi = plugins[i];
760 stringstream xml;
761 int graphs = 0;
762 for (int j = 0; j < pi->get_param_count(); j++)
764 parameter_properties &props = *pi->get_param_props(j);
765 if (props.flags & PF_PROP_GRAPH)
766 graphs++;
768 xml << "<table rows=\"" << pi->get_param_count() << "\" cols=\"" << (graphs ? "4" : "3") << "\">\n";
769 for (int j = 0; j < pi->get_param_count(); j++)
771 if (j)
772 xml << "\n <!-- -->\n\n";
773 parameter_properties &props = *pi->get_param_props(j);
774 string expand_x = "expand-x=\"1\" ";
775 string fill_x = "fill-x=\"1\" ";
776 string shrink_x = "shrink-x=\"1\" ";
777 string pad_x = "pad-x=\"10\" ";
778 string attach_x = "attach-x=\"1\" attach-w=\"2\" ";
779 string attach_y = "attach-y=\"" + i2s(j) + "\" ";
780 string param = "param=\"" + string(props.short_name) + "\" ";
781 string label = " <align attach-x=\"0\" " + attach_y + " " + " align-x=\"1\"><label " + param + " /></align>\n";
782 string value = " <value " + param + " " + "attach-x=\"2\" " + attach_y + pad_x + "/>\n";
783 string attach_xv = "attach-x=\"1\" attach-w=\"1\" ";
784 string ctl;
785 if ((props.flags & PF_TYPEMASK) == PF_ENUM &&
786 (props.flags & PF_CTLMASK) == PF_CTL_COMBO)
788 ctl = " <combo " + attach_x + attach_y + param + expand_x + pad_x + " />\n";
790 else if ((props.flags & PF_TYPEMASK) == PF_STRING)
792 ctl = " <entry " + attach_x + attach_y + "key=\"" + props.short_name + "\" " + expand_x + pad_x + " />\n";
794 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
795 (props.flags & PF_CTLMASK) == PF_CTL_TOGGLE)
797 ctl = " <align " + attach_x + attach_y + expand_x + fill_x + pad_x + "><toggle " + param + "/></align>\n";
799 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
800 (props.flags & PF_CTLMASK) == PF_CTL_BUTTON)
802 ctl = " <button attach-x=\"0\" attach-w=\"3\" " + expand_x + attach_y + param + pad_x + "/>\n";
803 label.clear();
805 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
806 (props.flags & PF_CTLMASK) == PF_CTL_LED)
808 ctl = " <led " + attach_x + attach_y + param + shrink_x + pad_x + " />\n";
810 else if ((props.flags & PF_CTLMASK) == PF_CTL_METER)
812 if (props.flags & PF_CTLO_LABEL) {
813 attach_x = attach_xv;
814 pad_x.clear();
816 if (props.flags & PF_CTLO_REVERSE)
817 ctl = " <vumeter " + attach_x + attach_y + expand_x + fill_x + param + pad_x + "mode=\"2\"/>\n";
818 else
819 ctl = " <vumeter " + attach_x + attach_y + expand_x + fill_x + param + pad_x + "/>\n";
820 if (props.flags & PF_CTLO_LABEL)
821 ctl += value;
823 else if ((props.flags & PF_CTLMASK) != PF_CTL_FADER)
825 if ((props.flags & PF_UNITMASK) == PF_UNIT_DEG)
826 ctl = " <knob " + attach_xv + attach_y + shrink_x + param + " type=\"3\"/>\n";
827 else
828 ctl = " <knob " + attach_xv + attach_y + shrink_x + param + " />\n";
829 ctl += value;
831 else
833 ctl += " <hscale " + param + " " + attach_x + attach_y + " pad-x=\"10\"/>";
835 if (!label.empty())
836 xml << label;
837 if (!ctl.empty())
838 xml << ctl;
840 if (graphs)
842 xml << " <if cond=\"directlink\">" << endl;
843 xml << " <vbox expand-x=\"1\" fill-x=\"1\" attach-x=\"3\" attach-y=\"0\" attach-h=\"" << pi->get_param_count() << "\">" << endl;
844 for (int j = 0; j < pi->get_param_count(); j++)
846 parameter_properties &props = *pi->get_param_props(j);
847 if (props.flags & PF_PROP_GRAPH)
849 xml << " <line-graph refresh=\"1\" width=\"160\" param=\"" << props.short_name << "\"/>\n" << endl;
852 xml << " </vbox>" << endl;
853 xml << " </if>" << endl;
855 xml << "</table>\n";
856 FILE *f = fopen((path_prefix+string(pi->get_id())+".xml").c_str(), "w");
857 fprintf(f, "%s\n", xml.str().c_str());
858 fclose(f);
862 int main(int argc, char *argv[])
864 string mode = "rdf";
865 string path_prefix;
866 while(1) {
867 int option_index;
868 int c = getopt_long(argc, argv, "hvm:p:", long_options, &option_index);
869 if (c == -1)
870 break;
871 switch(c) {
872 case 'h':
873 case '?':
874 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]);
875 return 0;
876 case 'v':
877 printf("%s\n", PACKAGE_STRING);
878 return 0;
879 case 'm':
880 mode = optarg;
881 if (mode != "rdf" && mode != "ttl" && mode != "gui") {
882 fprintf(stderr, "calfmakerdf: Invalid mode %s\n", optarg);
883 return 1;
885 break;
886 case 'p':
887 path_prefix = optarg;
888 break;
891 if (false)
894 #if USE_LADSPA
895 else if (mode == "rdf")
896 make_rdf();
897 #endif
898 #if USE_LV2
899 else if (mode == "ttl")
900 make_ttl(path_prefix);
901 #endif
902 else if (mode == "gui")
903 make_gui(path_prefix);
904 else
906 fprintf(stderr, "calfmakerdf: Mode %s unsupported in this version\n", optarg);
907 return 1;
909 return 0;