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.
24 #include <calf/giface.h>
25 #include <calf/plugininfo.h>
26 #include <calf/utils.h>
28 #include <calf/lv2_contexts.h>
29 #include <calf/lv2_event.h>
30 #include <calf/lv2_uri_map.h>
34 using namespace calf_utils
;
35 using namespace calf_plugins
;
37 static struct option long_options
[] = {
39 {"version", 0, 0, 'v'},
47 static std::string
unit_to_string(parameter_properties
&props
)
49 uint32_t flags
= props
.flags
& PF_UNITMASK
;
53 return "ladspa:hasUnit=\"&ladspa;dB\" ";
55 return "ladspa:hasUnit=\"&ladspa;coef\" ";
57 return "ladspa:hasUnit=\"&ladspa;Hz\" ";
59 return "ladspa:hasUnit=\"&ladspa;seconds\" ";
61 return "ladspa:hasUnit=\"&ladspa;milliseconds\" ";
67 static std::string
scale_to_string(parameter_properties
&props
)
69 if ((props
.flags
& PF_TYPEMASK
) != PF_ENUM
) {
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
,
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
++) {
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
]) +
99 " </ladspa:hasPort>\n";
101 rdf
+= " <ladspa:hasSetting>\n"
102 " <ladspa:Default>\n";
103 for (unsigned int i
= 0; i
< count
; i
++) {
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";
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"
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());
143 rdf
+= "</rdf:RDF>\n";
145 printf("%s\n", rdf
.c_str());
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)
153 const char *ind
= " ";
156 if (ports
!= "") ports
+= " , ";
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";
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
;
173 if (!strcmp(symbol
, "in_r"))
174 ss
<< ind
<< "pg:membership [ pg:group <#stereoIn>; pg:role pg:rightChannel ] ;" << endl
;
176 if (!strcmp(symbol
, "out_l"))
177 ss
<< ind
<< "pg:membership [ pg:group <#stereoOut>; pg:role pg:leftChannel ] ;" << endl
;
179 if (!strcmp(symbol
, "out_r"))
180 ss
<< ind
<< "pg:membership [ pg:group <#stereoOut>; pg:role pg:rightChannel ] ;" << endl
;
185 static const char *units
[] = {
191 "ue:cent", // - ask SWH (or maybe ue2: and write the extension by myself)
192 "ue:semitone12TET", // - ask SWH
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
)
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
+= " , ";
211 if (pp
.flags
& PF_PROP_OUTPUT
)
212 ss
<< ind
<< "a lv2:OutputPort ;\n";
214 ss
<< ind
<< "a lv2:InputPort ;\n";
215 if (type
== PF_STRING
)
216 ss
<< ind
<< "a strport:StringPort ;\n";
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";
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";
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";
275 struct lv2_port_base
{
277 std::string symbol
, name
, extras
, microname
;
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() {
301 const char *ind
= " ";
303 to_stream_base(ss
, ind
, "lv2:AudioPort");
310 struct lv2_event_port_base
: public lv2_port_base
312 std::string
to_string() {
314 const char *ind
= " ";
316 to_stream_base(ss
, ind
, "lv2ev:EventPort");
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
;
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;
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() {
373 const char *ind
= " ";
375 to_stream_base(ss
, ind
, "lv2:ControlPort");
377 ss
<< ind
<< "lv2:portProperty lv2:toggled ;\n";
379 ss
<< ind
<< "lv2:portProperty lv2:integer ;\n";
381 ss
<< ind
<< "lv2:default " << def_value
<< " ;\n";
383 ss
<< ind
<< "lv2:minimum " << min
<< " ;\n";
385 ss
<< ind
<< "lv2:maximum " << max
<< " ;\n";
392 struct lv2_plugin_info
: public plugin_info_iface
398 /// Plugin label (short name)
400 /// Plugin class (category)
401 std::string category
;
402 /// Plugin micro-name
403 std::string microname
;
404 /// Extra declarations for LV2
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
) {
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
);
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
);
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
);
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
;
451 void make_ttl(string path_prefix
)
453 if (path_prefix
.empty())
455 fprintf(stderr
, "Path parameter is required for TTL mode\n");
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"
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";
504 string gui_uri
= "<http://calf.sourceforge.net/plugins/gui/gtk2-gui>";
505 gui_header
= gui_uri
+ "\n"
507 " uiext:binary <calflv2gui.so> ;\n"
508 " uiext:requiredFeature uiext:makeResident .\n"
510 #ifdef ENABLE_EXPERIMENTAL
511 "<http://calf.sourceforge.net/small_plugins/gui/gtk2-gui>\n"
513 " uiext:binary <calflv2gui.so> ;\n"
514 " uiext:requiredFeature uiext:makeResident .\n"
520 for (unsigned int i
= 0; i
< plugins
.size(); i
++) {
521 plugin_metadata_iface
*pi
= plugins
[i
];
522 const ladspa_plugin_info
&lpi
= pi
->get_plugin_info();
523 string uri
= string("<http://calf.sourceforge.net/plugins/") + string(lpi
.label
) + ">";
525 ttl
= "@prefix : " + uri
+ " .\n" + header
+ gui_header
;
528 for (int j
= 0; j
< pi
->get_param_count(); j
++)
530 parameter_properties
&props
= *pi
->get_param_props(j
);
531 if (props
.flags
& PF_PROP_OUTPUT
)
532 ttl
+= gui_uri
+ " uiext:portNotification [\n uiext:plugin " + uri
+ " ;\n uiext:portIndex " + i2s(j
) + "\n] .\n\n";
536 ttl
+= uri
+ " a lv2:Plugin ;\n";
538 if (classes
.count(lpi
.plugin_type
))
539 ttl
+= " a " + classes
[lpi
.plugin_type
]+" ;\n";
542 ttl
+= " doap:name \""+string(lpi
.name
)+"\" ;\n";
543 ttl
+= " doap:maintainer [ foaf:name \""+string(lpi
.maker
)+"\" ; ] ;\n";
546 ttl
+= " uiext:ui <http://calf.sourceforge.net/plugins/gui/gtk2-gui> ;\n";
547 ttl
+= " lv2:optionalFeature <http://lv2plug.in/ns/ext/instance-access> ;\n";
548 ttl
+= " lv2:optionalFeature <http://lv2plug.in/ns/ext/data-access> ;\n";
551 ttl
+= " doap:license <http://usefulinc.com/doap/licenses/lgpl> ;\n";
552 ttl
+= " dc:replaces <ladspa:" + i2s(lpi
.unique_id
) + "> ;\n";
553 // XXXKF not really optional for now, to be honest
554 ttl
+= " lv2:optionalFeature epp:supportsStrictBounds ;\n";
555 if (pi
->is_rt_capable())
556 ttl
+= " lv2:optionalFeature lv2:hardRtCapable ;\n";
559 if (pi
->requires_midi()) {
560 ttl
+= " lv2:requiredFeature <" LV2_EVENT_URI
"> ;\n";
561 ttl
+= " lv2:requiredFeature <" LV2_URI_MAP_URI
"> ;\n";
564 ttl
+= " lv2:optionalFeature <" LV2_EVENT_URI
"> ;\n";
565 ttl
+= " lv2:optionalFeature <" LV2_URI_MAP_URI
"> ;\n";
569 if (pi
->requires_message_context())
571 ttl
+= " lv2:requiredFeature <http://lv2plug.in/ns/dev/contexts> ;\n";
572 ttl
+= " lv2:requiredFeature <" LV2_CONTEXT_MESSAGE
"> ;\n";
573 ttl
+= " lv2ctx:requiredContext lv2ctx:MessageContext ;\n";
575 if (pi
->requires_string_ports())
576 ttl
+= " lv2:requiredFeature <http://lv2plug.in/ns/dev/string-port#StringTransfer> ;\n";
580 const char *in_names
[] = { "in_l", "in_r" };
581 const char *out_names
[] = { "out_l", "out_r" };
582 for (int i
= 0; i
< pi
->get_input_count(); i
++)
583 add_port(ports
, in_names
[i
], in_names
[i
], "Input", pn
++);
584 for (int i
= 0; i
< pi
->get_output_count(); i
++)
585 add_port(ports
, out_names
[i
], out_names
[i
], "Output", pn
++);
586 for (int i
= 0; i
< pi
->get_param_count(); i
++)
587 add_ctl_port(ports
, *pi
->get_param_props(i
), pn
++, pi
, i
);
588 if (pi
->get_midi()) {
589 add_port(ports
, "event_in", "Event", "Input", pn
++, "lv2ev:EventPort", true);
592 ttl
+= " lv2:port " + ports
+ "\n";
595 FILE *f
= fopen((path_prefix
+string(lpi
.label
)+".ttl").c_str(), "w");
596 fprintf(f
, "%s\n", ttl
.c_str());
600 calf_plugins::get_all_small_plugins(&lpl
);
601 for (unsigned int i
= 0; i
< lpl
.size(); i
++)
603 lv2_plugin_info
*pi
= lpl
[i
];
604 // Copy-pasted code is the root of all evil, I know!
605 string uri
= string("<http://calf.sourceforge.net/small_plugins/") + string(pi
->id
) + ">";
607 ttl
= "@prefix : " + uri
+ " .\n" + header
+ gui_header
;
609 ttl
+= uri
+ " a lv2:Plugin ;\n";
611 if (!pi
->category
.empty())
612 ttl
+= " a " + pi
->category
+" ;\n";
614 ttl
+= " doap:name \""+string(pi
->label
)+"\" ;\n";
615 ttl
+= " doap:license <http://usefulinc.com/doap/licenses/lgpl> ;\n";
616 if (!pi
->microname
.empty())
617 ttl
+= " <http://lv2plug.in/ns/dev/tiny-name> \"" + pi
->microname
+ "\" ;\n";
620 if (!pi
->ports
.empty())
623 for (unsigned int i
= 0; i
< pi
->ports
.size(); i
++)
627 ttl
+= pi
->ports
[i
]->to_string();
631 FILE *f
= fopen((path_prefix
+string(pi
->id
)+".ttl").c_str(), "w");
632 fprintf(f
, "%s\n", ttl
.c_str());
636 // Generate a manifest
639 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
640 "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
641 "@prefix kf: <http://foltman.com/ns/> .\n"
643 "kf:BooleanPlugin a rdfs:Class ; rdfs:label \"Boolean-oriented\" ; rdfs:subClassOf lv2:UtilityPlugin ; rdfs:comment \"\"\"Modules heavily inspired by digital electronics (gates, flip-flops etc.)\"\"\" .\n"
644 "kf:MathOperatorPlugin a rdfs:Class ; rdfs:label \"Math operators\" ; rdfs:subClassOf lv2:UtilityPlugin ; rdfs:comment \"\"\"Mathematical operators and utility functions\"\"\" .\n"
645 "kf:IntegerPlugin a rdfs:Class ; rdfs:label \"Integer-oriented\" ; rdfs:subClassOf lv2:UtilityPlugin ; rdfs:comment \"\"\"Operations using integer values (counters, multiplexers etc.)\"\"\" .\n"
646 "kf:MIDIPlugin a rdfs:Class ; rdfs:label \"MIDI\" ; rdfs:subClassOf lv2:UtilityPlugin ; rdfs:comment \"\"\"Operations on MIDI streams (filters, transposers, mappers etc.)\"\"\" .\n"
649 for (unsigned int i
= 0; i
< plugins
.size(); i
++)
650 ttl
+= string("<http://calf.sourceforge.net/plugins/")
651 + string(plugins
[i
]->get_plugin_info().label
)
652 + "> a lv2:Plugin ; lv2:binary <calf.so> ; rdfs:seeAlso <" + string(plugins
[i
]->get_plugin_info().label
) + ".ttl> .\n";
654 for (unsigned int i
= 0; i
< lpl
.size(); i
++)
655 ttl
+= string("<http://calf.sourceforge.net/small_plugins/")
657 + "> a lv2:Plugin ; lv2:binary <calf.so> ; rdfs:seeAlso <" + string(lpl
[i
]->id
) + ".ttl> .\n";
659 FILE *f
= fopen((path_prefix
+"manifest.ttl").c_str(), "w");
660 fprintf(f
, "%s\n", ttl
.c_str());
664 void make_ttl(string tmp
)
666 fprintf(stderr
, "LV2 not supported.\n");
670 void make_gui(string path_prefix
)
672 if (path_prefix
.empty())
674 fprintf(stderr
, "Path parameter is required for GUI mode\n");
677 vector
<plugin_metadata_iface
*> plugins
;
678 calf_plugins::get_all_plugins(plugins
);
679 path_prefix
+= "/gui-";
680 for (unsigned int i
= 0; i
< plugins
.size(); i
++)
682 plugin_metadata_iface
*pi
= plugins
[i
];
686 for (int j
= 0; j
< pi
->get_param_count(); j
++)
688 parameter_properties
&props
= *pi
->get_param_props(j
);
689 if (props
.flags
& PF_PROP_GRAPH
)
692 xml
<< "<table rows=\"" << pi
->get_param_count() << "\" cols=\"" << (graphs
? "4" : "3") << "\">\n";
693 for (int j
= 0; j
< pi
->get_param_count(); j
++)
696 xml
<< "\n <!-- -->\n\n";
697 parameter_properties
&props
= *pi
->get_param_props(j
);
698 string expand_x
= "expand-x=\"1\" ";
699 string fill_x
= "fill-x=\"1\" ";
700 string shrink_x
= "shrink-x=\"1\" ";
701 string pad_x
= "pad-x=\"10\" ";
702 string attach_x
= "attach-x=\"1\" attach-w=\"2\" ";
703 string attach_y
= "attach-y=\"" + i2s(j
) + "\" ";
704 string param
= "param=\"" + string(props
.short_name
) + "\" ";
705 string label
= " <align attach-x=\"0\" " + attach_y
+ " " + " align-x=\"1\"><label " + param
+ " /></align>\n";
706 string value
= " <value " + param
+ " " + "attach-x=\"2\" " + attach_y
+ pad_x
+ "/>\n";
707 string attach_xv
= "attach-x=\"1\" attach-w=\"1\" ";
709 if ((props
.flags
& PF_TYPEMASK
) == PF_ENUM
&&
710 (props
.flags
& PF_CTLMASK
) == PF_CTL_COMBO
)
712 ctl
= " <combo " + attach_x
+ attach_y
+ param
+ expand_x
+ pad_x
+ " />\n";
714 else if ((props
.flags
& PF_TYPEMASK
) == PF_BOOL
&&
715 (props
.flags
& PF_CTLMASK
) == PF_CTL_TOGGLE
)
717 ctl
= " <align " + attach_x
+ attach_y
+ expand_x
+ fill_x
+ pad_x
+ "><toggle " + param
+ "/></align>\n";
719 else if ((props
.flags
& PF_TYPEMASK
) == PF_BOOL
&&
720 (props
.flags
& PF_CTLMASK
) == PF_CTL_BUTTON
)
722 ctl
= " <button attach-x=\"0\" attach-w=\"3\" " + expand_x
+ attach_y
+ param
+ pad_x
+ "/>\n";
725 else if ((props
.flags
& PF_TYPEMASK
) == PF_BOOL
&&
726 (props
.flags
& PF_CTLMASK
) == PF_CTL_LED
)
728 ctl
= " <led " + attach_x
+ attach_y
+ param
+ shrink_x
+ pad_x
+ " />\n";
730 else if ((props
.flags
& PF_CTLMASK
) == PF_CTL_METER
)
732 if (props
.flags
& PF_CTLO_LABEL
) {
733 attach_x
= attach_xv
;
736 if (props
.flags
& PF_CTLO_REVERSE
)
737 ctl
= " <vumeter " + attach_x
+ attach_y
+ expand_x
+ fill_x
+ param
+ pad_x
+ "mode=\"2\"/>\n";
739 ctl
= " <vumeter " + attach_x
+ attach_y
+ expand_x
+ fill_x
+ param
+ pad_x
+ "/>\n";
740 if (props
.flags
& PF_CTLO_LABEL
)
743 else if ((props
.flags
& PF_CTLMASK
) != PF_CTL_FADER
)
745 if ((props
.flags
& PF_UNITMASK
) == PF_UNIT_DEG
)
746 ctl
= " <knob " + attach_xv
+ attach_y
+ shrink_x
+ param
+ " type=\"3\"/>\n";
748 ctl
= " <knob " + attach_xv
+ attach_y
+ shrink_x
+ param
+ " />\n";
753 ctl
+= " <hscale " + param
+ " " + attach_x
+ attach_y
+ " pad-x=\"10\"/>";
762 xml
<< " <if cond=\"directlink\">" << endl
;
763 xml
<< " <vbox expand-x=\"1\" fill-x=\"1\" attach-x=\"3\" attach-y=\"0\" attach-h=\"" << pi
->get_param_count() << "\">" << endl
;
764 for (int j
= 0; j
< pi
->get_param_count(); j
++)
766 parameter_properties
&props
= *pi
->get_param_props(j
);
767 if (props
.flags
& PF_PROP_GRAPH
)
769 xml
<< " <line-graph refresh=\"1\" width=\"160\" param=\"" << props
.short_name
<< "\"/>\n" << endl
;
772 xml
<< " </vbox>" << endl
;
773 xml
<< " </if>" << endl
;
776 FILE *f
= fopen((path_prefix
+string(pi
->get_id())+".xml").c_str(), "w");
777 fprintf(f
, "%s\n", xml
.str().c_str());
782 int main(int argc
, char *argv
[])
788 int c
= getopt_long(argc
, argv
, "hvm:p:", long_options
, &option_index
);
794 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]);
797 printf("%s\n", PACKAGE_STRING
);
801 if (mode
!= "rdf" && mode
!= "ttl" && mode
!= "gui") {
802 fprintf(stderr
, "calfmakerdf: Invalid mode %s\n", optarg
);
807 path_prefix
= optarg
;
815 else if (mode
== "rdf")
819 else if (mode
== "ttl")
820 make_ttl(path_prefix
);
822 else if (mode
== "gui")
823 make_gui(path_prefix
);
826 fprintf(stderr
, "calfmakerdf: Mode %s unsupported in this version\n", optarg
);