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
25 #include <calf/giface.h>
26 #include <calf/plugininfo.h>
27 #include <calf/utils.h>
29 #include <calf/lv2_contexts.h>
30 #include <calf/lv2_event.h>
31 #include <calf/lv2_uri_map.h>
36 using namespace calf_utils
;
37 using namespace calf_plugins
;
39 static struct option long_options
[] = {
41 {"version", 0, 0, 'v'},
49 static std::string
unit_to_string(parameter_properties
&props
)
51 uint32_t flags
= props
.flags
& PF_UNITMASK
;
55 return "ladspa:hasUnit=\"&ladspa;dB\" ";
57 return "ladspa:hasUnit=\"&ladspa;coef\" ";
59 return "ladspa:hasUnit=\"&ladspa;Hz\" ";
61 return "ladspa:hasUnit=\"&ladspa;seconds\" ";
63 return "ladspa:hasUnit=\"&ladspa;milliseconds\" ";
69 static std::string
scale_to_string(parameter_properties
&props
)
71 if ((props
.flags
& PF_TYPEMASK
) != PF_ENUM
) {
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
,
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
++) {
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
]) +
101 " </ladspa:hasPort>\n";
103 rdf
+= " <ladspa:hasSetting>\n"
104 " <ladspa:Default>\n";
105 for (unsigned int i
= 0; i
< count
; i
++) {
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";
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"
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
);
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
);
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());
155 rdf
+= "</rdf:RDF>\n";
157 printf("%s\n", rdf
.c_str());
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)
165 const char *ind
= " ";
168 if (ports
!= "") ports
+= " , ";
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";
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
;
185 if (!strcmp(symbol
, "in_r"))
186 ss
<< ind
<< "pg:membership [ pg:group <#stereoIn>; pg:role pg:rightChannel ] ;" << endl
;
188 if (!strcmp(symbol
, "out_l"))
189 ss
<< ind
<< "pg:membership [ pg:group <#stereoOut>; pg:role pg:leftChannel ] ;" << endl
;
191 if (!strcmp(symbol
, "out_r"))
192 ss
<< ind
<< "pg:membership [ pg:group <#stereoOut>; pg:role pg:rightChannel ] ;" << endl
;
197 static const char *units
[] = {
203 "ue:cent", // - ask SWH (or maybe ue2: and write the extension by myself)
204 "ue:semitone12TET", // - ask SWH
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
)
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
+= " , ";
223 if (pp
.flags
& PF_PROP_OUTPUT
)
224 ss
<< ind
<< "a lv2:OutputPort ;\n";
226 ss
<< ind
<< "a lv2:InputPort ;\n";
227 if (type
== PF_STRING
)
228 ss
<< ind
<< "a strport:StringPort ;\n";
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";
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";
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";
288 struct lv2_port_base
{
290 std::string symbol
, name
, extras
, microname
;
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() {
314 const char *ind
= " ";
316 to_stream_base(ss
, ind
, "lv2:AudioPort");
323 struct lv2_event_port_base
: public lv2_port_base
325 std::string
to_string() {
327 const char *ind
= " ";
329 to_stream_base(ss
, ind
, "lv2ev:EventPort");
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
;
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;
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() {
386 const char *ind
= " ";
388 to_stream_base(ss
, ind
, "lv2:ControlPort");
390 ss
<< ind
<< "lv2:portProperty lv2:toggled ;\n";
392 ss
<< ind
<< "lv2:portProperty lv2:integer ;\n";
394 ss
<< ind
<< "lv2:default " << def_value
<< " ;\n";
396 ss
<< ind
<< "lv2:minimum " << min
<< " ;\n";
398 ss
<< ind
<< "lv2:maximum " << max
<< " ;\n";
405 struct lv2_plugin_info
: public plugin_info_iface
411 /// Plugin label (short name)
413 /// Plugin class (category)
414 std::string category
;
415 /// Plugin micro-name
416 std::string microname
;
417 /// Extra declarations for LV2
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
) {
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
);
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
);
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
);
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
;
464 void make_ttl(string path_prefix
)
466 if (path_prefix
.empty())
468 fprintf(stderr
, "Path parameter is required for TTL mode\n");
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"
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/";
519 string gui_uri
= "<http://calf.sourceforge.net/plugins/gui/gtk2-gui>";
520 gui_header
= gui_uri
+ "\n"
522 " uiext:binary <calflv2gui.so> ;\n"
523 " uiext:requiredFeature uiext:makeResident .\n"
525 #ifdef ENABLE_EXPERIMENTAL
526 "<http://calf.sourceforge.net/small_plugins/gui/gtk2-gui>\n"
528 " uiext:binary <calflv2gui.so> ;\n"
529 " uiext:requiredFeature uiext:makeResident .\n"
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
) + ">";
543 ttl
= "@prefix : " + uri
+ " .\n" + header
+ gui_header
;
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";
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";
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";
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";
577 if (pi
->requires_midi()) {
578 ttl
+= " lv2:requiredFeature <" LV2_EVENT_URI
"> ;\n";
579 ttl
+= " lv2:requiredFeature <" LV2_URI_MAP_URI
"> ;\n";
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";
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
++);
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
++);
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);
616 ttl
+= " lv2:port " + ports
+ "\n";
619 FILE *f
= fopen((path_prefix
+string(lpi
.label
)+".ttl").c_str(), "w");
620 fprintf(f
, "%s\n", ttl
.c_str());
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
) + ">";
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";
644 if (!pi
->ports
.empty())
647 for (unsigned int i
= 0; i
< pi
->ports
.size(); i
++)
651 ttl
+= pi
->ports
[i
]->to_string();
655 FILE *f
= fopen((path_prefix
+string(pi
->id
)+".ttl").c_str(), "w");
656 fprintf(f
, "%s\n", ttl
.c_str());
660 // Generate a manifest
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"
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"
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"
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/")
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
;
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())
708 string uri
= "<http://calf.sourceforge.net/factory_presets#"
709 + pr
.plugin
+ "_" + pr
.get_safe_name()
711 ttl
+= string("<" + plugin_uri_prefix
+ ilm
->second
+ "> lv2p:hasPreset\n " + uri
+ " .\n");
715 " dc:title \"" + pr
.name
+ "\" ;\n"
716 " lv2p:appliesTo <" + plugin_uri_prefix
+ ilm
->second
+ "> ;\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
]) + " ] ";
728 presets_ttl
+= ".\n\n";
730 FILE *f
= fopen((path_prefix
+"manifest.ttl").c_str(), "w");
731 fprintf(f
, "%s\n", ttl
.c_str());
733 f
= fopen((path_prefix
+"presets.ttl").c_str(), "w");
734 fprintf(f
, "%s\n", presets_ttl
.c_str());
739 void make_ttl(string tmp
)
741 fprintf(stderr
, "LV2 not supported.\n");
746 void make_gui(string path_prefix
)
748 if (path_prefix
.empty())
750 fprintf(stderr
, "Path parameter is required for GUI mode\n");
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
];
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
)
768 xml
<< "<table rows=\"" << pi
->get_param_count() << "\" cols=\"" << (graphs
? "4" : "3") << "\">\n";
769 for (int j
= 0; j
< pi
->get_param_count(); 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\" ";
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";
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
;
816 if (props
.flags
& PF_CTLO_REVERSE
)
817 ctl
= " <vumeter " + attach_x
+ attach_y
+ expand_x
+ fill_x
+ param
+ pad_x
+ "mode=\"2\"/>\n";
819 ctl
= " <vumeter " + attach_x
+ attach_y
+ expand_x
+ fill_x
+ param
+ pad_x
+ "/>\n";
820 if (props
.flags
& PF_CTLO_LABEL
)
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";
828 ctl
= " <knob " + attach_xv
+ attach_y
+ shrink_x
+ param
+ " />\n";
833 ctl
+= " <hscale " + param
+ " " + attach_x
+ attach_y
+ " pad-x=\"10\"/>";
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
;
856 FILE *f
= fopen((path_prefix
+string(pi
->get_id())+".xml").c_str(), "w");
857 fprintf(f
, "%s\n", xml
.str().c_str());
862 int main(int argc
, char *argv
[])
868 int c
= getopt_long(argc
, argv
, "hvm:p:", long_options
, &option_index
);
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]);
877 printf("%s\n", PACKAGE_STRING
);
881 if (mode
!= "rdf" && mode
!= "ttl" && mode
!= "gui") {
882 fprintf(stderr
, "calfmakerdf: Invalid mode %s\n", optarg
);
887 path_prefix
= optarg
;
895 else if (mode
== "rdf")
899 else if (mode
== "ttl")
900 make_ttl(path_prefix
);
902 else if (mode
== "gui")
903 make_gui(path_prefix
);
906 fprintf(stderr
, "calfmakerdf: Mode %s unsupported in this version\n", optarg
);