Vintage Delay: (temporarily?) disable LV2 BPM linking with host
[calf.git] / src / makerdf.cpp
blob04485d201836115f72053d1ef416a855af86110a
1 /* Calf DSP Library
2 * RDF file generator for LV2 plugins.
3 * Copyright (C) 2007-2011 Krzysztof Foltman and others.
4 * See AUTHORS file for a complete list.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
21 #include <calf/giface.h>
22 #include <calf/preset.h>
23 #include <calf/utils.h>
24 #if USE_LV2
25 #include <lv2.h>
26 #include <calf/lv2_event.h>
27 #include <calf/lv2_state.h>
28 #include <calf/lv2_uri_map.h>
29 #endif
30 #include <getopt.h>
31 #include <string.h>
32 #include <set>
34 using namespace std;
35 using namespace calf_utils;
36 using namespace calf_plugins;
38 static struct option long_options[] = {
39 {"help", 0, 0, 'h'},
40 {"version", 0, 0, 'v'},
41 {"mode", 1, 0, 'm'},
42 {"path", 1, 0, 'p'},
43 {0,0,0,0},
46 static FILE *open_and_check(const std::string &filename)
48 FILE *f = fopen(filename.c_str(), "w");
49 if (!f)
51 fprintf(stderr, "Cannot write file '%s': %s\n", filename.c_str(), strerror(errno));
52 exit(1);
54 return f;
57 #if USE_LV2
58 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)
60 stringstream ss;
61 const char *ind = " ";
64 if (ports != "") ports += " , ";
65 ss << "[\n";
66 if (direction) ss << ind << "a lv2:" << direction << "Port ;\n";
67 ss << ind << "a " << type << " ;\n";
68 ss << ind << "lv2:index " << pidx << " ;\n";
69 ss << ind << "lv2:symbol \"" << symbol << "\" ;\n";
70 ss << ind << "lv2:name \"" << name << "\" ;\n";
71 if (optional)
72 ss << ind << "lv2:portProperty lv2:connectionOptional ;\n";
73 if (!strcmp(type, "lv2ev:EventPort")) {
74 ss << ind << "lv2ev:supportsEvent lv2midi:MidiEvent ;\n";
76 if (!strcmp(std::string(symbol, 0, 4).c_str(), "in_l"))
77 ss << ind << "lv2:designation pg:left ;\n"
78 << ind << "pg:group :in ;" << endl;
79 else
80 if (!strcmp(std::string(symbol, 0, 4).c_str(), "in_r"))
81 ss << ind << "lv2:designation pg:right ;\n"
82 << ind << "pg:group :in ;" << endl;
83 else
84 if (!strcmp(std::string(symbol, 0, 5).c_str(), "out_l"))
85 ss << ind << "lv2:designation pg:left ;\n"
86 << ind << "pg:group :out ;" << endl;
87 else
88 if (!strcmp(std::string(symbol, 0, 5).c_str(), "out_r"))
89 ss << ind << "lv2:designation pg:right ;\n"
90 << ind << "pg:group :out ;" << endl;
91 ss << " ]";
92 ports += ss.str();
95 static const char *units[] = {
96 "ue:db",
97 "ue:coef",
98 "ue:hz",
99 "ue:s",
100 "ue:ms",
101 "ue:cent",
102 "ue:semitone12TET",
103 "ue:bpm",
104 "ue:degree",
105 "ue:midiNote",
106 NULL // rotations per minute
109 //////////////// To all haters: calm down, I'll rewrite it to use the new interface one day
111 static bool add_ctl_port(string &ports, const parameter_properties &pp, int pidx, const plugin_metadata_iface *pmi, int param)
113 stringstream ss;
114 const char *ind = " ";
116 parameter_flags type = (parameter_flags)(pp.flags & PF_TYPEMASK);
117 uint8_t unit = (pp.flags & PF_UNITMASK) >> 24;
119 if (ports != "") ports += " , ";
120 ss << "[\n";
121 if (pp.flags & PF_PROP_OUTPUT)
122 ss << ind << "a lv2:OutputPort ;\n";
123 else
124 ss << ind << "a lv2:InputPort ;\n";
125 ss << ind << "a lv2:ControlPort ;\n";
126 ss << ind << "lv2:index " << pidx << " ;\n";
127 ss << ind << "lv2:symbol \"" << pp.short_name << "\" ;\n";
128 ss << ind << "lv2:name \"" << pp.name << "\" ;\n";
129 if ((pp.flags & PF_CTLMASK) == PF_CTL_BUTTON)
130 ss << ind << "lv2:portProperty epp:trigger ;\n";
131 if (!(pp.flags & PF_PROP_NOBOUNDS))
132 ss << ind << "lv2:portProperty epp:hasStrictBounds ;\n";
133 if (pp.flags & PF_PROP_EXPENSIVE)
134 ss << ind << "lv2:portProperty epp:expensive ;\n";
135 if (pp.flags & PF_PROP_OPTIONAL)
136 ss << ind << "lv2:portProperty lv2:connectionOptional ;\n";
137 if (pmi->is_noisy(param))
138 ss << ind << "lv2:portProperty epp:causesArtifacts ;\n";
139 if (!pmi->is_cv(param))
140 ss << ind << "lv2:portProperty epp:notAutomatic ;\n";
141 if (pp.flags & PF_PROP_OUTPUT_GAIN)
142 ss << ind << "lv2:designation param:gain ;\n";
143 if (type == PF_BOOL)
144 ss << ind << "lv2:portProperty lv2:toggled ;\n";
145 else if (type == PF_ENUM)
147 ss << ind << "lv2:portProperty lv2:integer ;\n";
148 ss << ind << "lv2:portProperty lv2:enumeration ;\n";
149 for (int i = (int)pp.min; i <= (int)pp.max; i++)
150 ss << ind << "lv2:scalePoint [ rdfs:label \"" << pp.choices[i - (int)pp.min] << "\"; rdf:value " << i <<" ] ;\n";
152 else if (type == PF_INT || type == PF_ENUM_MULTI)
153 ss << ind << "lv2:portProperty lv2:integer ;\n";
154 else if ((pp.flags & PF_SCALEMASK) == PF_SCALE_LOG)
155 ss << ind << "lv2:portProperty epp:logarithmic ;\n";
156 ss << showpoint;
157 if (!(pp.flags & PF_PROP_OUTPUT))
158 ss << ind << "lv2:default " << pp.def_value << " ;\n";
159 ss << ind << "lv2:minimum " << pp.min << " ;\n";
160 ss << ind << "lv2:maximum " << pp.max << " ;\n";
161 // XXX This value does not seem to match the definition of the property
162 //if (pp.step > 1)
163 // ss << ind << "epp:rangeSteps " << pp.step << " ;\n";
164 if (unit > 0 && unit < (sizeof(units) / sizeof(char *)) && units[unit - 1] != NULL)
165 ss << ind << "ue:unit " << units[unit - 1] << " ;\n";
167 // for now I assume that the only tempo passed is the tempo the plugin should operate with
168 // this may change as more complex plugins are added
170 // Markus: This entry binds BPM to the hosts BPM setting, so delays
171 // are fixed within multitrack environments, disabling it to fix
172 // some reported bugs
174 // if (unit == (PF_UNIT_BPM >> 24))
175 // ss << ind << "lv2:designation <http://lv2plug.in/ns/ext/time#beatsPerMinute> ;\n";
177 ss << " ]";
178 ports += ss.str();
179 return true;
182 void make_ttl(string path_prefix)
184 if (path_prefix.empty())
186 fprintf(stderr, "Path parameter is required for TTL mode\n");
187 exit(1);
189 string header;
191 header =
192 "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
193 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
194 "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
195 "@prefix dct: <http://purl.org/dc/terms/> .\n"
196 "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"
197 "@prefix uiext: <http://lv2plug.in/ns/extensions/ui#> .\n"
198 "@prefix lv2ev: <http://lv2plug.in/ns/ext/event#> .\n"
199 "@prefix lv2midi: <http://lv2plug.in/ns/ext/midi#> .\n"
200 "@prefix lv2ctx: <http://lv2plug.in/ns/dev/contexts#> .\n"
201 "@prefix strport: <http://lv2plug.in/ns/dev/string-port#> .\n"
202 "@prefix pg: <http://lv2plug.in/ns/ext/port-groups#> .\n"
203 "@prefix ue: <http://lv2plug.in/ns/extensions/units#> .\n"
204 "@prefix epp: <http://lv2plug.in/ns/ext/port-props#> .\n"
205 "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"
206 "@prefix param: <http://lv2plug.in/ns/ext/parameters#> .\n"
208 "\n"
211 printf("Retrieving plugin list\n");
212 fflush(stdout);
213 const plugin_registry::plugin_vector &plugins = plugin_registry::instance().get_all();
215 map<string, string> classes;
217 printf("Creating a list of plugin classes\n");
218 fflush(stdout);
219 const char *ptypes[] = {
220 "Flanger", "Reverb", "Generator", "Instrument", "Oscillator",
221 "Utility", "Converter", "Analyser", "Mixer", "Simulator",
222 "Delay", "Modulator", "Phaser", "Chorus", "Filter",
223 "Lowpass", "Highpass", "Bandpass", "Comb", "Allpass",
224 "Amplifier", "Distortion", "Waveshaper", "Dynamics", "Compressor",
225 "Expander", "Limiter", "Gate", "EQ", "ParaEQ", "MultiEQ", "Spatial",
226 "Spectral", "Pitch", "Envelope", "Function", "Constant",
227 NULL
230 for(const char **p = ptypes; *p; p++) {
231 string name = string(*p) + "Plugin";
232 classes[name] = "lv2:" + name;
234 classes["SynthesizerPlugin"] = "lv2:InstrumentPlugin";
236 string plugin_uri_prefix = "http://calf.sourceforge.net/plugins/";
238 string gui_header;
240 #if USE_LV2_GUI
241 string gtkgui_uri = "<http://calf.sourceforge.net/plugins/gui/gtk2-gui>";
242 gui_header = gtkgui_uri + "\n"
243 " a uiext:GtkUI ;\n"
244 " uiext:binary <calflv2gui.so> ;\n"
245 " uiext:requiredFeature uiext:makeResident .\n"
246 "\n"
248 #endif
250 map<string, pair<string, string> > id_to_info;
252 for (unsigned int i = 0; i < plugins.size(); i++) {
253 const plugin_metadata_iface *pi = plugins[i];
254 const ladspa_plugin_info &lpi = pi->get_plugin_info();
255 printf("Generating a .ttl file for plugin '%s'\n", lpi.label);
256 fflush(stdout);
257 string unquoted_uri = plugin_uri_prefix + string(lpi.label);
258 string uri = string("<" + unquoted_uri + ">");
259 id_to_info[pi->get_id()] = make_pair(lpi.label, uri);
260 string ttl;
261 ttl = "@prefix : <" + unquoted_uri + "#> .\n" + header + gui_header;
263 #if USE_LV2_GUI
264 for (int j = 0; j < pi->get_param_count(); j++)
266 const parameter_properties &props = *pi->get_param_props(j);
267 if (props.flags & PF_PROP_OUTPUT)
269 string portnot = " uiext:portNotification [\n uiext:plugin " + uri + " ;\n uiext:portIndex " + i2s(j) + "\n] .\n\n";
270 ttl += gtkgui_uri + portnot;
273 #endif
275 if(pi->get_input_count() == 1) {
276 ttl += ":in a pg:MonoGroup , pg:InputGroup ;\n"
277 " lv2:symbol \"in\" ;\n"
278 " rdfs:label \"Input\" .\n\n";
279 } else if(pi->get_input_count() >= 2) {
280 ttl += ":in a pg:StereoGroup , pg:InputGroup ;\n"
281 " lv2:symbol \"in\" ;\n"
282 " rdfs:label \"Input\" .\n\n";
284 if(pi->get_output_count() >= 2) {
285 ttl += ":out a pg:StereoGroup , pg:OutputGroup ;\n"
286 " lv2:symbol \"out\" ;\n"
287 " rdfs:label \"Output\" .\n\n";
290 ttl += uri + " a lv2:Plugin ;\n";
292 if (classes.count(lpi.plugin_type))
293 ttl += " a " + classes[lpi.plugin_type]+" ;\n";
296 ttl += " doap:name \""+string(lpi.name)+"\" ;\n";
297 ttl += " doap:maintainer [ foaf:name \""+string(lpi.maker)+"\" ; ] ;\n";
299 #if USE_LV2_GUI
300 ttl += " uiext:ui <http://calf.sourceforge.net/plugins/gui/gtk2-gui> ;\n";
301 ttl += " lv2:optionalFeature <http://lv2plug.in/ns/ext/instance-access> ;\n";
302 ttl += " lv2:optionalFeature <http://lv2plug.in/ns/ext/data-access> ;\n";
303 #endif
305 ttl += " doap:license <http://usefulinc.com/doap/licenses/lgpl> ;\n";
306 ttl += " dct:replaces <urn:ladspa:" + i2s(lpi.unique_id) + "> ;\n";
307 // XXXKF not really optional for now, to be honest
308 ttl += " lv2:optionalFeature epp:supportsStrictBounds ;\n";
309 if (pi->is_rt_capable())
310 ttl += " lv2:optionalFeature lv2:hardRTCapable ;\n";
311 if (pi->get_midi())
313 if (pi->requires_midi()) {
314 ttl += " lv2:requiredFeature <" LV2_URI_MAP_URI "> ;\n";
316 else {
317 ttl += " lv2:optionalFeature <" LV2_URI_MAP_URI "> ;\n";
321 vector<string> configure_keys;
322 pi->get_configure_vars(configure_keys);
323 if (!configure_keys.empty())
325 ttl += " lv2:extensionData <" LV2_STATE__interface "> ;\n";
328 if(pi->get_input_count() >= 1) {
329 ttl += " pg:mainInput :in ;\n";
331 if(pi->get_output_count() >= 2) {
332 ttl += " pg:mainOutput :out ;\n";
335 string ports = "";
336 int pn = 0;
337 const char *in_symbols[] = { "in_l", "in_r", "sidechain", "sidechain2" };
338 const char *in_names[] = { "In L", "In R", "Sidechain", "Sidechain 2" };
339 const char *out_symbols[] = { "out_l", "out_r", "out_l_2", "out_r_2", "out_l_3", "out_r_3", "out_l_4", "out_r_4" };
340 const char *out_names[] = { "Out L", "Out R", "Out L 2", "Out R 2", "Out L 3", "Out R 3", "Out L 4", "Out R 4" };
341 for (int i = 0; i < pi->get_input_count(); i++)
342 if(i <= pi->get_input_count() - pi->get_inputs_optional() - 1 && !(i == 1 && pi->get_simulate_stereo_input()))
343 add_port(ports, in_symbols[i], in_names[i], "Input", pn++);
344 else
345 add_port(ports, in_symbols[i], in_names[i], "Input", pn++, "lv2:AudioPort", true);
346 for (int i = 0; i < pi->get_output_count(); i++)
347 if(i <= pi->get_output_count() - pi->get_outputs_optional() - 1)
348 add_port(ports, out_symbols[i], out_names[i], "Output", pn++);
349 else
350 add_port(ports, out_symbols[i], out_names[i], "Output", pn++, "lv2:AudioPort", true);
351 for (int i = 0; i < pi->get_param_count(); i++)
352 if (add_ctl_port(ports, *pi->get_param_props(i), pn, pi, i))
353 pn++;
354 if (pi->get_midi()) {
355 add_port(ports, "event_in", "Event", "Input", pn++, "lv2ev:EventPort", true);
357 if (!ports.empty())
358 ttl += " lv2:port " + ports + "\n";
359 ttl += ".\n\n";
361 FILE *f = open_and_check(path_prefix+string(lpi.label)+".ttl");
362 fprintf(f, "%s\n", ttl.c_str());
363 fclose(f);
365 // Generate a manifest
366 printf("Writing presets\n");
367 fflush(stdout);
369 // Prefixes for the preset TTL
370 string presets_ttl_head =
371 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
372 "@prefix lv2p: <http://lv2plug.in/ns/ext/presets#> .\n"
373 "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
374 "@prefix dct: <http://purl.org/dc/terms/> .\n"
375 "\n"
378 // Prefixes for the manifest TTL
379 string ttl = presets_ttl_head;
381 calf_plugins::get_builtin_presets().load_defaults(true);
382 calf_plugins::preset_vector &factory_presets = calf_plugins::get_builtin_presets().presets;
384 ttl += "\n";
386 // We'll collect TTL for presets here, maps plugin label to TTL generated so far
387 map<string, string> preset_data;
389 for (unsigned int i = 0; i < factory_presets.size(); i++)
391 plugin_preset &pr = factory_presets[i];
392 map<string, pair<string, string> >::iterator ilm = id_to_info.find(pr.plugin);
393 if (ilm == id_to_info.end())
394 continue;
396 string presets_ttl;
397 // if this is the first preset, add a header
398 if (!preset_data.count(ilm->second.first))
399 presets_ttl = presets_ttl_head;
401 string uri = "<http://calf.sourceforge.net/factory_presets#"
402 + pr.plugin + "_" + pr.get_safe_name()
403 + ">";
404 ttl += ilm->second.second + " lv2p:hasPreset\n " + uri + " .\n";
406 presets_ttl += uri +
407 " a lv2p:Preset ;\n"
408 " rdfs:label \"" + pr.name + "\" ;\n"
409 " lv2:appliesTo " + ilm->second.second + " ;\n"
410 " lv2:port \n"
413 unsigned int count = min(pr.param_names.size(), pr.values.size());
414 for (unsigned int j = 0; j < count; j++)
416 presets_ttl += " [ lv2:symbol \"" + pr.param_names[j] + "\" ; lv2p:value " + ff2s(pr.values[j]) + " ] ";
417 if (j < count - 1)
418 presets_ttl += ',';
419 presets_ttl += '\n';
421 presets_ttl += ".\n\n";
423 preset_data[ilm->second.first] += presets_ttl;
425 for (map<string, string>::iterator i = preset_data.begin(); i != preset_data.end(); i++)
427 FILE *f = open_and_check(path_prefix + "presets-" + i->first + ".ttl");
428 fprintf(f, "%s\n", i->second.c_str());
429 fclose(f);
432 printf("Generating a manifest file\n");
433 fflush(stdout);
434 for (unsigned int i = 0; i < plugins.size(); i++)
436 string label = plugins[i]->get_plugin_info().label;
437 ttl += string("<" + plugin_uri_prefix)
438 + string(plugins[i]->get_plugin_info().label)
439 + "> a lv2:Plugin ;\n dct:replaces <urn:ladspa:"
440 + i2s(plugins[i]->get_plugin_info().unique_id) + "> ;\n "
441 + "lv2:binary <calf.so> ; rdfs:seeAlso <" + label + ".ttl> ";
442 if (preset_data.count(label))
443 ttl += ", <presets-" + label + ".ttl>";
444 ttl += ".\n";
447 FILE *f = open_and_check(path_prefix+"manifest.ttl");
448 fprintf(f, "%s\n", ttl.c_str());
449 fclose(f);
453 #else
454 void make_ttl(string tmp)
456 fprintf(stderr, "LV2 not supported.\n");
459 #endif
461 void make_gui(string path_prefix)
463 if (path_prefix.empty())
465 fprintf(stderr, "Path parameter is required for GUI mode\n");
466 exit(1);
468 const plugin_registry::plugin_vector &plugins = plugin_registry::instance().get_all();
469 path_prefix += "/gui-";
470 for (unsigned int i = 0; i < plugins.size(); i++)
472 const plugin_metadata_iface *pi = plugins[i];
474 // check for empty item after all the params
475 assert(pi->get_id());
476 assert(pi->get_param_props(pi->get_param_count())->short_name == NULL);
477 assert(pi->get_param_props(pi->get_param_count())->name == NULL);
479 stringstream xml;
480 int graphs = 0;
481 for (int j = 0; j < pi->get_param_count(); j++)
483 const parameter_properties &props = *pi->get_param_props(j);
484 if (props.flags & PF_PROP_GRAPH)
485 graphs++;
487 xml << "<table rows=\"" << pi->get_param_count() << "\" cols=\"" << (graphs ? "4" : "3") << "\">\n";
488 for (int j = 0; j < pi->get_param_count(); j++)
490 if (j)
491 xml << "\n <!-- -->\n\n";
492 const parameter_properties &props = *pi->get_param_props(j);
493 if (!props.short_name)
495 fprintf(stderr, "Plugin %s is missing short name for parameter %d\n", pi->get_name(), j);
496 exit(1);
498 string expand_x = "expand-x=\"1\" ";
499 string fill_x = "fill-x=\"1\" ";
500 string shrink_x = "shrink-x=\"1\" ";
501 string pad_x = "pad-x=\"10\" ";
502 string attach_x = "attach-x=\"1\" attach-w=\"2\" ";
503 string attach_y = "attach-y=\"" + i2s(j) + "\" ";
504 string param = "param=\"" + string(props.short_name) + "\" ";
505 string label = " <align attach-x=\"0\" " + attach_y + " " + " align-x=\"1\"><label " + param + " /></align>\n";
506 string value = " <value " + param + " " + "attach-x=\"2\" " + attach_y + pad_x + "/>\n";
507 string attach_xv = "attach-x=\"1\" attach-w=\"1\" ";
508 string ctl;
509 if ((props.flags & PF_TYPEMASK) == PF_ENUM &&
510 (props.flags & PF_CTLMASK) == PF_CTL_COMBO)
512 ctl = " <combo " + attach_x + attach_y + param + expand_x + pad_x + " />\n";
514 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
515 (props.flags & PF_CTLMASK) == PF_CTL_TOGGLE)
517 ctl = " <align " + attach_x + attach_y + expand_x + fill_x + pad_x + "><toggle " + param + "/></align>\n";
519 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
520 (props.flags & PF_CTLMASK) == PF_CTL_BUTTON)
522 ctl = " <button attach-x=\"0\" attach-w=\"3\" " + expand_x + attach_y + param + pad_x + "/>\n";
523 label.clear();
525 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
526 (props.flags & PF_CTLMASK) == PF_CTL_LED)
528 ctl = " <led " + attach_x + attach_y + param + shrink_x + pad_x + " />\n";
530 else if ((props.flags & PF_CTLMASK) == PF_CTL_METER)
532 if (props.flags & PF_CTLO_LABEL) {
533 attach_x = attach_xv;
534 pad_x.clear();
536 if (props.flags & PF_CTLO_REVERSE)
537 ctl = " <vumeter " + attach_x + attach_y + expand_x + fill_x + param + pad_x + "mode=\"2\"/>\n";
538 else
539 ctl = " <vumeter " + attach_x + attach_y + expand_x + fill_x + param + pad_x + "/>\n";
540 if (props.flags & PF_CTLO_LABEL)
541 ctl += value;
543 else if ((props.flags & PF_CTLMASK) != PF_CTL_FADER)
545 if ((props.flags & PF_UNITMASK) == PF_UNIT_DEG)
546 ctl = " <knob " + attach_xv + attach_y + shrink_x + param + " type=\"3\"/>\n";
547 else
548 ctl = " <knob " + attach_xv + attach_y + shrink_x + param + " />\n";
549 ctl += value;
551 else
553 ctl += " <hscale " + param + " " + attach_x + attach_y + " pad-x=\"10\"/>";
555 if (!label.empty())
556 xml << label;
557 if (!ctl.empty())
558 xml << ctl;
560 if (graphs)
562 xml << " <if cond=\"directlink\">" << endl;
563 xml << " <vbox expand-x=\"1\" fill-x=\"1\" attach-x=\"3\" attach-y=\"0\" attach-h=\"" << pi->get_param_count() << "\">" << endl;
564 for (int j = 0; j < pi->get_param_count(); j++)
566 const parameter_properties &props = *pi->get_param_props(j);
567 if (props.flags & PF_PROP_GRAPH)
569 xml << " <line-graph refresh=\"1\" width=\"160\" param=\"" << props.short_name << "\"/>\n" << endl;
572 xml << " </vbox>" << endl;
573 xml << " </if>" << endl;
575 xml << "</table>\n";
576 FILE *f = open_and_check(path_prefix+string(pi->get_id())+".xml");
577 fprintf(f, "%s\n", xml.str().c_str());
578 fclose(f);
582 int main(int argc, char *argv[])
584 string mode = "rdf";
585 string path_prefix;
586 while(1) {
587 int option_index;
588 int c = getopt_long(argc, argv, "hvm:p:", long_options, &option_index);
589 if (c == -1)
590 break;
591 switch(c) {
592 case 'h':
593 case '?':
594 printf("LV2 TTL / XML GUI generator for Calf plugin pack\nSyntax: %s [--help] [--version] [--mode rdf|ttl|gui] [--path <path>]\n", argv[0]);
595 return 0;
596 case 'v':
597 printf("%s\n", PACKAGE_STRING);
598 return 0;
599 case 'm':
600 mode = optarg;
601 if (mode != "rdf" && mode != "ttl" && mode != "gui") {
602 fprintf(stderr, "calfmakerdf: Invalid mode %s\n", optarg);
603 return 1;
605 break;
606 case 'p':
607 path_prefix = optarg;
608 if (path_prefix.empty())
610 fprintf(stderr, "calfmakerdf: Path prefix must not be empty\n");
611 exit(1);
613 if (path_prefix[path_prefix.length() - 1] != '/')
614 path_prefix += '/';
615 break;
618 if (false)
621 #if USE_LV2
622 else if (mode == "ttl")
623 make_ttl(path_prefix);
624 #endif
625 else if (mode == "gui")
626 make_gui(path_prefix);
627 else
629 fprintf(stderr, "calfmakerdf: Mode '%s' unsupported in this version\n", mode.c_str());
630 return 1;
632 return 0;