1 /*
2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any purpose with
6 * or without fee is hereby granted, provided that the above copyright notice and this
7 * permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "DistrhoPluginInternal.hpp"
18
19 #include "lv2/atom.h"
20 #include "lv2/buf-size.h"
21 #include "lv2/data-access.h"
22 #include "lv2/instance-access.h"
23 #include "lv2/midi.h"
24 #include "lv2/options.h"
25 #include "lv2/port-props.h"
26 #include "lv2/presets.h"
27 #include "lv2/resize-port.h"
28 #include "lv2/state.h"
29 #include "lv2/time.h"
30 #include "lv2/ui.h"
31 #include "lv2/units.h"
32 #include "lv2/urid.h"
33 #include "lv2/worker.h"
34 #include "lv2/lv2_kxstudio_properties.h"
35 #include "lv2/lv2_programs.h"
36
37 #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
38 # include "mod-license.h"
39 #endif
40
41 #include <fstream>
42 #include <iostream>
43
44 #ifndef DISTRHO_PLUGIN_URI
45 # error DISTRHO_PLUGIN_URI undefined!
46 #endif
47
48 #ifndef DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE
49 # define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048
50 #endif
51
52 #ifndef DISTRHO_PLUGIN_USES_MODGUI
53 # define DISTRHO_PLUGIN_USES_MODGUI 0
54 #endif
55
56 #if DISTRHO_PLUGIN_HAS_EMBED_UI
57 # if DISTRHO_OS_HAIKU
58 # define DISTRHO_LV2_UI_TYPE "BeUI"
59 # elif DISTRHO_OS_MAC
60 # define DISTRHO_LV2_UI_TYPE "CocoaUI"
61 # elif DISTRHO_OS_WINDOWS
62 # define DISTRHO_LV2_UI_TYPE "WindowsUI"
63 # else
64 # define DISTRHO_LV2_UI_TYPE "X11UI"
65 # endif
66 #else
67 # define DISTRHO_LV2_UI_TYPE "UI"
68 #endif
69
70 #define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI))
71 #define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI))
72
73 // -----------------------------------------------------------------------
74 static const char* const lv2ManifestPluginExtensionData[] =
75 {
76 "opts:interface",
77 #if DISTRHO_PLUGIN_WANT_STATE
78 LV2_STATE__interface,
79 LV2_WORKER__interface,
80 #endif
81 #if DISTRHO_PLUGIN_WANT_PROGRAMS
82 LV2_PROGRAMS__Interface,
83 #endif
84 #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
85 MOD_LICENSE__interface,
86 #endif
87 nullptr
88 };
89
90 static const char* const lv2ManifestPluginOptionalFeatures[] =
91 {
92 #if DISTRHO_PLUGIN_IS_RT_SAFE
93 LV2_CORE__hardRTCapable,
94 #endif
95 LV2_BUF_SIZE__boundedBlockLength,
96 nullptr
97 };
98
99 static const char* const lv2ManifestPluginRequiredFeatures[] =
100 {
101 "opts:options",
102 LV2_URID__map,
103 #if DISTRHO_PLUGIN_WANT_STATE
104 LV2_WORKER__schedule,
105 #endif
106 #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
107 MOD_LICENSE__feature,
108 #endif
109 nullptr
110 };
111
112 static const char* const lv2ManifestPluginSupportedOptions[] =
113 {
114 LV2_BUF_SIZE__nominalBlockLength,
115 LV2_BUF_SIZE__maxBlockLength,
116 LV2_PARAMETERS__sampleRate,
117 nullptr
118 };
119
120 #if DISTRHO_PLUGIN_HAS_UI
121 static const char* const lv2ManifestUiExtensionData[] =
122 {
123 "opts:interface",
124 "ui:idleInterface",
125 "ui:showInterface",
126 "ui:resize",
127 #if DISTRHO_PLUGIN_WANT_PROGRAMS
128 LV2_PROGRAMS__UIInterface,
129 #endif
130 nullptr
131 };
132
133 static const char* const lv2ManifestUiOptionalFeatures[] =
134 {
135 #if DISTRHO_PLUGIN_HAS_EMBED_UI
136 # if !DISTRHO_UI_USER_RESIZABLE
137 "ui:noUserResize",
138 # endif
139 "ui:resize",
140 "ui:touch",
141 #endif
142 nullptr
143 };
144
145 static const char* const lv2ManifestUiRequiredFeatures[] =
146 {
147 "opts:options",
148 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
149 LV2_DATA_ACCESS_URI,
150 LV2_INSTANCE_ACCESS_URI,
151 #endif
152 LV2_URID__map,
153 nullptr
154 };
155
156 static const char* const lv2ManifestUiSupportedOptions[] =
157 {
158 LV2_PARAMETERS__sampleRate,
159 nullptr
160 };
161 #endif // DISTRHO_PLUGIN_HAS_UI
162
addAttribute(DISTRHO_NAMESPACE::String & text,const char * const attribute,const char * const values[],const uint indent,const bool endInDot=false)163 static void addAttribute(DISTRHO_NAMESPACE::String& text,
164 const char* const attribute,
165 const char* const values[],
166 const uint indent,
167 const bool endInDot = false)
168 {
169 if (values[0] == nullptr)
170 {
171 if (endInDot)
172 {
173 bool found;
174 const size_t index = text.rfind(';', &found);
175 if (found) text[index] = '.';
176 }
177 return;
178 }
179
180 const size_t attributeLength = std::strlen(attribute);
181
182 for (uint i = 0; values[i] != nullptr; ++i)
183 {
184 for (uint j = 0; j < indent; ++j)
185 text += " ";
186
187 if (i == 0)
188 {
189 text += attribute;
190 }
191 else
192 {
193 for (uint j = 0; j < attributeLength; ++j)
194 text += " ";
195 }
196
197 text += " ";
198
199 const bool isUrl = std::strstr(values[i], "://") != nullptr || std::strncmp(values[i], "urn:", 4) == 0;
200 if (isUrl) text += "<";
201 text += values[i];
202 if (isUrl) text += ">";
203 text += values[i + 1] ? " ,\n" : (endInDot ? " .\n\n" : " ;\n\n");
204 }
205 }
206
207 // -----------------------------------------------------------------------
208
209 DISTRHO_PLUGIN_EXPORT
lv2_generate_ttl(const char * const basename)210 void lv2_generate_ttl(const char* const basename)
211 {
212 USE_NAMESPACE_DISTRHO
213
214 // Dummy plugin to get data from
215 d_lastBufferSize = 512;
216 d_lastSampleRate = 44100.0;
217 PluginExporter plugin(nullptr, nullptr);
218 d_lastBufferSize = 0;
219 d_lastSampleRate = 0.0;
220
221 String pluginDLL(basename);
222 String pluginTTL(pluginDLL + ".ttl");
223
224 #if DISTRHO_PLUGIN_HAS_UI
225 String pluginUI(pluginDLL);
226 # if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
227 pluginUI.truncate(pluginDLL.rfind("_dsp"));
228 pluginUI += "_ui";
229 const String uiTTL(pluginUI + ".ttl");
230 # endif
231 #endif
232
233 // ---------------------------------------------
234
235 {
236 std::cout << "Writing manifest.ttl..."; std::cout.flush();
237 std::fstream manifestFile("manifest.ttl", std::ios::out);
238
239 String manifestString;
240 manifestString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n";
241 manifestString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
242 #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
243 manifestString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n";
244 #endif
245 #if DISTRHO_PLUGIN_WANT_PROGRAMS
246 manifestString += "@prefix pset: <" LV2_PRESETS_PREFIX "> .\n";
247 #endif
248 #if DISTRHO_PLUGIN_HAS_UI
249 manifestString += "@prefix ui: <" LV2_UI_PREFIX "> .\n";
250 #endif
251 manifestString += "\n";
252
253 manifestString += "<" DISTRHO_PLUGIN_URI ">\n";
254 manifestString += " a lv2:Plugin ;\n";
255 manifestString += " lv2:binary <" + pluginDLL + "." DISTRHO_DLL_EXTENSION "> ;\n";
256 #if DISTRHO_PLUGIN_USES_MODGUI
257 manifestString += " rdfs:seeAlso <" + pluginTTL + "> ,\n";
258 manifestString += " <modgui.ttl> .\n";
259 #else
260 manifestString += " rdfs:seeAlso <" + pluginTTL + "> .\n";
261 #endif
262 manifestString += "\n";
263
264 #if DISTRHO_PLUGIN_HAS_UI
265 manifestString += "<" DISTRHO_UI_URI ">\n";
266 manifestString += " a ui:" DISTRHO_LV2_UI_TYPE " ;\n";
267 manifestString += " ui:binary <" + pluginUI + "." DISTRHO_DLL_EXTENSION "> ;\n";
268 # if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
269 addAttribute(manifestString, "lv2:extensionData", lv2ManifestUiExtensionData, 4);
270 addAttribute(manifestString, "lv2:optionalFeature", lv2ManifestUiOptionalFeatures, 4);
271 addAttribute(manifestString, "lv2:requiredFeature", lv2ManifestUiRequiredFeatures, 4);
272 addAttribute(manifestString, "opts:supportedOption", lv2ManifestUiSupportedOptions, 4, true);
273 # else // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
274 manifestString += " rdfs:seeAlso <" + uiTTL + "> .\n";
275 # endif // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
276 manifestString += "\n";
277 #endif
278
279 #if DISTRHO_PLUGIN_WANT_PROGRAMS
280 const String presetSeparator(std::strstr(DISTRHO_PLUGIN_URI, "#") != nullptr ? ":" : "#");
281
282 char strBuf[0xff+1];
283 strBuf[0xff] = '\0';
284
285 String presetString;
286
287 // Presets
288 for (uint32_t i = 0; i < plugin.getProgramCount(); ++i)
289 {
290 std::snprintf(strBuf, 0xff, "%03i", i+1);
291
292 presetString = "<" DISTRHO_PLUGIN_URI + presetSeparator + "preset" + strBuf + ">\n";
293 presetString += " a pset:Preset ;\n";
294 presetString += " lv2:appliesTo <" DISTRHO_PLUGIN_URI "> ;\n";
295 presetString += " rdfs:label \"" + plugin.getProgramName(i) + "\" ;\n";
296 presetString += " rdfs:seeAlso <presets.ttl> .\n";
297 presetString += "\n";
298
299 manifestString += presetString;
300 }
301 #endif
302
303 manifestFile << manifestString << std::endl;
304 manifestFile.close();
305 std::cout << " done!" << std::endl;
306 }
307
308 // ---------------------------------------------
309
310 {
311 std::cout << "Writing " << pluginTTL << "..."; std::cout.flush();
312 std::fstream pluginFile(pluginTTL, std::ios::out);
313
314 String pluginString;
315
316 // header
317 #if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT
318 pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n";
319 #endif
320 pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n";
321 pluginString += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n";
322 pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n";
323 #ifdef DISTRHO_PLUGIN_BRAND
324 pluginString += "@prefix mod: <http://moddevices.com/ns/mod#> .\n";
325 #endif
326 pluginString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n";
327 pluginString += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n";
328 pluginString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
329 #if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT
330 pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n";
331 #endif
332 #if DISTRHO_PLUGIN_HAS_UI
333 pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n";
334 #endif
335 pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n";
336 pluginString += "\n";
337
338 // plugin
339 pluginString += "<" DISTRHO_PLUGIN_URI ">\n";
340 #ifdef DISTRHO_PLUGIN_LV2_CATEGORY
341 pluginString += " a " DISTRHO_PLUGIN_LV2_CATEGORY ", lv2:Plugin ;\n";
342 #elif DISTRHO_PLUGIN_IS_SYNTH
343 pluginString += " a lv2:InstrumentPlugin, lv2:Plugin ;\n";
344 #else
345 pluginString += " a lv2:Plugin ;\n";
346 #endif
347 pluginString += "\n";
348
349 addAttribute(pluginString, "lv2:extensionData", lv2ManifestPluginExtensionData, 4);
350 addAttribute(pluginString, "lv2:optionalFeature", lv2ManifestPluginOptionalFeatures, 4);
351 addAttribute(pluginString, "lv2:requiredFeature", lv2ManifestPluginRequiredFeatures, 4);
352 addAttribute(pluginString, "opts:supportedOption", lv2ManifestPluginSupportedOptions, 4);
353
354 // UI
355 #if DISTRHO_PLUGIN_HAS_UI
356 pluginString += " ui:ui <" DISTRHO_UI_URI "> ;\n";
357 pluginString += "\n";
358 #endif
359
360 {
361 uint32_t portIndex = 0;
362
363 #if DISTRHO_PLUGIN_NUM_INPUTS > 0
364 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++portIndex)
365 {
366 const AudioPort& port(plugin.getAudioPort(true, i));
367
368 if (i == 0)
369 pluginString += " lv2:port [\n";
370 else
371 pluginString += " [\n";
372
373 if (port.hints & kAudioPortIsCV)
374 pluginString += " a lv2:InputPort, lv2:CVPort ;\n";
375 else
376 pluginString += " a lv2:InputPort, lv2:AudioPort ;\n";
377
378 pluginString += " lv2:index " + String(portIndex) + " ;\n";
379 pluginString += " lv2:symbol \"lv2_" + port.symbol + "\" ;\n";
380 pluginString += " lv2:name \"" + port.name + "\" ;\n";
381
382 if (port.hints & kAudioPortIsSidechain)
383 pluginString += " lv2:portProperty lv2:isSideChain;\n";
384
385 if (i+1 == DISTRHO_PLUGIN_NUM_INPUTS)
386 pluginString += " ] ;\n";
387 else
388 pluginString += " ] ,\n";
389 }
390 pluginString += "\n";
391 #endif
392
393 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
394 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++portIndex)
395 {
396 const AudioPort& port(plugin.getAudioPort(false, i));
397
398 if (i == 0)
399 pluginString += " lv2:port [\n";
400 else
401 pluginString += " [\n";
402
403 if (port.hints & kAudioPortIsCV)
404 pluginString += " a lv2:OutputPort, lv2:CVPort ;\n";
405 else
406 pluginString += " a lv2:OutputPort, lv2:AudioPort ;\n";
407
408 pluginString += " lv2:index " + String(portIndex) + " ;\n";
409 pluginString += " lv2:symbol \"lv2_" + port.symbol + "\" ;\n";
410 pluginString += " lv2:name \"" + port.name + "\" ;\n";
411
412 if (port.hints & kAudioPortIsSidechain)
413 pluginString += " lv2:portProperty lv2:isSideChain;\n";
414
415 if (i+1 == DISTRHO_PLUGIN_NUM_OUTPUTS)
416 pluginString += " ] ;\n";
417 else
418 pluginString += " ] ,\n";
419 }
420 pluginString += "\n";
421 #endif
422
423 #if DISTRHO_LV2_USE_EVENTS_IN
424 pluginString += " lv2:port [\n";
425 pluginString += " a lv2:InputPort, atom:AtomPort ;\n";
426 pluginString += " lv2:index " + String(portIndex) + " ;\n";
427 pluginString += " lv2:name \"Events Input\" ;\n";
428 pluginString += " lv2:symbol \"lv2_events_in\" ;\n";
429 pluginString += " rsz:minimumSize " + String(DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE) + " ;\n";
430 pluginString += " atom:bufferType atom:Sequence ;\n";
431 # if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)
432 pluginString += " atom:supports <" LV2_ATOM__String "> ;\n";
433 # endif
434 # if DISTRHO_PLUGIN_WANT_MIDI_INPUT
435 pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n";
436 # endif
437 # if DISTRHO_PLUGIN_WANT_TIMEPOS
438 pluginString += " atom:supports <" LV2_TIME__Position "> ;\n";
439 # endif
440 pluginString += " ] ;\n\n";
441 ++portIndex;
442 #endif
443
444 #if DISTRHO_LV2_USE_EVENTS_OUT
445 pluginString += " lv2:port [\n";
446 pluginString += " a lv2:OutputPort, atom:AtomPort ;\n";
447 pluginString += " lv2:index " + String(portIndex) + " ;\n";
448 pluginString += " lv2:name \"Events Output\" ;\n";
449 pluginString += " lv2:symbol \"lv2_events_out\" ;\n";
450 pluginString += " rsz:minimumSize " + String(DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE) + " ;\n";
451 pluginString += " atom:bufferType atom:Sequence ;\n";
452 # if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)
453 pluginString += " atom:supports <" LV2_ATOM__String "> ;\n";
454 # endif
455 # if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
456 pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n";
457 # endif
458 pluginString += " ] ;\n\n";
459 ++portIndex;
460 #endif
461
462 #if DISTRHO_PLUGIN_WANT_LATENCY
463 pluginString += " lv2:port [\n";
464 pluginString += " a lv2:OutputPort, lv2:ControlPort ;\n";
465 pluginString += " lv2:index " + String(portIndex) + " ;\n";
466 pluginString += " lv2:name \"Latency\" ;\n";
467 pluginString += " lv2:symbol \"lv2_latency\" ;\n";
468 pluginString += " lv2:designation lv2:latency ;\n";
469 pluginString += " lv2:portProperty lv2:reportsLatency, lv2:integer, <" LV2_PORT_PROPS__notOnGUI "> ;\n";
470 pluginString += " ] ;\n\n";
471 ++portIndex;
472 #endif
473
474 for (uint32_t i=0, count=plugin.getParameterCount(); i < count; ++i, ++portIndex)
475 {
476 if (i == 0)
477 pluginString += " lv2:port [\n";
478 else
479 pluginString += " [\n";
480
481 if (plugin.isParameterOutput(i))
482 pluginString += " a lv2:OutputPort, lv2:ControlPort ;\n";
483 else
484 pluginString += " a lv2:InputPort, lv2:ControlPort ;\n";
485
486 pluginString += " lv2:index " + String(portIndex) + " ;\n";
487
488 bool designated = false;
489
490 // designation
491 if (plugin.isParameterInput(i))
492 {
493 switch (plugin.getParameterDesignation(i))
494 {
495 case kParameterDesignationNull:
496 break;
497 case kParameterDesignationBypass:
498 designated = true;
499 pluginString += " lv2:name \"Enabled\" ;\n";
500 pluginString += " lv2:symbol \"lv2_enabled\" ;\n";
501 pluginString += " lv2:default 1 ;\n";
502 pluginString += " lv2:minimum 0 ;\n";
503 pluginString += " lv2:maximum 1 ;\n";
504 pluginString += " lv2:portProperty lv2:toggled , lv2:integer ;\n";
505 pluginString += " lv2:designation lv2:enabled ;\n";
506 break;
507 }
508 }
509
510 if (! designated)
511 {
512 // name and symbol
513 pluginString += " lv2:name \"\"\"" + plugin.getParameterName(i) + "\"\"\" ;\n";
514
515 String symbol(plugin.getParameterSymbol(i));
516
517 if (symbol.isEmpty())
518 symbol = "lv2_port_" + String(portIndex-1);
519
520 pluginString += " lv2:symbol \"" + symbol + "\" ;\n";
521
522 // short name
523 const String& shortName(plugin.getParameterShortName(i));
524
525 if (shortName.isNotEmpty())
526 pluginString += " lv2:shortName \"\"\"" + shortName + "\"\"\" ;\n";
527
528 // ranges
529 const ParameterRanges& ranges(plugin.getParameterRanges(i));
530
531 if (plugin.getParameterHints(i) & kParameterIsInteger)
532 {
533 if (plugin.isParameterInput(i))
534 pluginString += " lv2:default " + String(int(plugin.getParameterValue(i))) + " ;\n";
535 pluginString += " lv2:minimum " + String(int(ranges.min)) + " ;\n";
536 pluginString += " lv2:maximum " + String(int(ranges.max)) + " ;\n";
537 }
538 else
539 {
540 if (plugin.isParameterInput(i))
541 pluginString += " lv2:default " + String(plugin.getParameterValue(i)) + " ;\n";
542 pluginString += " lv2:minimum " + String(ranges.min) + " ;\n";
543 pluginString += " lv2:maximum " + String(ranges.max) + " ;\n";
544 }
545
546 // enumeration
547 const ParameterEnumerationValues& enumValues(plugin.getParameterEnumValues(i));
548
549 if (enumValues.count > 0)
550 {
551 if (enumValues.count >= 2 && enumValues.restrictedMode)
552 pluginString += " lv2:portProperty lv2:enumeration ;\n";
553
554 for (uint8_t j=0; j < enumValues.count; ++j)
555 {
556 const ParameterEnumerationValue& enumValue(enumValues.values[j]);
557
558 if (j == 0)
559 pluginString += " lv2:scalePoint [\n";
560 else
561 pluginString += " [\n";
562
563 pluginString += " rdfs:label \"\"\"" + enumValue.label + "\"\"\" ;\n";
564 pluginString += " rdf:value " + String(enumValue.value) + " ;\n";
565
566 if (j+1 == enumValues.count)
567 pluginString += " ] ;\n\n";
568 else
569 pluginString += " ] ,\n";
570 }
571 }
572
573 // unit
574 const String& unit(plugin.getParameterUnit(i));
575
576 if (! unit.isEmpty())
577 {
578 if (unit == "db" || unit == "dB")
579 {
580 pluginString += " unit:unit unit:db ;\n";
581 }
582 else if (unit == "hz" || unit == "Hz")
583 {
584 pluginString += " unit:unit unit:hz ;\n";
585 }
586 else if (unit == "khz" || unit == "kHz")
587 {
588 pluginString += " unit:unit unit:khz ;\n";
589 }
590 else if (unit == "mhz" || unit == "mHz")
591 {
592 pluginString += " unit:unit unit:mhz ;\n";
593 }
594 else if (unit == "ms")
595 {
596 pluginString += " unit:unit unit:ms ;\n";
597 }
598 else if (unit == "s")
599 {
600 pluginString += " unit:unit unit:s ;\n";
601 }
602 else if (unit == "%")
603 {
604 pluginString += " unit:unit unit:pc ;\n";
605 }
606 else
607 {
608 pluginString += " unit:unit [\n";
609 pluginString += " a unit:Unit ;\n";
610 pluginString += " rdfs:label \"" + unit + "\" ;\n";
611 pluginString += " unit:symbol \"" + unit + "\" ;\n";
612 pluginString += " unit:render \"%f " + unit + "\" ;\n";
613 pluginString += " ] ;\n";
614 }
615 }
616
617 // comment
618 const String& comment(plugin.getParameterDescription(i));
619
620 if (comment.isNotEmpty())
621 pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n";
622
623 // hints
624 const uint32_t hints(plugin.getParameterHints(i));
625
626 if (hints & kParameterIsBoolean)
627 {
628 if ((hints & kParameterIsTrigger) == kParameterIsTrigger)
629 pluginString += " lv2:portProperty <" LV2_PORT_PROPS__trigger "> ;\n";
630 pluginString += " lv2:portProperty lv2:toggled ;\n";
631 }
632 if (hints & kParameterIsInteger)
633 pluginString += " lv2:portProperty lv2:integer ;\n";
634 if (hints & kParameterIsLogarithmic)
635 pluginString += " lv2:portProperty <" LV2_PORT_PROPS__logarithmic "> ;\n";
636 if ((hints & kParameterIsAutomable) == 0 && plugin.isParameterInput(i))
637 {
638 pluginString += " lv2:portProperty <" LV2_PORT_PROPS__expensive "> ,\n";
639 pluginString += " <" LV2_KXSTUDIO_PROPERTIES__NonAutomable "> ;\n";
640 }
641 } // ! designated
642
643 if (i+1 == count)
644 pluginString += " ] ;\n\n";
645 else
646 pluginString += " ] ,\n";
647 }
648 }
649
650 // comment
651 {
652 const String comment(plugin.getDescription());
653
654 if (comment.isNotEmpty())
655 pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n\n";
656 }
657
658 #ifdef DISTRHO_PLUGIN_BRAND
659 // MOD
660 pluginString += " mod:brand \"" DISTRHO_PLUGIN_BRAND "\" ;\n";
661 pluginString += " mod:label \"" DISTRHO_PLUGIN_NAME "\" ;\n\n";
662 #endif
663
664 // name
665 pluginString += " doap:name \"\"\"" + String(plugin.getName()) + "\"\"\" ;\n";
666
667 // license
668 {
669 const String license(plugin.getLicense());
670
671 if (license.contains("://"))
672 pluginString += " doap:license <" + license + "> ;\n\n";
673 else
674 pluginString += " doap:license \"\"\"" + license + "\"\"\" ;\n\n";
675 }
676
677 // developer
678 {
679 const String homepage(plugin.getHomePage());
680
681 pluginString += " doap:maintainer [\n";
682 pluginString += " foaf:name \"\"\"" + String(plugin.getMaker()) + "\"\"\" ;\n";
683
684 if (homepage.isNotEmpty())
685 pluginString += " foaf:homepage <" + homepage + "> ;\n";
686
687 pluginString += " ] ;\n\n";
688 }
689
690 {
691 const uint32_t version(plugin.getVersion());
692
693 const uint32_t majorVersion = (version & 0xFF0000) >> 16;
694 const uint32_t microVersion = (version & 0x00FF00) >> 8;
695 /* */ uint32_t minorVersion = (version & 0x0000FF) >> 0;
696
697 // NOTE: LV2 ignores 'major' version and says 0 for minor is pre-release/unstable.
698 if (majorVersion > 0)
699 minorVersion += 2;
700
701 pluginString += " lv2:microVersion " + String(microVersion) + " ;\n";
702 pluginString += " lv2:minorVersion " + String(minorVersion) + " .\n";
703 }
704
705 pluginFile << pluginString << std::endl;
706 pluginFile.close();
707 std::cout << " done!" << std::endl;
708 }
709
710 // ---------------------------------------------
711
712 #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
713 {
714 std::cout << "Writing " << uiTTL << "..."; std::cout.flush();
715 std::fstream uiFile(uiTTL, std::ios::out);
716
717 String uiString;
718 uiString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n";
719 uiString += "@prefix ui: <" LV2_UI_PREFIX "> .\n";
720 uiString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n";
721 uiString += "\n";
722
723 uiString += "<" DISTRHO_UI_URI ">\n";
724
725 addAttribute(uiString, "lv2:extensionData", lv2ManifestUiExtensionData, 4);
726 addAttribute(uiString, "lv2:optionalFeature", lv2ManifestUiOptionalFeatures, 4);
727 addAttribute(uiString, "lv2:requiredFeature", lv2ManifestUiRequiredFeatures, 4);
728 addAttribute(uiString, "opts:supportedOption", lv2ManifestUiSupportedOptions, 4, true);
729
730 uiFile << uiString << std::endl;
731 uiFile.close();
732 std::cout << " done!" << std::endl;
733 }
734 #endif
735
736 // ---------------------------------------------
737
738 #if DISTRHO_PLUGIN_WANT_PROGRAMS
739 {
740 std::cout << "Writing presets.ttl..."; std::cout.flush();
741 std::fstream presetsFile("presets.ttl", std::ios::out);
742
743 String presetsString;
744 presetsString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n";
745 presetsString += "@prefix pset: <" LV2_PRESETS_PREFIX "> .\n";
746 # if DISTRHO_PLUGIN_WANT_STATE
747 presetsString += "@prefix state: <" LV2_STATE_PREFIX "> .\n";
748 # endif
749 presetsString += "\n";
750
751 const uint32_t numParameters = plugin.getParameterCount();
752 const uint32_t numPrograms = plugin.getProgramCount();
753 # if DISTRHO_PLUGIN_WANT_FULL_STATE
754 const uint32_t numStates = plugin.getStateCount();
755 # endif
756
757 const String presetSeparator(std::strstr(DISTRHO_PLUGIN_URI, "#") != nullptr ? ":" : "#");
758
759 char strBuf[0xff+1];
760 strBuf[0xff] = '\0';
761
762 String presetString;
763
764 for (uint32_t i=0; i<numPrograms; ++i)
765 {
766 std::snprintf(strBuf, 0xff, "%03i", i+1);
767
768 plugin.loadProgram(i);
769
770 presetString = "<" DISTRHO_PLUGIN_URI + presetSeparator + "preset" + strBuf + ">\n";
771
772 # if DISTRHO_PLUGIN_WANT_FULL_STATE
773 if (numParameters == 0 && numStates == 0)
774 #else
775 if (numParameters == 0)
776 #endif
777 {
778 presetString += " .";
779 presetsString += presetString;
780 continue;
781 }
782
783 # if DISTRHO_PLUGIN_WANT_FULL_STATE
784 presetString += " state:state [\n";
785 for (uint32_t j=0; j<numStates; ++j)
786 {
787 const String key = plugin.getStateKey(j);
788 const String value = plugin.getState(key);
789
790 presetString += " <urn:distrho:" + key + ">";
791
792 if (value.length() < 10)
793 presetString += " \"" + value + "\" ;\n";
794 else
795 presetString += "\n\"\"\"" + value + "\"\"\" ;\n";
796 }
797
798 if (numParameters > 0)
799 presetString += " ] ;\n\n";
800 else
801 presetString += " ] .\n\n";
802 # endif
803
804 bool firstParameter = true;
805
806 for (uint32_t j=0; j <numParameters; ++j)
807 {
808 if (plugin.isParameterOutput(j))
809 continue;
810
811 if (firstParameter)
812 {
813 presetString += " lv2:port [\n";
814 firstParameter = false;
815 }
816 else
817 {
818 presetString += " [\n";
819 }
820
821 presetString += " lv2:symbol \"" + plugin.getParameterSymbol(j) + "\" ;\n";
822
823 if (plugin.getParameterHints(j) & kParameterIsInteger)
824 presetString += " pset:value " + String(int(plugin.getParameterValue(j))) + " ;\n";
825 else
826 presetString += " pset:value " + String(plugin.getParameterValue(j)) + " ;\n";
827
828 if (j+1 == numParameters || plugin.isParameterOutput(j+1))
829 presetString += " ] .\n\n";
830 else
831 presetString += " ] ,\n";
832 }
833
834 presetsString += presetString;
835 }
836
837 presetsFile << presetsString << std::endl;
838 presetsFile.close();
839 std::cout << " done!" << std::endl;
840 }
841 #endif
842 }
843