New LED mode (red)
[calf.git] / src / makerdf.cpp
blob430fb91f9b7a8f29f7749d368d229fe83cec2ab3
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 <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 if (!(pp.flags & PF_PROP_OUTPUT))
258 ss << ind << "lv2:default " << pp.def_value << " ;\n";
259 ss << ind << "lv2:minimum " << pp.min << " ;\n";
260 ss << ind << "lv2:maximum " << pp.max << " ;\n";
261 if (pp.step > 1)
262 ss << ind << "epp:rangeSteps " << pp.step << " ;\n";
263 if (unit > 0 && unit < (sizeof(units) / sizeof(char *)) && units[unit - 1] != NULL)
264 ss << ind << "ue:unit " << units[unit - 1] << " ;\n";
267 // for now I assume that the only tempo passed is the tempo the plugin should operate with
268 // this may change as more complex plugins are added
269 if (unit == (PF_UNIT_BPM >> 24))
270 ss << ind << "lv2:portProperty epp:reportsBpm ;\n";
272 ss << " ]";
273 ports += ss.str();
276 struct lv2_port_base {
277 int index;
278 std::string symbol, name, extras, microname;
279 bool is_input;
281 virtual std::string to_string() = 0;
282 virtual ~lv2_port_base() {}
283 void to_stream_base(stringstream &ss, string ind, string port_class)
285 ss << ind << (is_input ? "a lv2:InputPort ;\n" : "a lv2:OutputPort ;\n");
286 ss << ind << "a " << port_class << " ;\n";
287 ss << ind << "lv2:index " << index << " ;\n";
288 ss << ind << "lv2:symbol \"" << symbol << "\" ;\n";
289 ss << ind << "lv2:name \"" << name << "\" ;\n";
290 if (!extras.empty()) {
291 ss << calf_utils::indent(extras, ind);
293 if (microname != "N/A")
294 ss << ind << "<http://lv2plug.in/ns/dev/tiny-name> \"" << microname << "\" ;\n";
298 struct lv2_audio_port_base: public lv2_port_base
300 std::string to_string() {
301 stringstream ss;
302 const char *ind = " ";
303 ss << "[\n";
304 to_stream_base(ss, ind, "lv2:AudioPort");
305 ss << " ]\n";
307 return ss.str();
311 struct lv2_event_port_base: public lv2_port_base
313 std::string to_string() {
314 stringstream ss;
315 const char *ind = " ";
316 ss << "[\n";
317 to_stream_base(ss, ind, "lv2ev:EventPort");
318 ss << " ]\n";
320 return ss.str();
324 template<class base_iface, class base_data>
325 struct lv2_port_impl: public base_iface, public base_data
327 lv2_port_impl(int _index, const std::string &_symbol, const std::string &_name, const std::string &_microname)
329 this->index = _index;
330 this->symbol = _symbol;
331 this->name = _name;
332 this->microname = _microname;
333 this->is_input = true;
335 /// Called if it's an input port
336 virtual base_iface& input() { this->is_input = true; return *this; }
337 /// Called if it's an output port
338 virtual base_iface& output() { this->is_input = false; return *this; }
339 virtual base_iface& lv2_ttl(const std::string &text) { this->extras += text + "\n"; return *this; }
342 struct lv2_control_port_base: public lv2_port_base
344 bool is_log, is_toggle, is_trigger, is_integer;
345 double min, max, def_value;
346 bool has_min, has_max;
348 lv2_control_port_base()
350 has_min = has_max = is_log = is_toggle = is_trigger = is_integer = false;
351 def_value = 0.f;
355 typedef lv2_port_impl<plain_port_info_iface, lv2_audio_port_base> lv2_audio_port_info;
356 typedef lv2_port_impl<plain_port_info_iface, lv2_event_port_base> lv2_event_port_info;
358 struct lv2_control_port_info: public lv2_port_impl<control_port_info_iface, lv2_control_port_base>
360 lv2_control_port_info(int _index, const std::string &_symbol, const std::string &_name, const std::string &_microname)
361 : lv2_port_impl<control_port_info_iface, lv2_control_port_base>(_index, _symbol, _name, _microname)
364 /// Called to mark the port as using linear range [from, to]
365 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; }
366 /// Called to mark the port as using log range [from, to]
367 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; }
368 virtual control_port_info_iface& toggle() { is_toggle = true; return *this; }
369 virtual control_port_info_iface& trigger() { is_trigger = true; return *this; }
370 virtual control_port_info_iface& integer() { is_integer = true; return *this; }
371 virtual control_port_info_iface& lv2_ttl(const std::string &text) { extras += text + "\n"; return *this; }
372 std::string to_string() {
373 stringstream ss;
374 const char *ind = " ";
375 ss << "[\n";
376 to_stream_base(ss, ind, "lv2:ControlPort");
377 if (is_toggle)
378 ss << ind << "lv2:portProperty lv2:toggled ;\n";
379 if (is_integer)
380 ss << ind << "lv2:portProperty lv2:integer ;\n";
381 if (is_input)
382 ss << ind << "lv2:default " << def_value << " ;\n";
383 if (has_min)
384 ss << ind << "lv2:minimum " << min << " ;\n";
385 if (has_max)
386 ss << ind << "lv2:maximum " << max << " ;\n";
387 ss << " ]\n";
389 return ss.str();
393 struct lv2_plugin_info: public plugin_info_iface
395 /// Plugin id
396 std::string id;
397 /// Plugin name
398 std::string name;
399 /// Plugin label (short name)
400 std::string label;
401 /// Plugin class (category)
402 std::string category;
403 /// Plugin micro-name
404 std::string microname;
405 /// Extra declarations for LV2
406 std::string extras;
407 /// Vector of ports
408 vector<lv2_port_base *> ports;
409 /// Set plugin names (ID, name and label)
410 virtual void names(const std::string &_name, const std::string &_label, const std::string &_category, const std::string &_microname) {
411 name = _name;
412 label = _label;
413 category = _category;
414 microname = _microname;
416 /// Add an audio port (returns a sink which accepts further description)
417 virtual plain_port_info_iface &audio_port(const std::string &id, const std::string &name, const std::string &_microname) {
418 lv2_audio_port_info *port = new lv2_audio_port_info(ports.size(), id, name, _microname);
419 ports.push_back(port);
420 return *port;
422 /// Add an eventport (returns a sink which accepts further description)
423 virtual plain_port_info_iface &event_port(const std::string &id, const std::string &name, const std::string &_microname) {
424 lv2_event_port_info *port = new lv2_event_port_info(ports.size(), id, name, _microname);
425 ports.push_back(port);
426 return *port;
428 /// Add a control port (returns a sink which accepts further description)
429 virtual control_port_info_iface &control_port(const std::string &id, const std::string &name, double def_value, const std::string &_microname) {
430 lv2_control_port_info *port = new lv2_control_port_info(ports.size(), id, name, _microname);
431 port->def_value = def_value;
432 ports.push_back(port);
433 return *port;
435 /// Add extra TTL to plugin declaration
436 virtual void lv2_ttl(const std::string &text) { this->extras += " " + text + "\n"; }
437 /// Called after plugin has reported all the information
438 virtual void finalize() {
442 struct lv2_plugin_list: public plugin_list_info_iface, public vector<lv2_plugin_info *>
444 virtual plugin_info_iface &plugin(const std::string &id) {
445 lv2_plugin_info *pi = new lv2_plugin_info;
446 pi->id = id;
447 push_back(pi);
448 return *pi;
452 void make_ttl(string path_prefix)
454 if (path_prefix.empty())
456 fprintf(stderr, "Path parameter is required for TTL mode\n");
457 exit(1);
459 string header;
461 header =
462 "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
463 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
464 "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
465 "@prefix dc: <http://dublincore.org/documents/dcmi-namespace/> .\n"
466 "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"
467 "@prefix uiext: <http://lv2plug.in/ns/extensions/ui#> .\n"
468 "@prefix lv2ev: <http://lv2plug.in/ns/ext/event#> .\n"
469 "@prefix lv2midi: <http://lv2plug.in/ns/ext/midi#> .\n"
470 "@prefix lv2ctx: <http://lv2plug.in/ns/dev/contexts#> .\n"
471 "@prefix strport: <http://lv2plug.in/ns/dev/string-port#> .\n"
472 "@prefix pg: <http://ll-plugins.nongnu.org/lv2/ext/portgroups#> .\n"
473 "@prefix ue: <http://lv2plug.in/ns/extensions/units#> .\n"
474 "@prefix epp: <http://lv2plug.in/ns/dev/extportinfo#> .\n"
475 "@prefix kf: <http://foltman.com/ns/> .\n"
476 "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"
477 "@prefix poly: <http://lv2plug.in/ns/dev/polymorphic-port#> .\n"
479 "\n"
482 vector<plugin_metadata_iface *> plugins;
483 calf_plugins::get_all_plugins(plugins);
485 map<string, string> classes;
487 const char *ptypes[] = {
488 "Flanger", "Reverb", "Generator", "Instrument", "Oscillator",
489 "Utility", "Converter", "Analyser", "Mixer", "Simulator",
490 "Delay", "Modulator", "Phaser", "Chorus", "Filter",
491 "Lowpass", "Highpass", "Bandpass", "Comb", "Allpass",
492 "Amplifier", "Distortion", "Waveshaper", "Dynamics", "Compressor",
493 "Expander", "Limiter", "Gate", NULL
496 for(const char **p = ptypes; *p; p++) {
497 string name = string(*p) + "Plugin";
498 classes[name] = "lv2:" + name;
500 classes["SynthesizerPlugin"] = "lv2:InstrumentPlugin";
502 string plugin_uri_prefix = "http://calf.sourceforge.net/plugins/";
504 string gui_header;
506 #if USE_LV2_GUI
507 string gui_uri = "<http://calf.sourceforge.net/plugins/gui/gtk2-gui>";
508 gui_header = gui_uri + "\n"
509 " a uiext:GtkUI ;\n"
510 " uiext:binary <calflv2gui.so> ;\n"
511 " uiext:requiredFeature uiext:makeResident .\n"
512 " \n"
513 #ifdef ENABLE_EXPERIMENTAL
514 "<http://calf.sourceforge.net/small_plugins/gui/gtk2-gui>\n"
515 " a uiext:GtkUI ;\n"
516 " uiext:binary <calflv2gui.so> ;\n"
517 " uiext:requiredFeature uiext:makeResident .\n"
518 " \n"
519 #endif
521 #endif
523 map<string, string> id_to_label;
525 for (unsigned int i = 0; i < plugins.size(); i++) {
526 plugin_metadata_iface *pi = plugins[i];
527 const ladspa_plugin_info &lpi = pi->get_plugin_info();
528 id_to_label[pi->get_id()] = pi->get_label();
529 string uri = string("<" + plugin_uri_prefix) + string(lpi.label) + ">";
530 string ttl;
531 ttl = "@prefix : " + uri + " .\n" + header + gui_header;
533 #if USE_LV2_GUI
534 for (int j = 0; j < pi->get_param_count(); j++)
536 parameter_properties &props = *pi->get_param_props(j);
537 if (props.flags & PF_PROP_OUTPUT)
538 ttl += gui_uri + " uiext:portNotification [\n uiext:plugin " + uri + " ;\n uiext:portIndex " + i2s(j) + "\n] .\n\n";
540 #endif
542 ttl += uri + " a lv2:Plugin ;\n";
544 if (classes.count(lpi.plugin_type))
545 ttl += " a " + classes[lpi.plugin_type]+" ;\n";
548 ttl += " doap:name \""+string(lpi.name)+"\" ;\n";
549 ttl += " doap:maintainer [ foaf:name \""+string(lpi.maker)+"\" ; ] ;\n";
551 #if USE_LV2_GUI
552 ttl += " uiext:ui <http://calf.sourceforge.net/plugins/gui/gtk2-gui> ;\n";
553 ttl += " lv2:optionalFeature <http://lv2plug.in/ns/ext/instance-access> ;\n";
554 ttl += " lv2:optionalFeature <http://lv2plug.in/ns/ext/data-access> ;\n";
555 #endif
557 ttl += " doap:license <http://usefulinc.com/doap/licenses/lgpl> ;\n";
558 ttl += " dc:replaces <urn:ladspa:" + i2s(lpi.unique_id) + "> ;\n";
559 // XXXKF not really optional for now, to be honest
560 ttl += " lv2:optionalFeature epp:supportsStrictBounds ;\n";
561 if (pi->is_rt_capable())
562 ttl += " lv2:optionalFeature lv2:hardRtCapable ;\n";
563 if (pi->get_midi())
565 if (pi->requires_midi()) {
566 ttl += " lv2:requiredFeature <" LV2_EVENT_URI "> ;\n";
567 ttl += " lv2:requiredFeature <" LV2_URI_MAP_URI "> ;\n";
569 else {
570 ttl += " lv2:optionalFeature <" LV2_EVENT_URI "> ;\n";
571 ttl += " lv2:optionalFeature <" LV2_URI_MAP_URI "> ;\n";
575 if (pi->requires_message_context())
577 ttl += " lv2:requiredFeature <http://lv2plug.in/ns/dev/contexts> ;\n";
578 ttl += " lv2:requiredFeature <" LV2_CONTEXT_MESSAGE "> ;\n";
579 ttl += " lv2ctx:requiredContext lv2ctx:MessageContext ;\n";
581 if (pi->requires_string_ports())
582 ttl += " lv2:requiredFeature <http://lv2plug.in/ns/dev/string-port#StringTransfer> ;\n";
584 string ports = "";
585 int pn = 0;
586 const char *in_names[] = { "in_l", "in_r" };
587 const char *out_names[] = { "out_l", "out_r" };
588 for (int i = 0; i < pi->get_input_count(); i++)
589 add_port(ports, in_names[i], in_names[i], "Input", pn++);
590 for (int i = 0; i < pi->get_output_count(); i++)
591 add_port(ports, out_names[i], out_names[i], "Output", pn++);
592 for (int i = 0; i < pi->get_param_count(); i++)
593 add_ctl_port(ports, *pi->get_param_props(i), pn++, pi, i);
594 if (pi->get_midi()) {
595 add_port(ports, "event_in", "Event", "Input", pn++, "lv2ev:EventPort", true);
597 if (!ports.empty())
598 ttl += " lv2:port " + ports + "\n";
599 ttl += ".\n\n";
601 FILE *f = fopen((path_prefix+string(lpi.label)+".ttl").c_str(), "w");
602 fprintf(f, "%s\n", ttl.c_str());
603 fclose(f);
605 lv2_plugin_list lpl;
606 calf_plugins::get_all_small_plugins(&lpl);
607 for (unsigned int i = 0; i < lpl.size(); i++)
609 lv2_plugin_info *pi = lpl[i];
610 // Copy-pasted code is the root of all evil, I know!
611 string uri = string("<http://calf.sourceforge.net/small_plugins/") + string(pi->id) + ">";
612 string ttl;
613 ttl = "@prefix : " + uri + " .\n" + header + gui_header;
615 ttl += uri + " a lv2:Plugin ;\n";
617 if (!pi->category.empty())
618 ttl += " a " + pi->category+" ;\n";
620 ttl += " doap:name \""+string(pi->label)+"\" ;\n";
621 ttl += " doap:license <http://usefulinc.com/doap/licenses/lgpl> ;\n";
622 if (!pi->microname.empty())
623 ttl += " <http://lv2plug.in/ns/dev/tiny-name> \"" + pi->microname + "\" ;\n";
624 ttl += pi->extras;
626 if (!pi->ports.empty())
628 ttl += " lv2:port ";
629 for (unsigned int i = 0; i < pi->ports.size(); i++)
631 if (i)
632 ttl += " ,\n ";
633 ttl += pi->ports[i]->to_string();
635 ttl += ".\n\n";
637 FILE *f = fopen((path_prefix+string(pi->id)+".ttl").c_str(), "w");
638 fprintf(f, "%s\n", ttl.c_str());
639 fclose(f);
642 // Generate a manifest
644 string ttl =
645 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
646 "@prefix lv2p: <http://lv2plug.in/ns/dev/presets#> .\n"
647 "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
648 "@prefix kf: <http://foltman.com/ns/> .\n"
649 "\n"
650 "kf:BooleanPlugin a rdfs:Class ; rdfs:subClassOf lv2:UtilityPlugin ;\n"
651 " rdfs:label \"Boolean-oriented\" ;\n rdfs:comment \"Modules heavily inspired by digital electronics (gates, flip-flops, etc.)\" .\n"
652 "kf:MathOperatorPlugin a rdfs:Class ; rdfs:subClassOf lv2:UtilityPlugin ;\n"
653 " rdfs:label \"Math operators\" ;\n rdfs:comment \"Mathematical operators and utility functions\" .\n"
654 "kf:IntegerPlugin a rdfs:Class ; rdfs:subClassOf lv2:UtilityPlugin ;\n"
655 " rdfs:label \"Integer-oriented\" ;\n rdfs:comment \"Operations on integer values (counters, multiplexers, etc.)\" .\n"
656 "kf:MIDIPlugin a rdfs:Class ; rdfs:subClassOf lv2:UtilityPlugin ;\n"
657 " rdfs:label \"MIDI\" ;\n rdfs:comment \"Operations on MIDI streams (filters, transposers, mappers, etc.)\" .\n"
658 "\n"
661 string presets_ttl =
662 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
663 "@prefix lv2p: <http://lv2plug.in/ns/dev/presets#> .\n"
664 "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
665 "@prefix dc: <http://dublincore.org/documents/dcmi-namespace/> .\n"
666 "\n"
669 for (unsigned int i = 0; i < plugins.size(); i++)
670 ttl += string("<" + plugin_uri_prefix)
671 + string(plugins[i]->get_plugin_info().label)
672 + "> a lv2:Plugin ;\n lv2:binary <calf.so> ; rdfs:seeAlso <" + string(plugins[i]->get_plugin_info().label) + ".ttl> , <presets.ttl> .\n";
674 for (unsigned int i = 0; i < lpl.size(); i++)
675 ttl += string("<http://calf.sourceforge.net/small_plugins/")
676 + string(lpl[i]->id)
677 + "> a lv2:Plugin ;\n lv2:binary <calf.so> ; rdfs:seeAlso <" + string(lpl[i]->id) + ".ttl> .\n";
679 calf_plugins::get_builtin_presets().load_defaults(true);
680 calf_plugins::preset_vector &factory_presets = calf_plugins::get_builtin_presets().presets;
682 ttl += "\n";
684 for (unsigned int i = 0; i < factory_presets.size(); i++)
686 plugin_preset &pr = factory_presets[i];
687 map<string, string>::iterator ilm = id_to_label.find(pr.plugin);
688 if (ilm == id_to_label.end())
689 continue;
690 string uri = "<http://calf.sourceforge.net/factory_presets#"
691 + pr.plugin + "_" + pr.get_safe_name()
692 + ">";
693 ttl += string("<" + plugin_uri_prefix + ilm->second + "> lv2p:hasPreset\n " + uri + " .\n");
695 presets_ttl += uri +
696 " a lv2p:Preset ;\n"
697 " dc:title \"" + pr.name + "\" ;\n"
698 " lv2p:appliesTo <" + plugin_uri_prefix + ilm->second + "> ;\n"
699 " lv2:port \n"
702 unsigned int count = min(pr.param_names.size(), pr.values.size());
703 for (unsigned int j = 0; j < count; j++)
705 presets_ttl += " [ lv2:symbol \"" + pr.param_names[j] + "\" ; lv2p:value " + ff2s(pr.values[j]) + " ] ";
706 if (j < count - 1)
707 presets_ttl += ',';
708 presets_ttl += '\n';
710 presets_ttl += ".\n\n";
712 FILE *f = fopen((path_prefix+"manifest.ttl").c_str(), "w");
713 fprintf(f, "%s\n", ttl.c_str());
714 fclose(f);
715 f = fopen((path_prefix+"presets.ttl").c_str(), "w");
716 fprintf(f, "%s\n", presets_ttl.c_str());
717 fclose(f);
720 #else
721 void make_ttl(string tmp)
723 fprintf(stderr, "LV2 not supported.\n");
726 #endif
728 void make_gui(string path_prefix)
730 if (path_prefix.empty())
732 fprintf(stderr, "Path parameter is required for GUI mode\n");
733 exit(1);
735 vector<plugin_metadata_iface *> plugins;
736 calf_plugins::get_all_plugins(plugins);
737 path_prefix += "/gui-";
738 for (unsigned int i = 0; i < plugins.size(); i++)
740 plugin_metadata_iface *pi = plugins[i];
742 stringstream xml;
743 int graphs = 0;
744 for (int j = 0; j < pi->get_param_count(); j++)
746 parameter_properties &props = *pi->get_param_props(j);
747 if (props.flags & PF_PROP_GRAPH)
748 graphs++;
750 xml << "<table rows=\"" << pi->get_param_count() << "\" cols=\"" << (graphs ? "4" : "3") << "\">\n";
751 for (int j = 0; j < pi->get_param_count(); j++)
753 if (j)
754 xml << "\n <!-- -->\n\n";
755 parameter_properties &props = *pi->get_param_props(j);
756 string expand_x = "expand-x=\"1\" ";
757 string fill_x = "fill-x=\"1\" ";
758 string shrink_x = "shrink-x=\"1\" ";
759 string pad_x = "pad-x=\"10\" ";
760 string attach_x = "attach-x=\"1\" attach-w=\"2\" ";
761 string attach_y = "attach-y=\"" + i2s(j) + "\" ";
762 string param = "param=\"" + string(props.short_name) + "\" ";
763 string label = " <align attach-x=\"0\" " + attach_y + " " + " align-x=\"1\"><label " + param + " /></align>\n";
764 string value = " <value " + param + " " + "attach-x=\"2\" " + attach_y + pad_x + "/>\n";
765 string attach_xv = "attach-x=\"1\" attach-w=\"1\" ";
766 string ctl;
767 if ((props.flags & PF_TYPEMASK) == PF_ENUM &&
768 (props.flags & PF_CTLMASK) == PF_CTL_COMBO)
770 ctl = " <combo " + attach_x + attach_y + param + expand_x + pad_x + " />\n";
772 else if ((props.flags & PF_TYPEMASK) == PF_STRING)
774 ctl = " <entry " + attach_x + attach_y + "key=\"" + props.short_name + "\" " + expand_x + pad_x + " />\n";
776 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
777 (props.flags & PF_CTLMASK) == PF_CTL_TOGGLE)
779 ctl = " <align " + attach_x + attach_y + expand_x + fill_x + pad_x + "><toggle " + param + "/></align>\n";
781 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
782 (props.flags & PF_CTLMASK) == PF_CTL_BUTTON)
784 ctl = " <button attach-x=\"0\" attach-w=\"3\" " + expand_x + attach_y + param + pad_x + "/>\n";
785 label.clear();
787 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
788 (props.flags & PF_CTLMASK) == PF_CTL_LED)
790 ctl = " <led " + attach_x + attach_y + param + shrink_x + pad_x + " />\n";
792 else if ((props.flags & PF_CTLMASK) == PF_CTL_METER)
794 if (props.flags & PF_CTLO_LABEL) {
795 attach_x = attach_xv;
796 pad_x.clear();
798 if (props.flags & PF_CTLO_REVERSE)
799 ctl = " <vumeter " + attach_x + attach_y + expand_x + fill_x + param + pad_x + "mode=\"2\"/>\n";
800 else
801 ctl = " <vumeter " + attach_x + attach_y + expand_x + fill_x + param + pad_x + "/>\n";
802 if (props.flags & PF_CTLO_LABEL)
803 ctl += value;
805 else if ((props.flags & PF_CTLMASK) != PF_CTL_FADER)
807 if ((props.flags & PF_UNITMASK) == PF_UNIT_DEG)
808 ctl = " <knob " + attach_xv + attach_y + shrink_x + param + " type=\"3\"/>\n";
809 else
810 ctl = " <knob " + attach_xv + attach_y + shrink_x + param + " />\n";
811 ctl += value;
813 else
815 ctl += " <hscale " + param + " " + attach_x + attach_y + " pad-x=\"10\"/>";
817 if (!label.empty())
818 xml << label;
819 if (!ctl.empty())
820 xml << ctl;
822 if (graphs)
824 xml << " <if cond=\"directlink\">" << endl;
825 xml << " <vbox expand-x=\"1\" fill-x=\"1\" attach-x=\"3\" attach-y=\"0\" attach-h=\"" << pi->get_param_count() << "\">" << endl;
826 for (int j = 0; j < pi->get_param_count(); j++)
828 parameter_properties &props = *pi->get_param_props(j);
829 if (props.flags & PF_PROP_GRAPH)
831 xml << " <line-graph refresh=\"1\" width=\"160\" param=\"" << props.short_name << "\"/>\n" << endl;
834 xml << " </vbox>" << endl;
835 xml << " </if>" << endl;
837 xml << "</table>\n";
838 FILE *f = fopen((path_prefix+string(pi->get_id())+".xml").c_str(), "w");
839 fprintf(f, "%s\n", xml.str().c_str());
840 fclose(f);
844 int main(int argc, char *argv[])
846 string mode = "rdf";
847 string path_prefix;
848 while(1) {
849 int option_index;
850 int c = getopt_long(argc, argv, "hvm:p:", long_options, &option_index);
851 if (c == -1)
852 break;
853 switch(c) {
854 case 'h':
855 case '?':
856 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]);
857 return 0;
858 case 'v':
859 printf("%s\n", PACKAGE_STRING);
860 return 0;
861 case 'm':
862 mode = optarg;
863 if (mode != "rdf" && mode != "ttl" && mode != "gui") {
864 fprintf(stderr, "calfmakerdf: Invalid mode %s\n", optarg);
865 return 1;
867 break;
868 case 'p':
869 path_prefix = optarg;
870 break;
873 if (false)
876 #if USE_LADSPA
877 else if (mode == "rdf")
878 make_rdf();
879 #endif
880 #if USE_LV2
881 else if (mode == "ttl")
882 make_ttl(path_prefix);
883 #endif
884 else if (mode == "gui")
885 make_gui(path_prefix);
886 else
888 fprintf(stderr, "calfmakerdf: Mode %s unsupported in this version\n", optarg);
889 return 1;
891 return 0;