Vocoder: bugfixes
[calf.git] / src / makerdf.cpp
blob6672eda440a0145bef2e3992609e354bedf2b05f
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
169 if (unit == (PF_UNIT_BPM >> 24))
170 ss << ind << "lv2:designation <http://lv2plug.in/ns/ext/time#beatsPerMinute> ;\n";
172 ss << " ]";
173 ports += ss.str();
174 return true;
177 void make_ttl(string path_prefix)
179 if (path_prefix.empty())
181 fprintf(stderr, "Path parameter is required for TTL mode\n");
182 exit(1);
184 string header;
186 header =
187 "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
188 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
189 "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
190 "@prefix dct: <http://purl.org/dc/terms/> .\n"
191 "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"
192 "@prefix uiext: <http://lv2plug.in/ns/extensions/ui#> .\n"
193 "@prefix lv2ev: <http://lv2plug.in/ns/ext/event#> .\n"
194 "@prefix lv2midi: <http://lv2plug.in/ns/ext/midi#> .\n"
195 "@prefix lv2ctx: <http://lv2plug.in/ns/dev/contexts#> .\n"
196 "@prefix strport: <http://lv2plug.in/ns/dev/string-port#> .\n"
197 "@prefix pg: <http://lv2plug.in/ns/ext/port-groups#> .\n"
198 "@prefix ue: <http://lv2plug.in/ns/extensions/units#> .\n"
199 "@prefix epp: <http://lv2plug.in/ns/ext/port-props#> .\n"
200 "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"
201 "@prefix param: <http://lv2plug.in/ns/ext/parameters#> .\n"
203 "\n"
206 printf("Retrieving plugin list\n");
207 fflush(stdout);
208 const plugin_registry::plugin_vector &plugins = plugin_registry::instance().get_all();
210 map<string, string> classes;
212 printf("Creating a list of plugin classes\n");
213 fflush(stdout);
214 const char *ptypes[] = {
215 "Flanger", "Reverb", "Generator", "Instrument", "Oscillator",
216 "Utility", "Converter", "Analyser", "Mixer", "Simulator",
217 "Delay", "Modulator", "Phaser", "Chorus", "Filter",
218 "Lowpass", "Highpass", "Bandpass", "Comb", "Allpass",
219 "Amplifier", "Distortion", "Waveshaper", "Dynamics", "Compressor",
220 "Expander", "Limiter", "Gate", "EQ", "ParaEQ", "MultiEQ", "Spatial",
221 "Spectral", "Pitch", "Envelope", "Function", "Constant",
222 NULL
225 for(const char **p = ptypes; *p; p++) {
226 string name = string(*p) + "Plugin";
227 classes[name] = "lv2:" + name;
229 classes["SynthesizerPlugin"] = "lv2:InstrumentPlugin";
231 string plugin_uri_prefix = "http://calf.sourceforge.net/plugins/";
233 string gui_header;
235 #if USE_LV2_GUI
236 string gtkgui_uri = "<http://calf.sourceforge.net/plugins/gui/gtk2-gui>";
237 gui_header = gtkgui_uri + "\n"
238 " a uiext:GtkUI ;\n"
239 " uiext:binary <calflv2gui.so> ;\n"
240 " uiext:requiredFeature uiext:makeResident .\n"
241 "\n"
243 #endif
245 map<string, pair<string, string> > id_to_info;
247 for (unsigned int i = 0; i < plugins.size(); i++) {
248 const plugin_metadata_iface *pi = plugins[i];
249 const ladspa_plugin_info &lpi = pi->get_plugin_info();
250 printf("Generating a .ttl file for plugin '%s'\n", lpi.label);
251 fflush(stdout);
252 string unquoted_uri = plugin_uri_prefix + string(lpi.label);
253 string uri = string("<" + unquoted_uri + ">");
254 id_to_info[pi->get_id()] = make_pair(lpi.label, uri);
255 string ttl;
256 ttl = "@prefix : <" + unquoted_uri + "#> .\n" + header + gui_header;
258 #if USE_LV2_GUI
259 for (int j = 0; j < pi->get_param_count(); j++)
261 const parameter_properties &props = *pi->get_param_props(j);
262 if (props.flags & PF_PROP_OUTPUT)
264 string portnot = " uiext:portNotification [\n uiext:plugin " + uri + " ;\n uiext:portIndex " + i2s(j) + "\n] .\n\n";
265 ttl += gtkgui_uri + portnot;
268 #endif
270 if(pi->get_input_count() == 1) {
271 ttl += ":in a pg:MonoGroup , pg:InputGroup ;\n"
272 " lv2:symbol \"in\" ;\n"
273 " rdfs:label \"Input\" .\n\n";
274 } else if(pi->get_input_count() >= 2) {
275 ttl += ":in a pg:StereoGroup , pg:InputGroup ;\n"
276 " lv2:symbol \"in\" ;\n"
277 " rdfs:label \"Input\" .\n\n";
279 if(pi->get_output_count() >= 2) {
280 ttl += ":out a pg:StereoGroup , pg:OutputGroup ;\n"
281 " lv2:symbol \"out\" ;\n"
282 " rdfs:label \"Output\" .\n\n";
285 ttl += uri + " a lv2:Plugin ;\n";
287 if (classes.count(lpi.plugin_type))
288 ttl += " a " + classes[lpi.plugin_type]+" ;\n";
291 ttl += " doap:name \""+string(lpi.name)+"\" ;\n";
292 ttl += " doap:maintainer [ foaf:name \""+string(lpi.maker)+"\" ; ] ;\n";
294 #if USE_LV2_GUI
295 ttl += " uiext:ui <http://calf.sourceforge.net/plugins/gui/gtk2-gui> ;\n";
296 ttl += " lv2:optionalFeature <http://lv2plug.in/ns/ext/instance-access> ;\n";
297 ttl += " lv2:optionalFeature <http://lv2plug.in/ns/ext/data-access> ;\n";
298 #endif
300 ttl += " doap:license <http://usefulinc.com/doap/licenses/lgpl> ;\n";
301 ttl += " dct:replaces <urn:ladspa:" + i2s(lpi.unique_id) + "> ;\n";
302 // XXXKF not really optional for now, to be honest
303 ttl += " lv2:optionalFeature epp:supportsStrictBounds ;\n";
304 if (pi->is_rt_capable())
305 ttl += " lv2:optionalFeature lv2:hardRTCapable ;\n";
306 if (pi->get_midi())
308 if (pi->requires_midi()) {
309 ttl += " lv2:requiredFeature <" LV2_URI_MAP_URI "> ;\n";
311 else {
312 ttl += " lv2:optionalFeature <" LV2_URI_MAP_URI "> ;\n";
316 vector<string> configure_keys;
317 pi->get_configure_vars(configure_keys);
318 if (!configure_keys.empty())
320 ttl += " lv2:extensionData <" LV2_STATE__interface "> ;\n";
323 if(pi->get_input_count() >= 1) {
324 ttl += " pg:mainInput :in ;\n";
326 if(pi->get_output_count() >= 2) {
327 ttl += " pg:mainOutput :out ;\n";
330 string ports = "";
331 int pn = 0;
332 const char *in_symbols[] = { "in_l", "in_r", "sidechain", "sidechain2" };
333 const char *in_names[] = { "In L", "In R", "Sidechain", "Sidechain 2" };
334 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" };
335 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" };
336 for (int i = 0; i < pi->get_input_count(); i++)
337 if(i <= pi->get_input_count() - pi->get_inputs_optional() - 1 && !(i == 1 && pi->get_simulate_stereo_input()))
338 add_port(ports, in_symbols[i], in_names[i], "Input", pn++);
339 else
340 add_port(ports, in_symbols[i], in_names[i], "Input", pn++, "lv2:AudioPort", true);
341 for (int i = 0; i < pi->get_output_count(); i++)
342 if(i <= pi->get_output_count() - pi->get_outputs_optional() - 1)
343 add_port(ports, out_symbols[i], out_names[i], "Output", pn++);
344 else
345 add_port(ports, out_symbols[i], out_names[i], "Output", pn++, "lv2:AudioPort", true);
346 for (int i = 0; i < pi->get_param_count(); i++)
347 if (add_ctl_port(ports, *pi->get_param_props(i), pn, pi, i))
348 pn++;
349 if (pi->get_midi()) {
350 add_port(ports, "event_in", "Event", "Input", pn++, "lv2ev:EventPort", true);
352 if (!ports.empty())
353 ttl += " lv2:port " + ports + "\n";
354 ttl += ".\n\n";
356 FILE *f = open_and_check(path_prefix+string(lpi.label)+".ttl");
357 fprintf(f, "%s\n", ttl.c_str());
358 fclose(f);
360 // Generate a manifest
361 printf("Writing presets\n");
362 fflush(stdout);
364 // Prefixes for the preset TTL
365 string presets_ttl_head =
366 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
367 "@prefix lv2p: <http://lv2plug.in/ns/ext/presets#> .\n"
368 "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
369 "@prefix dct: <http://purl.org/dc/terms/> .\n"
370 "\n"
373 // Prefixes for the manifest TTL
374 string ttl = presets_ttl_head;
376 calf_plugins::get_builtin_presets().load_defaults(true);
377 calf_plugins::preset_vector &factory_presets = calf_plugins::get_builtin_presets().presets;
379 ttl += "\n";
381 // We'll collect TTL for presets here, maps plugin label to TTL generated so far
382 map<string, string> preset_data;
384 for (unsigned int i = 0; i < factory_presets.size(); i++)
386 plugin_preset &pr = factory_presets[i];
387 map<string, pair<string, string> >::iterator ilm = id_to_info.find(pr.plugin);
388 if (ilm == id_to_info.end())
389 continue;
391 string presets_ttl;
392 // if this is the first preset, add a header
393 if (!preset_data.count(ilm->second.first))
394 presets_ttl = presets_ttl_head;
396 string uri = "<http://calf.sourceforge.net/factory_presets#"
397 + pr.plugin + "_" + pr.get_safe_name()
398 + ">";
399 ttl += ilm->second.second + " lv2p:hasPreset\n " + uri + " .\n";
401 presets_ttl += uri +
402 " a lv2p:Preset ;\n"
403 " rdfs:label \"" + pr.name + "\" ;\n"
404 " lv2:appliesTo " + ilm->second.second + " ;\n"
405 " lv2:port \n"
408 unsigned int count = min(pr.param_names.size(), pr.values.size());
409 for (unsigned int j = 0; j < count; j++)
411 presets_ttl += " [ lv2:symbol \"" + pr.param_names[j] + "\" ; lv2p:value " + ff2s(pr.values[j]) + " ] ";
412 if (j < count - 1)
413 presets_ttl += ',';
414 presets_ttl += '\n';
416 presets_ttl += ".\n\n";
418 preset_data[ilm->second.first] += presets_ttl;
420 for (map<string, string>::iterator i = preset_data.begin(); i != preset_data.end(); i++)
422 FILE *f = open_and_check(path_prefix + "presets-" + i->first + ".ttl");
423 fprintf(f, "%s\n", i->second.c_str());
424 fclose(f);
427 printf("Generating a manifest file\n");
428 fflush(stdout);
429 for (unsigned int i = 0; i < plugins.size(); i++)
431 string label = plugins[i]->get_plugin_info().label;
432 ttl += string("<" + plugin_uri_prefix)
433 + string(plugins[i]->get_plugin_info().label)
434 + "> a lv2:Plugin ;\n dct:replaces <urn:ladspa:"
435 + i2s(plugins[i]->get_plugin_info().unique_id) + "> ;\n "
436 + "lv2:binary <calf.so> ; rdfs:seeAlso <" + label + ".ttl> ";
437 if (preset_data.count(label))
438 ttl += ", <presets-" + label + ".ttl>";
439 ttl += ".\n";
442 FILE *f = open_and_check(path_prefix+"manifest.ttl");
443 fprintf(f, "%s\n", ttl.c_str());
444 fclose(f);
448 #else
449 void make_ttl(string tmp)
451 fprintf(stderr, "LV2 not supported.\n");
454 #endif
456 void make_gui(string path_prefix)
458 if (path_prefix.empty())
460 fprintf(stderr, "Path parameter is required for GUI mode\n");
461 exit(1);
463 const plugin_registry::plugin_vector &plugins = plugin_registry::instance().get_all();
464 path_prefix += "/gui-";
465 for (unsigned int i = 0; i < plugins.size(); i++)
467 const plugin_metadata_iface *pi = plugins[i];
469 // check for empty item after all the params
470 assert(pi->get_id());
471 assert(pi->get_param_props(pi->get_param_count())->short_name == NULL);
472 assert(pi->get_param_props(pi->get_param_count())->name == NULL);
474 stringstream xml;
475 int graphs = 0;
476 for (int j = 0; j < pi->get_param_count(); j++)
478 const parameter_properties &props = *pi->get_param_props(j);
479 if (props.flags & PF_PROP_GRAPH)
480 graphs++;
482 xml << "<table rows=\"" << pi->get_param_count() << "\" cols=\"" << (graphs ? "4" : "3") << "\">\n";
483 for (int j = 0; j < pi->get_param_count(); j++)
485 if (j)
486 xml << "\n <!-- -->\n\n";
487 const parameter_properties &props = *pi->get_param_props(j);
488 if (!props.short_name)
490 fprintf(stderr, "Plugin %s is missing short name for parameter %d\n", pi->get_name(), j);
491 exit(1);
493 string expand_x = "expand-x=\"1\" ";
494 string fill_x = "fill-x=\"1\" ";
495 string shrink_x = "shrink-x=\"1\" ";
496 string pad_x = "pad-x=\"10\" ";
497 string attach_x = "attach-x=\"1\" attach-w=\"2\" ";
498 string attach_y = "attach-y=\"" + i2s(j) + "\" ";
499 string param = "param=\"" + string(props.short_name) + "\" ";
500 string label = " <align attach-x=\"0\" " + attach_y + " " + " align-x=\"1\"><label " + param + " /></align>\n";
501 string value = " <value " + param + " " + "attach-x=\"2\" " + attach_y + pad_x + "/>\n";
502 string attach_xv = "attach-x=\"1\" attach-w=\"1\" ";
503 string ctl;
504 if ((props.flags & PF_TYPEMASK) == PF_ENUM &&
505 (props.flags & PF_CTLMASK) == PF_CTL_COMBO)
507 ctl = " <combo " + attach_x + attach_y + param + expand_x + pad_x + " />\n";
509 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
510 (props.flags & PF_CTLMASK) == PF_CTL_TOGGLE)
512 ctl = " <align " + attach_x + attach_y + expand_x + fill_x + pad_x + "><toggle " + param + "/></align>\n";
514 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
515 (props.flags & PF_CTLMASK) == PF_CTL_BUTTON)
517 ctl = " <button attach-x=\"0\" attach-w=\"3\" " + expand_x + attach_y + param + pad_x + "/>\n";
518 label.clear();
520 else if ((props.flags & PF_TYPEMASK) == PF_BOOL &&
521 (props.flags & PF_CTLMASK) == PF_CTL_LED)
523 ctl = " <led " + attach_x + attach_y + param + shrink_x + pad_x + " />\n";
525 else if ((props.flags & PF_CTLMASK) == PF_CTL_METER)
527 if (props.flags & PF_CTLO_LABEL) {
528 attach_x = attach_xv;
529 pad_x.clear();
531 if (props.flags & PF_CTLO_REVERSE)
532 ctl = " <vumeter " + attach_x + attach_y + expand_x + fill_x + param + pad_x + "mode=\"2\"/>\n";
533 else
534 ctl = " <vumeter " + attach_x + attach_y + expand_x + fill_x + param + pad_x + "/>\n";
535 if (props.flags & PF_CTLO_LABEL)
536 ctl += value;
538 else if ((props.flags & PF_CTLMASK) != PF_CTL_FADER)
540 if ((props.flags & PF_UNITMASK) == PF_UNIT_DEG)
541 ctl = " <knob " + attach_xv + attach_y + shrink_x + param + " type=\"3\"/>\n";
542 else
543 ctl = " <knob " + attach_xv + attach_y + shrink_x + param + " />\n";
544 ctl += value;
546 else
548 ctl += " <hscale " + param + " " + attach_x + attach_y + " pad-x=\"10\"/>";
550 if (!label.empty())
551 xml << label;
552 if (!ctl.empty())
553 xml << ctl;
555 if (graphs)
557 xml << " <if cond=\"directlink\">" << endl;
558 xml << " <vbox expand-x=\"1\" fill-x=\"1\" attach-x=\"3\" attach-y=\"0\" attach-h=\"" << pi->get_param_count() << "\">" << endl;
559 for (int j = 0; j < pi->get_param_count(); j++)
561 const parameter_properties &props = *pi->get_param_props(j);
562 if (props.flags & PF_PROP_GRAPH)
564 xml << " <line-graph refresh=\"1\" width=\"160\" param=\"" << props.short_name << "\"/>\n" << endl;
567 xml << " </vbox>" << endl;
568 xml << " </if>" << endl;
570 xml << "</table>\n";
571 FILE *f = open_and_check(path_prefix+string(pi->get_id())+".xml");
572 fprintf(f, "%s\n", xml.str().c_str());
573 fclose(f);
577 int main(int argc, char *argv[])
579 string mode = "rdf";
580 string path_prefix;
581 while(1) {
582 int option_index;
583 int c = getopt_long(argc, argv, "hvm:p:", long_options, &option_index);
584 if (c == -1)
585 break;
586 switch(c) {
587 case 'h':
588 case '?':
589 printf("LV2 TTL / XML GUI generator for Calf plugin pack\nSyntax: %s [--help] [--version] [--mode rdf|ttl|gui] [--path <path>]\n", argv[0]);
590 return 0;
591 case 'v':
592 printf("%s\n", PACKAGE_STRING);
593 return 0;
594 case 'm':
595 mode = optarg;
596 if (mode != "rdf" && mode != "ttl" && mode != "gui") {
597 fprintf(stderr, "calfmakerdf: Invalid mode %s\n", optarg);
598 return 1;
600 break;
601 case 'p':
602 path_prefix = optarg;
603 if (path_prefix.empty())
605 fprintf(stderr, "calfmakerdf: Path prefix must not be empty\n");
606 exit(1);
608 if (path_prefix[path_prefix.length() - 1] != '/')
609 path_prefix += '/';
610 break;
613 if (false)
616 #if USE_LV2
617 else if (mode == "ttl")
618 make_ttl(path_prefix);
619 #endif
620 else if (mode == "gui")
621 make_gui(path_prefix);
622 else
624 fprintf(stderr, "calfmakerdf: Mode '%s' unsupported in this version\n", mode.c_str());
625 return 1;
627 return 0;