Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/distrho/src/DistrhoPluginLV2export.cpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2:cf2cb71d31dd | 3:84e66ea83026 |
---|---|
1 /* | |
2 * DISTRHO Plugin Framework (DPF) | |
3 * Copyright (C) 2012-2023 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 #include "../DistrhoPluginUtils.hpp" | |
19 | |
20 #include "lv2/atom.h" | |
21 #include "lv2/buf-size.h" | |
22 #include "lv2/data-access.h" | |
23 #include "lv2/instance-access.h" | |
24 #include "lv2/midi.h" | |
25 #include "lv2/options.h" | |
26 #include "lv2/parameters.h" | |
27 #include "lv2/patch.h" | |
28 #include "lv2/port-groups.h" | |
29 #include "lv2/port-props.h" | |
30 #include "lv2/presets.h" | |
31 #include "lv2/resize-port.h" | |
32 #include "lv2/state.h" | |
33 #include "lv2/time.h" | |
34 #include "lv2/ui.h" | |
35 #include "lv2/units.h" | |
36 #include "lv2/urid.h" | |
37 #include "lv2/worker.h" | |
38 #include "lv2/lv2_kxstudio_properties.h" | |
39 #include "lv2/lv2_programs.h" | |
40 | |
41 #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |
42 # include "mod-license.h" | |
43 #endif | |
44 | |
45 #ifdef DISTRHO_OS_WINDOWS | |
46 # include <direct.h> | |
47 #else | |
48 # include <sys/stat.h> | |
49 # include <sys/types.h> | |
50 # include <unistd.h> | |
51 #endif | |
52 | |
53 #include <fstream> | |
54 #include <iostream> | |
55 | |
56 #ifndef DISTRHO_PLUGIN_URI | |
57 # error DISTRHO_PLUGIN_URI undefined! | |
58 #endif | |
59 | |
60 #ifndef DISTRHO_PLUGIN_LV2_STATE_PREFIX | |
61 # define DISTRHO_PLUGIN_LV2_STATE_PREFIX "urn:distrho:" | |
62 #endif | |
63 | |
64 #ifndef DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE | |
65 # define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048 | |
66 #endif | |
67 | |
68 #ifndef DISTRHO_PLUGIN_USES_MODGUI | |
69 # define DISTRHO_PLUGIN_USES_MODGUI 0 | |
70 #endif | |
71 | |
72 #ifndef DISTRHO_PLUGIN_USES_CUSTOM_MODGUI | |
73 # define DISTRHO_PLUGIN_USES_CUSTOM_MODGUI 0 | |
74 #endif | |
75 | |
76 #if DISTRHO_PLUGIN_HAS_EMBED_UI | |
77 # if DISTRHO_OS_HAIKU | |
78 # define DISTRHO_LV2_UI_TYPE "BeUI" | |
79 # elif DISTRHO_OS_MAC | |
80 # define DISTRHO_LV2_UI_TYPE "CocoaUI" | |
81 # elif DISTRHO_OS_WINDOWS | |
82 # define DISTRHO_LV2_UI_TYPE "WindowsUI" | |
83 # else | |
84 # define DISTRHO_LV2_UI_TYPE "X11UI" | |
85 # endif | |
86 #else | |
87 # define DISTRHO_LV2_UI_TYPE "UI" | |
88 #endif | |
89 | |
90 #define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) | |
91 #define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) | |
92 | |
93 #define DISTRHO_BYPASS_PARAMETER_NAME "lv2_enabled" | |
94 | |
95 // ----------------------------------------------------------------------- | |
96 static const char* const lv2ManifestPluginExtensionData[] = | |
97 { | |
98 "opts:interface", | |
99 #if DISTRHO_PLUGIN_WANT_STATE | |
100 LV2_STATE__interface, | |
101 LV2_WORKER__interface, | |
102 #endif | |
103 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
104 LV2_PROGRAMS__Interface, | |
105 #endif | |
106 #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |
107 MOD_LICENSE__interface, | |
108 #endif | |
109 nullptr | |
110 }; | |
111 | |
112 static const char* const lv2ManifestPluginOptionalFeatures[] = | |
113 { | |
114 #if DISTRHO_PLUGIN_IS_RT_SAFE | |
115 LV2_CORE__hardRTCapable, | |
116 #endif | |
117 LV2_BUF_SIZE__boundedBlockLength, | |
118 nullptr | |
119 }; | |
120 | |
121 static const char* const lv2ManifestPluginRequiredFeatures[] = | |
122 { | |
123 "opts:options", | |
124 LV2_URID__map, | |
125 #if DISTRHO_PLUGIN_WANT_STATE | |
126 LV2_WORKER__schedule, | |
127 #endif | |
128 #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |
129 MOD_LICENSE__feature, | |
130 #endif | |
131 nullptr | |
132 }; | |
133 | |
134 static const char* const lv2ManifestPluginSupportedOptions[] = | |
135 { | |
136 LV2_BUF_SIZE__nominalBlockLength, | |
137 LV2_BUF_SIZE__maxBlockLength, | |
138 LV2_PARAMETERS__sampleRate, | |
139 nullptr | |
140 }; | |
141 | |
142 #if DISTRHO_PLUGIN_HAS_UI | |
143 static const char* const lv2ManifestUiExtensionData[] = | |
144 { | |
145 "opts:interface", | |
146 "ui:idleInterface", | |
147 "ui:showInterface", | |
148 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
149 LV2_PROGRAMS__UIInterface, | |
150 #endif | |
151 nullptr | |
152 }; | |
153 | |
154 static const char* const lv2ManifestUiOptionalFeatures[] = | |
155 { | |
156 #if DISTRHO_PLUGIN_HAS_EMBED_UI | |
157 # if !DISTRHO_UI_USER_RESIZABLE | |
158 "ui:noUserResize", | |
159 # endif | |
160 "ui:parent", | |
161 "ui:touch", | |
162 #endif | |
163 "ui:requestValue", | |
164 nullptr | |
165 }; | |
166 | |
167 static const char* const lv2ManifestUiRequiredFeatures[] = | |
168 { | |
169 "opts:options", | |
170 "ui:idleInterface", | |
171 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |
172 LV2_DATA_ACCESS_URI, | |
173 LV2_INSTANCE_ACCESS_URI, | |
174 #endif | |
175 LV2_URID__map, | |
176 nullptr | |
177 }; | |
178 | |
179 static const char* const lv2ManifestUiSupportedOptions[] = | |
180 { | |
181 LV2_PARAMETERS__sampleRate, | |
182 nullptr | |
183 }; | |
184 #endif // DISTRHO_PLUGIN_HAS_UI | |
185 | |
186 static void addAttribute(DISTRHO_NAMESPACE::String& text, | |
187 const char* const attribute, | |
188 const char* const values[], | |
189 const uint indent, | |
190 const bool endInDot = false) | |
191 { | |
192 if (values[0] == nullptr) | |
193 { | |
194 if (endInDot) | |
195 { | |
196 bool found; | |
197 const size_t index = text.rfind(';', &found); | |
198 if (found) text[index] = '.'; | |
199 } | |
200 return; | |
201 } | |
202 | |
203 const size_t attributeLength = std::strlen(attribute); | |
204 | |
205 for (uint i = 0; values[i] != nullptr; ++i) | |
206 { | |
207 for (uint j = 0; j < indent; ++j) | |
208 text += " "; | |
209 | |
210 if (i == 0) | |
211 { | |
212 text += attribute; | |
213 } | |
214 else | |
215 { | |
216 for (uint j = 0; j < attributeLength; ++j) | |
217 text += " "; | |
218 } | |
219 | |
220 text += " "; | |
221 | |
222 const bool isUrl = std::strstr(values[i], "://") != nullptr || std::strncmp(values[i], "urn:", 4) == 0; | |
223 if (isUrl) text += "<"; | |
224 text += values[i]; | |
225 if (isUrl) text += ">"; | |
226 text += values[i + 1] ? " ,\n" : (endInDot ? " .\n\n" : " ;\n\n"); | |
227 } | |
228 } | |
229 | |
230 // ----------------------------------------------------------------------- | |
231 | |
232 DISTRHO_PLUGIN_EXPORT | |
233 void lv2_generate_ttl(const char* const basename) | |
234 { | |
235 USE_NAMESPACE_DISTRHO | |
236 | |
237 String bundlePath(getBinaryFilename()); | |
238 if (bundlePath.isNotEmpty()) | |
239 { | |
240 bundlePath.truncate(bundlePath.rfind(DISTRHO_OS_SEP)); | |
241 } | |
242 #ifndef DISTRHO_OS_WINDOWS | |
243 else if (char* const cwd = ::getcwd(nullptr, 0)) | |
244 { | |
245 bundlePath = cwd; | |
246 std::free(cwd); | |
247 } | |
248 #endif | |
249 d_nextBundlePath = bundlePath.buffer(); | |
250 | |
251 // Dummy plugin to get data from | |
252 d_nextBufferSize = 512; | |
253 d_nextSampleRate = 44100.0; | |
254 d_nextPluginIsDummy = true; | |
255 PluginExporter plugin(nullptr, nullptr, nullptr, nullptr); | |
256 d_nextBufferSize = 0; | |
257 d_nextSampleRate = 0.0; | |
258 d_nextPluginIsDummy = false; | |
259 | |
260 const String pluginDLL(basename); | |
261 const String pluginTTL(pluginDLL + ".ttl"); | |
262 | |
263 #if DISTRHO_PLUGIN_HAS_UI | |
264 String pluginUI(pluginDLL); | |
265 # if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |
266 pluginUI.truncate(pluginDLL.rfind("_dsp")); | |
267 pluginUI += "_ui"; | |
268 const String uiTTL(pluginUI + ".ttl"); | |
269 # endif | |
270 #endif | |
271 | |
272 // --------------------------------------------- | |
273 | |
274 { | |
275 std::cout << "Writing manifest.ttl..."; std::cout.flush(); | |
276 std::fstream manifestFile("manifest.ttl", std::ios::out); | |
277 | |
278 String manifestString; | |
279 manifestString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |
280 manifestString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; | |
281 #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |
282 manifestString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; | |
283 #endif | |
284 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
285 manifestString += "@prefix pset: <" LV2_PRESETS_PREFIX "> .\n"; | |
286 #endif | |
287 #if DISTRHO_PLUGIN_HAS_UI | |
288 manifestString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |
289 #endif | |
290 manifestString += "\n"; | |
291 | |
292 manifestString += "<" DISTRHO_PLUGIN_URI ">\n"; | |
293 manifestString += " a lv2:Plugin ;\n"; | |
294 manifestString += " lv2:binary <" + pluginDLL + "." DISTRHO_DLL_EXTENSION "> ;\n"; | |
295 #if DISTRHO_PLUGIN_USES_MODGUI | |
296 manifestString += " rdfs:seeAlso <" + pluginTTL + "> ,\n"; | |
297 manifestString += " <modgui.ttl> .\n"; | |
298 #else | |
299 manifestString += " rdfs:seeAlso <" + pluginTTL + "> .\n"; | |
300 #endif | |
301 manifestString += "\n"; | |
302 | |
303 #if DISTRHO_PLUGIN_HAS_UI | |
304 manifestString += "<" DISTRHO_UI_URI ">\n"; | |
305 manifestString += " a ui:" DISTRHO_LV2_UI_TYPE " ;\n"; | |
306 manifestString += " ui:binary <" + pluginUI + "." DISTRHO_DLL_EXTENSION "> ;\n"; | |
307 # if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |
308 addAttribute(manifestString, "lv2:extensionData", lv2ManifestUiExtensionData, 4); | |
309 addAttribute(manifestString, "lv2:optionalFeature", lv2ManifestUiOptionalFeatures, 4); | |
310 addAttribute(manifestString, "lv2:requiredFeature", lv2ManifestUiRequiredFeatures, 4); | |
311 addAttribute(manifestString, "opts:supportedOption", lv2ManifestUiSupportedOptions, 4, true); | |
312 # else // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |
313 manifestString += " rdfs:seeAlso <" + uiTTL + "> .\n"; | |
314 # endif // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |
315 manifestString += "\n"; | |
316 #endif | |
317 | |
318 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
319 const String presetSeparator(std::strstr(DISTRHO_PLUGIN_URI, "#") != nullptr ? ":" : "#"); | |
320 | |
321 char strBuf[0xff+1]; | |
322 strBuf[0xff] = '\0'; | |
323 | |
324 String presetString; | |
325 | |
326 // Presets | |
327 for (uint32_t i = 0; i < plugin.getProgramCount(); ++i) | |
328 { | |
329 std::snprintf(strBuf, 0xff, "%03i", i+1); | |
330 | |
331 presetString = "<" DISTRHO_PLUGIN_URI + presetSeparator + "preset" + strBuf + ">\n"; | |
332 presetString += " a pset:Preset ;\n"; | |
333 presetString += " lv2:appliesTo <" DISTRHO_PLUGIN_URI "> ;\n"; | |
334 presetString += " rdfs:label \"" + plugin.getProgramName(i) + "\" ;\n"; | |
335 presetString += " rdfs:seeAlso <presets.ttl> .\n"; | |
336 presetString += "\n"; | |
337 | |
338 manifestString += presetString; | |
339 } | |
340 #endif | |
341 | |
342 manifestFile << manifestString; | |
343 manifestFile.close(); | |
344 std::cout << " done!" << std::endl; | |
345 } | |
346 | |
347 // --------------------------------------------- | |
348 | |
349 { | |
350 std::cout << "Writing " << pluginTTL << "..."; std::cout.flush(); | |
351 std::fstream pluginFile(pluginTTL, std::ios::out); | |
352 | |
353 String pluginString; | |
354 | |
355 // header | |
356 #if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT | |
357 pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; | |
358 #endif | |
359 pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"; | |
360 pluginString += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"; | |
361 pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |
362 pluginString += "@prefix midi: <" LV2_MIDI_PREFIX "> .\n"; | |
363 pluginString += "@prefix mod: <http://moddevices.com/ns/mod#> .\n"; | |
364 pluginString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; | |
365 pluginString += "@prefix pg: <" LV2_PORT_GROUPS_PREFIX "> .\n"; | |
366 pluginString += "@prefix patch: <" LV2_PATCH_PREFIX "> .\n"; | |
367 pluginString += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"; | |
368 pluginString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; | |
369 #if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT | |
370 pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n"; | |
371 #endif | |
372 pluginString += "@prefix spdx: <http://spdx.org/rdf/terms#> .\n"; | |
373 #if DISTRHO_PLUGIN_HAS_UI | |
374 pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |
375 #endif | |
376 pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n"; | |
377 pluginString += "\n"; | |
378 | |
379 #if DISTRHO_PLUGIN_WANT_STATE | |
380 bool hasHostVisibleState = false; | |
381 | |
382 for (uint32_t i=0, count=plugin.getStateCount(); i < count; ++i) | |
383 { | |
384 const uint32_t hints = plugin.getStateHints(i); | |
385 | |
386 if ((hints & kStateIsHostReadable) == 0x0) | |
387 continue; | |
388 | |
389 pluginString += "<" DISTRHO_PLUGIN_URI "#" + plugin.getStateKey(i) + ">\n"; | |
390 pluginString += " a lv2:Parameter ;\n"; | |
391 pluginString += " rdfs:label \"" + plugin.getStateLabel(i) + "\" ;\n"; | |
392 | |
393 const String& comment(plugin.getStateDescription(i)); | |
394 | |
395 if (comment.isNotEmpty()) | |
396 { | |
397 if (comment.contains('"') || comment.contains('\n')) | |
398 pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n"; | |
399 else | |
400 pluginString += " rdfs:comment \"" + comment + "\" ;\n"; | |
401 } | |
402 | |
403 if ((hints & kStateIsFilenamePath) == kStateIsFilenamePath) | |
404 { | |
405 #ifdef __MOD_DEVICES__ | |
406 const String& fileTypes(plugin.getStateFileTypes(i)); | |
407 if (fileTypes.isNotEmpty()) | |
408 pluginString += " mod:fileTypes \"" + fileTypes + "\" ; \n"; | |
409 #endif | |
410 pluginString += " rdfs:range atom:Path .\n\n"; | |
411 } | |
412 else | |
413 { | |
414 pluginString += " rdfs:range atom:String .\n\n"; | |
415 } | |
416 | |
417 hasHostVisibleState = true; | |
418 } | |
419 #endif | |
420 | |
421 // plugin | |
422 pluginString += "<" DISTRHO_PLUGIN_URI ">\n"; | |
423 #ifdef DISTRHO_PLUGIN_LV2_CATEGORY | |
424 pluginString += " a " DISTRHO_PLUGIN_LV2_CATEGORY ", lv2:Plugin, doap:Project ;\n"; | |
425 #elif DISTRHO_PLUGIN_IS_SYNTH | |
426 pluginString += " a lv2:InstrumentPlugin, lv2:Plugin, doap:Project ;\n"; | |
427 #else | |
428 pluginString += " a lv2:Plugin, doap:Project ;\n"; | |
429 #endif | |
430 pluginString += "\n"; | |
431 | |
432 addAttribute(pluginString, "lv2:extensionData", lv2ManifestPluginExtensionData, 4); | |
433 addAttribute(pluginString, "lv2:optionalFeature", lv2ManifestPluginOptionalFeatures, 4); | |
434 addAttribute(pluginString, "lv2:requiredFeature", lv2ManifestPluginRequiredFeatures, 4); | |
435 addAttribute(pluginString, "opts:supportedOption", lv2ManifestPluginSupportedOptions, 4); | |
436 | |
437 #if DISTRHO_PLUGIN_WANT_STATE | |
438 if (hasHostVisibleState) | |
439 { | |
440 for (uint32_t i=0, count=plugin.getStateCount(); i < count; ++i) | |
441 { | |
442 const uint32_t hints = plugin.getStateHints(i); | |
443 | |
444 if ((hints & kStateIsHostReadable) == 0x0) | |
445 continue; | |
446 | |
447 const String& key(plugin.getStateKey(i)); | |
448 | |
449 if ((hints & kStateIsHostWritable) == kStateIsHostWritable) | |
450 pluginString += " patch:writable <" DISTRHO_PLUGIN_URI "#" + key + "> ;\n"; | |
451 else | |
452 pluginString += " patch:readable <" DISTRHO_PLUGIN_URI "#" + key + "> ;\n"; | |
453 } | |
454 pluginString += "\n"; | |
455 } | |
456 #endif | |
457 | |
458 // UI | |
459 #if DISTRHO_PLUGIN_HAS_UI | |
460 pluginString += " ui:ui <" DISTRHO_UI_URI "> ;\n"; | |
461 pluginString += "\n"; | |
462 #endif | |
463 | |
464 { | |
465 uint32_t portIndex = 0; | |
466 | |
467 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
468 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++portIndex) | |
469 { | |
470 const AudioPort& port(plugin.getAudioPort(true, i)); | |
471 const bool cvPortScaled = port.hints & kCVPortHasScaledRange; | |
472 | |
473 if (i == 0) | |
474 pluginString += " lv2:port [\n"; | |
475 else | |
476 pluginString += " [\n"; | |
477 | |
478 if (cvPortScaled) | |
479 pluginString += " a lv2:InputPort, lv2:CVPort, mod:CVPort ;\n"; | |
480 else if (port.hints & kAudioPortIsCV) | |
481 pluginString += " a lv2:InputPort, lv2:CVPort ;\n"; | |
482 else | |
483 pluginString += " a lv2:InputPort, lv2:AudioPort ;\n"; | |
484 | |
485 pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |
486 pluginString += " lv2:symbol \"lv2_" + port.symbol + "\" ;\n"; | |
487 pluginString += " lv2:name \"" + port.name + "\" ;\n"; | |
488 | |
489 if (port.hints & kAudioPortIsSidechain) | |
490 pluginString += " lv2:portProperty lv2:isSideChain;\n"; | |
491 | |
492 if (port.groupId != kPortGroupNone) | |
493 { | |
494 pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" | |
495 + plugin.getPortGroupSymbolForId(port.groupId) + "> ;\n"; | |
496 | |
497 switch (port.groupId) | |
498 { | |
499 case kPortGroupMono: | |
500 pluginString += " lv2:designation pg:center ;\n"; | |
501 break; | |
502 case kPortGroupStereo: | |
503 if (i == 1) | |
504 pluginString += " lv2:designation pg:right ;\n"; | |
505 else | |
506 pluginString += " lv2:designation pg:left ;\n"; | |
507 break; | |
508 } | |
509 } | |
510 | |
511 // set ranges | |
512 if (port.hints & kCVPortHasBipolarRange) | |
513 { | |
514 if (cvPortScaled) | |
515 { | |
516 pluginString += " lv2:minimum -5.0 ;\n"; | |
517 pluginString += " lv2:maximum 5.0 ;\n"; | |
518 } | |
519 else | |
520 { | |
521 pluginString += " lv2:minimum -1.0 ;\n"; | |
522 pluginString += " lv2:maximum 1.0 ;\n"; | |
523 } | |
524 } | |
525 else if (port.hints & kCVPortHasNegativeUnipolarRange) | |
526 { | |
527 if (cvPortScaled) | |
528 { | |
529 pluginString += " lv2:minimum -10.0 ;\n"; | |
530 pluginString += " lv2:maximum 0.0 ;\n"; | |
531 } | |
532 else | |
533 { | |
534 pluginString += " lv2:minimum -1.0 ;\n"; | |
535 pluginString += " lv2:maximum 0.0 ;\n"; | |
536 } | |
537 } | |
538 else if (port.hints & kCVPortHasPositiveUnipolarRange) | |
539 { | |
540 if (cvPortScaled) | |
541 { | |
542 pluginString += " lv2:minimum 0.0 ;\n"; | |
543 pluginString += " lv2:maximum 10.0 ;\n"; | |
544 } | |
545 else | |
546 { | |
547 pluginString += " lv2:minimum 0.0 ;\n"; | |
548 pluginString += " lv2:maximum 1.0 ;\n"; | |
549 } | |
550 } | |
551 | |
552 if ((port.hints & (kAudioPortIsCV|kCVPortIsOptional)) == (kAudioPortIsCV|kCVPortIsOptional)) | |
553 pluginString += " lv2:portProperty lv2:connectionOptional;\n"; | |
554 | |
555 if (i+1 == DISTRHO_PLUGIN_NUM_INPUTS) | |
556 pluginString += " ] ;\n"; | |
557 else | |
558 pluginString += " ] ,\n"; | |
559 } | |
560 pluginString += "\n"; | |
561 #endif | |
562 | |
563 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |
564 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++portIndex) | |
565 { | |
566 const AudioPort& port(plugin.getAudioPort(false, i)); | |
567 const bool cvPortScaled = port.hints & kCVPortHasScaledRange; | |
568 | |
569 if (i == 0) | |
570 pluginString += " lv2:port [\n"; | |
571 else | |
572 pluginString += " [\n"; | |
573 | |
574 if (cvPortScaled) | |
575 pluginString += " a lv2:OutputPort, lv2:CVPort, mod:CVPort ;\n"; | |
576 else if (port.hints & kAudioPortIsCV) | |
577 pluginString += " a lv2:OutputPort, lv2:CVPort ;\n"; | |
578 else | |
579 pluginString += " a lv2:OutputPort, lv2:AudioPort ;\n"; | |
580 | |
581 pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |
582 pluginString += " lv2:symbol \"lv2_" + port.symbol + "\" ;\n"; | |
583 pluginString += " lv2:name \"" + port.name + "\" ;\n"; | |
584 | |
585 if (port.hints & kAudioPortIsSidechain) | |
586 pluginString += " lv2:portProperty lv2:isSideChain;\n"; | |
587 | |
588 if (port.groupId != kPortGroupNone) | |
589 { | |
590 pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" | |
591 + plugin.getPortGroupSymbolForId(port.groupId) + "> ;\n"; | |
592 | |
593 switch (port.groupId) | |
594 { | |
595 case kPortGroupMono: | |
596 pluginString += " lv2:designation pg:center ;\n"; | |
597 break; | |
598 case kPortGroupStereo: | |
599 if (i == 1) | |
600 pluginString += " lv2:designation pg:right ;\n"; | |
601 else | |
602 pluginString += " lv2:designation pg:left ;\n"; | |
603 break; | |
604 } | |
605 } | |
606 | |
607 // set ranges | |
608 if (port.hints & kCVPortHasBipolarRange) | |
609 { | |
610 if (cvPortScaled) | |
611 { | |
612 pluginString += " lv2:minimum -5.0 ;\n"; | |
613 pluginString += " lv2:maximum 5.0 ;\n"; | |
614 } | |
615 else | |
616 { | |
617 pluginString += " lv2:minimum -1.0 ;\n"; | |
618 pluginString += " lv2:maximum 1.0 ;\n"; | |
619 } | |
620 } | |
621 else if (port.hints & kCVPortHasNegativeUnipolarRange) | |
622 { | |
623 if (cvPortScaled) | |
624 { | |
625 pluginString += " lv2:minimum -10.0 ;\n"; | |
626 pluginString += " lv2:maximum 0.0 ;\n"; | |
627 } | |
628 else | |
629 { | |
630 pluginString += " lv2:minimum -1.0 ;\n"; | |
631 pluginString += " lv2:maximum 0.0 ;\n"; | |
632 } | |
633 } | |
634 else if (port.hints & kCVPortHasPositiveUnipolarRange) | |
635 { | |
636 if (cvPortScaled) | |
637 { | |
638 pluginString += " lv2:minimum 0.0 ;\n"; | |
639 pluginString += " lv2:maximum 10.0 ;\n"; | |
640 } | |
641 else | |
642 { | |
643 pluginString += " lv2:minimum 0.0 ;\n"; | |
644 pluginString += " lv2:maximum 1.0 ;\n"; | |
645 } | |
646 } | |
647 | |
648 if (i+1 == DISTRHO_PLUGIN_NUM_OUTPUTS) | |
649 pluginString += " ] ;\n"; | |
650 else | |
651 pluginString += " ] ,\n"; | |
652 } | |
653 pluginString += "\n"; | |
654 #endif | |
655 | |
656 #if DISTRHO_LV2_USE_EVENTS_IN | |
657 pluginString += " lv2:port [\n"; | |
658 pluginString += " a lv2:InputPort, atom:AtomPort ;\n"; | |
659 pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |
660 pluginString += " lv2:name \"Events Input\" ;\n"; | |
661 pluginString += " lv2:symbol \"lv2_events_in\" ;\n"; | |
662 pluginString += " rsz:minimumSize " + String(DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE) + " ;\n"; | |
663 pluginString += " atom:bufferType atom:Sequence ;\n"; | |
664 # if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) | |
665 pluginString += " atom:supports atom:String ;\n"; | |
666 # endif | |
667 # if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
668 pluginString += " atom:supports midi:MidiEvent ;\n"; | |
669 # endif | |
670 # if DISTRHO_PLUGIN_WANT_TIMEPOS | |
671 pluginString += " atom:supports <" LV2_TIME__Position "> ;\n"; | |
672 # endif | |
673 # if DISTRHO_PLUGIN_WANT_STATE | |
674 if (hasHostVisibleState) | |
675 { | |
676 pluginString += " atom:supports <" LV2_PATCH__Message "> ;\n"; | |
677 pluginString += " lv2:designation lv2:control ;\n"; | |
678 } | |
679 # endif | |
680 pluginString += " ] ;\n\n"; | |
681 ++portIndex; | |
682 #endif | |
683 | |
684 #if DISTRHO_LV2_USE_EVENTS_OUT | |
685 pluginString += " lv2:port [\n"; | |
686 pluginString += " a lv2:OutputPort, atom:AtomPort ;\n"; | |
687 pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |
688 pluginString += " lv2:name \"Events Output\" ;\n"; | |
689 pluginString += " lv2:symbol \"lv2_events_out\" ;\n"; | |
690 pluginString += " rsz:minimumSize " + String(DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE) + " ;\n"; | |
691 pluginString += " atom:bufferType atom:Sequence ;\n"; | |
692 # if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) | |
693 pluginString += " atom:supports atom:String ;\n"; | |
694 # endif | |
695 # if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
696 pluginString += " atom:supports midi:MidiEvent ;\n"; | |
697 # endif | |
698 # if DISTRHO_PLUGIN_WANT_STATE | |
699 if (hasHostVisibleState) | |
700 { | |
701 pluginString += " atom:supports <" LV2_PATCH__Message "> ;\n"; | |
702 pluginString += " lv2:designation lv2:control ;\n"; | |
703 } | |
704 # endif | |
705 pluginString += " ] ;\n\n"; | |
706 ++portIndex; | |
707 #endif | |
708 | |
709 #if DISTRHO_PLUGIN_WANT_LATENCY | |
710 pluginString += " lv2:port [\n"; | |
711 pluginString += " a lv2:OutputPort, lv2:ControlPort ;\n"; | |
712 pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |
713 pluginString += " lv2:name \"Latency\" ;\n"; | |
714 pluginString += " lv2:symbol \"lv2_latency\" ;\n"; | |
715 pluginString += " lv2:designation lv2:latency ;\n"; | |
716 pluginString += " lv2:portProperty lv2:reportsLatency, lv2:integer, <" LV2_PORT_PROPS__notOnGUI "> ;\n"; | |
717 pluginString += " ] ;\n\n"; | |
718 ++portIndex; | |
719 #endif | |
720 | |
721 for (uint32_t i=0, count=plugin.getParameterCount(); i < count; ++i, ++portIndex) | |
722 { | |
723 if (i == 0) | |
724 pluginString += " lv2:port [\n"; | |
725 else | |
726 pluginString += " [\n"; | |
727 | |
728 if (plugin.isParameterOutput(i)) | |
729 pluginString += " a lv2:OutputPort, lv2:ControlPort ;\n"; | |
730 else | |
731 pluginString += " a lv2:InputPort, lv2:ControlPort ;\n"; | |
732 | |
733 pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |
734 | |
735 bool designated = false; | |
736 | |
737 // designation | |
738 if (plugin.isParameterInput(i)) | |
739 { | |
740 switch (plugin.getParameterDesignation(i)) | |
741 { | |
742 case kParameterDesignationNull: | |
743 break; | |
744 case kParameterDesignationBypass: | |
745 designated = true; | |
746 pluginString += " lv2:name \"Enabled\" ;\n"; | |
747 pluginString += " lv2:symbol \"" DISTRHO_BYPASS_PARAMETER_NAME "\" ;\n"; | |
748 pluginString += " lv2:default 1 ;\n"; | |
749 pluginString += " lv2:minimum 0 ;\n"; | |
750 pluginString += " lv2:maximum 1 ;\n"; | |
751 pluginString += " lv2:portProperty lv2:toggled , lv2:integer ;\n"; | |
752 pluginString += " lv2:designation lv2:enabled ;\n"; | |
753 break; | |
754 } | |
755 } | |
756 | |
757 if (! designated) | |
758 { | |
759 const uint32_t hints = plugin.getParameterHints(i); | |
760 | |
761 // name and symbol | |
762 const String& paramName(plugin.getParameterName(i)); | |
763 | |
764 if (paramName.contains('"')) | |
765 pluginString += " lv2:name \"\"\"" + paramName + "\"\"\" ;\n"; | |
766 else | |
767 pluginString += " lv2:name \"" + paramName + "\" ;\n"; | |
768 | |
769 String symbol(plugin.getParameterSymbol(i)); | |
770 | |
771 if (symbol.isEmpty()) | |
772 symbol = "lv2_port_" + String(portIndex-1); | |
773 | |
774 pluginString += " lv2:symbol \"" + symbol + "\" ;\n"; | |
775 | |
776 // short name | |
777 const String& shortName(plugin.getParameterShortName(i)); | |
778 | |
779 if (shortName.isNotEmpty()) | |
780 pluginString += " lv2:shortName \"\"\"" + shortName + "\"\"\" ;\n"; | |
781 | |
782 // ranges | |
783 const ParameterRanges& ranges(plugin.getParameterRanges(i)); | |
784 | |
785 if (hints & kParameterIsInteger) | |
786 { | |
787 if (plugin.isParameterInput(i)) | |
788 pluginString += " lv2:default " + String(int(ranges.def)) + " ;\n"; | |
789 pluginString += " lv2:minimum " + String(int(ranges.min)) + " ;\n"; | |
790 pluginString += " lv2:maximum " + String(int(ranges.max)) + " ;\n"; | |
791 } | |
792 else if (hints & kParameterIsLogarithmic) | |
793 { | |
794 if (plugin.isParameterInput(i)) | |
795 { | |
796 if (d_isNotZero(ranges.def)) | |
797 pluginString += " lv2:default " + String(ranges.def) + " ;\n"; | |
798 else if (d_isEqual(ranges.def, ranges.max)) | |
799 pluginString += " lv2:default -0.0001 ;\n"; | |
800 else | |
801 pluginString += " lv2:default 0.0001 ;\n"; | |
802 } | |
803 | |
804 if (d_isNotZero(ranges.min)) | |
805 pluginString += " lv2:minimum " + String(ranges.min) + " ;\n"; | |
806 else | |
807 pluginString += " lv2:minimum 0.0001 ;\n"; | |
808 | |
809 if (d_isNotZero(ranges.max)) | |
810 pluginString += " lv2:maximum " + String(ranges.max) + " ;\n"; | |
811 else | |
812 pluginString += " lv2:maximum -0.0001 ;\n"; | |
813 } | |
814 else | |
815 { | |
816 if (plugin.isParameterInput(i)) | |
817 pluginString += " lv2:default " + String(ranges.def) + " ;\n"; | |
818 pluginString += " lv2:minimum " + String(ranges.min) + " ;\n"; | |
819 pluginString += " lv2:maximum " + String(ranges.max) + " ;\n"; | |
820 } | |
821 | |
822 // enumeration | |
823 const ParameterEnumerationValues& enumValues(plugin.getParameterEnumValues(i)); | |
824 | |
825 if (enumValues.count > 0) | |
826 { | |
827 if (enumValues.count >= 2 && enumValues.restrictedMode) | |
828 pluginString += " lv2:portProperty lv2:enumeration ;\n"; | |
829 | |
830 for (uint8_t j=0; j < enumValues.count; ++j) | |
831 { | |
832 const ParameterEnumerationValue& enumValue(enumValues.values[j]); | |
833 | |
834 if (j == 0) | |
835 pluginString += " lv2:scalePoint [\n"; | |
836 else | |
837 pluginString += " [\n"; | |
838 | |
839 if (enumValue.label.contains('"')) | |
840 pluginString += " rdfs:label \"\"\"" + enumValue.label + "\"\"\" ;\n"; | |
841 else | |
842 pluginString += " rdfs:label \"" + enumValue.label + "\" ;\n"; | |
843 | |
844 if (hints & kParameterIsInteger) | |
845 { | |
846 const int rounded = (int)(enumValue.value + (enumValue.value < 0.0f ? -0.5f : 0.5f)); | |
847 pluginString += " rdf:value " + String(rounded) + " ;\n"; | |
848 } | |
849 else | |
850 { | |
851 pluginString += " rdf:value " + String(enumValue.value) + " ;\n"; | |
852 } | |
853 | |
854 if (j+1 == enumValues.count) | |
855 pluginString += " ] ;\n"; | |
856 else | |
857 pluginString += " ] ,\n"; | |
858 } | |
859 } | |
860 | |
861 // MIDI CC binding | |
862 if (const uint8_t midiCC = plugin.getParameterMidiCC(i)) | |
863 { | |
864 pluginString += " midi:binding [\n"; | |
865 pluginString += " a midi:Controller ;\n"; | |
866 pluginString += " midi:controllerNumber " + String(midiCC) + " ;\n"; | |
867 pluginString += " ] ;\n"; | |
868 } | |
869 | |
870 // unit | |
871 const String& unit(plugin.getParameterUnit(i)); | |
872 | |
873 if (unit.isNotEmpty() && ! unit.contains(' ')) | |
874 { | |
875 String lunit(unit); | |
876 lunit.toLower(); | |
877 | |
878 /**/ if (lunit == "db") | |
879 { | |
880 pluginString += " unit:unit unit:db ;\n"; | |
881 } | |
882 else if (lunit == "hz") | |
883 { | |
884 pluginString += " unit:unit unit:hz ;\n"; | |
885 } | |
886 else if (lunit == "khz") | |
887 { | |
888 pluginString += " unit:unit unit:khz ;\n"; | |
889 } | |
890 else if (lunit == "mhz") | |
891 { | |
892 pluginString += " unit:unit unit:mhz ;\n"; | |
893 } | |
894 else if (lunit == "ms") | |
895 { | |
896 pluginString += " unit:unit unit:ms ;\n"; | |
897 } | |
898 else if (lunit == "s") | |
899 { | |
900 pluginString += " unit:unit unit:s ;\n"; | |
901 } | |
902 else if (lunit == "%") | |
903 { | |
904 pluginString += " unit:unit unit:pc ;\n"; | |
905 } | |
906 else | |
907 { | |
908 pluginString += " unit:unit [\n"; | |
909 pluginString += " a unit:Unit ;\n"; | |
910 pluginString += " rdfs:label \"" + unit + "\" ;\n"; | |
911 pluginString += " unit:symbol \"" + unit + "\" ;\n"; | |
912 if (hints & kParameterIsInteger) | |
913 pluginString += " unit:render \"%d " + unit + "\" ;\n"; | |
914 else | |
915 pluginString += " unit:render \"%f " + unit + "\" ;\n"; | |
916 pluginString += " ] ;\n"; | |
917 } | |
918 } | |
919 | |
920 // comment | |
921 const String& comment(plugin.getParameterDescription(i)); | |
922 | |
923 if (comment.isNotEmpty()) | |
924 { | |
925 if (comment.contains('"') || comment.contains('\n')) | |
926 pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n"; | |
927 else | |
928 pluginString += " rdfs:comment \"" + comment + "\" ;\n"; | |
929 } | |
930 | |
931 // hints | |
932 if (hints & kParameterIsBoolean) | |
933 { | |
934 if ((hints & kParameterIsTrigger) == kParameterIsTrigger) | |
935 pluginString += " lv2:portProperty <" LV2_PORT_PROPS__trigger "> ;\n"; | |
936 pluginString += " lv2:portProperty lv2:toggled ;\n"; | |
937 } | |
938 if (hints & kParameterIsInteger) | |
939 pluginString += " lv2:portProperty lv2:integer ;\n"; | |
940 if (hints & kParameterIsLogarithmic) | |
941 pluginString += " lv2:portProperty <" LV2_PORT_PROPS__logarithmic "> ;\n"; | |
942 if (hints & kParameterIsHidden) | |
943 pluginString += " lv2:portProperty <" LV2_PORT_PROPS__notOnGUI "> ;\n"; | |
944 if ((hints & kParameterIsAutomatable) == 0 && plugin.isParameterInput(i)) | |
945 { | |
946 pluginString += " lv2:portProperty <" LV2_PORT_PROPS__expensive "> ,\n"; | |
947 pluginString += " <" LV2_KXSTUDIO_PROPERTIES__NonAutomatable "> ;\n"; | |
948 } | |
949 | |
950 // group | |
951 const uint32_t groupId = plugin.getParameterGroupId(i); | |
952 | |
953 if (groupId != kPortGroupNone) | |
954 pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" | |
955 + plugin.getPortGroupSymbolForId(groupId) + "> ;\n"; | |
956 | |
957 } // ! designated | |
958 | |
959 if (i+1 == count) | |
960 pluginString += " ] ;\n\n"; | |
961 else | |
962 pluginString += " ] ,\n"; | |
963 } | |
964 } | |
965 | |
966 // comment | |
967 { | |
968 const String comment(plugin.getDescription()); | |
969 | |
970 if (comment.isNotEmpty()) | |
971 { | |
972 if (comment.contains('"') || comment.contains('\n')) | |
973 pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n\n"; | |
974 else | |
975 pluginString += " rdfs:comment \"" + comment + "\" ;\n\n"; | |
976 } | |
977 } | |
978 | |
979 #ifdef DISTRHO_PLUGIN_BRAND | |
980 // MOD | |
981 pluginString += " mod:brand \"" DISTRHO_PLUGIN_BRAND "\" ;\n"; | |
982 pluginString += " mod:label \"" DISTRHO_PLUGIN_NAME "\" ;\n\n"; | |
983 #endif | |
984 | |
985 // name | |
986 { | |
987 const String name(plugin.getName()); | |
988 | |
989 if (name.contains('"')) | |
990 pluginString += " doap:name \"\"\"" + name + "\"\"\" ;\n"; | |
991 else | |
992 pluginString += " doap:name \"" + name + "\" ;\n"; | |
993 } | |
994 | |
995 // license | |
996 { | |
997 const String license(plugin.getLicense()); | |
998 | |
999 // Using URL as license | |
1000 if (license.contains("://")) | |
1001 { | |
1002 pluginString += " doap:license <" + license + "> ;\n\n"; | |
1003 } | |
1004 // String contaning quotes, use as-is | |
1005 else if (license.contains('"')) | |
1006 { | |
1007 pluginString += " doap:license \"\"\"" + license + "\"\"\" ;\n\n"; | |
1008 } | |
1009 // Regular license string, convert to URL as much as we can | |
1010 else | |
1011 { | |
1012 const String uplicense(license.asUpper()); | |
1013 | |
1014 // for reference, see https://spdx.org/licenses/ | |
1015 | |
1016 // common licenses | |
1017 /**/ if (uplicense == "AGPL-1.0-ONLY" || | |
1018 uplicense == "AGPL1" || | |
1019 uplicense == "AGPLV1") | |
1020 { | |
1021 pluginString += " doap:license <http://spdx.org/licenses/AGPL-1.0-only.html> ;\n\n"; | |
1022 } | |
1023 else if (uplicense == "AGPL-1.0-OR-LATER" || | |
1024 uplicense == "AGPL1+" || | |
1025 uplicense == "AGPLV1+") | |
1026 { | |
1027 pluginString += " doap:license <http://spdx.org/licenses/AGPL-1.0-or-later.html> ;\n\n"; | |
1028 } | |
1029 else if (uplicense == "AGPL-3.0-ONLY" || | |
1030 uplicense == "AGPL3" || | |
1031 uplicense == "AGPLV3") | |
1032 { | |
1033 pluginString += " doap:license <http://spdx.org/licenses/AGPL-3.0-only.html> ;\n\n"; | |
1034 } | |
1035 else if (uplicense == "AGPL-3.0-OR-LATER" || | |
1036 uplicense == "AGPL3+" || | |
1037 uplicense == "AGPLV3+") | |
1038 { | |
1039 pluginString += " doap:license <http://spdx.org/licenses/AGPL-3.0-or-later.html> ;\n\n"; | |
1040 } | |
1041 else if (uplicense == "APACHE-2.0" || | |
1042 uplicense == "APACHE2" || | |
1043 uplicense == "APACHE-2") | |
1044 { | |
1045 pluginString += " doap:license <http://spdx.org/licenses/Apache-2.0.html> ;\n\n"; | |
1046 } | |
1047 else if (uplicense == "BSD-2-CLAUSE" || | |
1048 uplicense == "BSD2" || | |
1049 uplicense == "BSD-2") | |
1050 { | |
1051 pluginString += " doap:license <http://spdx.org/licenses/BSD-2-Clause.html> ;\n\n"; | |
1052 } | |
1053 else if (uplicense == "BSD-3-CLAUSE" || | |
1054 uplicense == "BSD3" || | |
1055 uplicense == "BSD-3") | |
1056 { | |
1057 pluginString += " doap:license <http://spdx.org/licenses/BSD-3-Clause.html> ;\n\n"; | |
1058 } | |
1059 else if (uplicense == "GPL-2.0-ONLY" || | |
1060 uplicense == "GPL2" || | |
1061 uplicense == "GPLV2") | |
1062 { | |
1063 pluginString += " doap:license <http://spdx.org/licenses/GPL-2.0-only.html> ;\n\n"; | |
1064 } | |
1065 else if (uplicense == "GPL-2.0-OR-LATER" || | |
1066 uplicense == "GPL2+" || | |
1067 uplicense == "GPLV2+" || | |
1068 uplicense == "GPLV2.0+" || | |
1069 uplicense == "GPL V2+") | |
1070 { | |
1071 pluginString += " doap:license <http://spdx.org/licenses/GPL-2.0-or-later.html> ;\n\n"; | |
1072 } | |
1073 else if (uplicense == "GPL-3.0-ONLY" || | |
1074 uplicense == "GPL3" || | |
1075 uplicense == "GPLV3") | |
1076 { | |
1077 pluginString += " doap:license <http://spdx.org/licenses/GPL-3.0-only.html> ;\n\n"; | |
1078 } | |
1079 else if (uplicense == "GPL-3.0-OR-LATER" || | |
1080 uplicense == "GPL3+" || | |
1081 uplicense == "GPLV3+" || | |
1082 uplicense == "GPLV3.0+" || | |
1083 uplicense == "GPL V3+") | |
1084 { | |
1085 pluginString += " doap:license <http://spdx.org/licenses/GPL-3.0-or-later.html> ;\n\n"; | |
1086 } | |
1087 else if (uplicense == "ISC") | |
1088 { | |
1089 pluginString += " doap:license <http://spdx.org/licenses/ISC.html> ;\n\n"; | |
1090 } | |
1091 else if (uplicense == "LGPL-2.0-ONLY" || | |
1092 uplicense == "LGPL2" || | |
1093 uplicense == "LGPLV2") | |
1094 { | |
1095 pluginString += " doap:license <http://spdx.org/licenses/LGPL-2.0-only.html> ;\n\n"; | |
1096 } | |
1097 else if (uplicense == "LGPL-2.0-OR-LATER" || | |
1098 uplicense == "LGPL2+" || | |
1099 uplicense == "LGPLV2+") | |
1100 { | |
1101 pluginString += " doap:license <http://spdx.org/licenses/LGPL-2.0-or-later.html> ;\n\n"; | |
1102 } | |
1103 else if (uplicense == "LGPL-2.1-ONLY" || | |
1104 uplicense == "LGPL2.1" || | |
1105 uplicense == "LGPLV2.1") | |
1106 { | |
1107 pluginString += " doap:license <http://spdx.org/licenses/LGPL-2.1-only.html> ;\n\n"; | |
1108 } | |
1109 else if (uplicense == "LGPL-2.1-OR-LATER" || | |
1110 uplicense == "LGPL2.1+" || | |
1111 uplicense == "LGPLV2.1+") | |
1112 { | |
1113 pluginString += " doap:license <http://spdx.org/licenses/LGPL-2.1-or-later.html> ;\n\n"; | |
1114 } | |
1115 else if (uplicense == "LGPL-3.0-ONLY" || | |
1116 uplicense == "LGPL3" || | |
1117 uplicense == "LGPLV3") | |
1118 { | |
1119 pluginString += " doap:license <http://spdx.org/licenses/LGPL-2.0-only.html> ;\n\n"; | |
1120 } | |
1121 else if (uplicense == "LGPL-3.0-OR-LATER" || | |
1122 uplicense == "LGPL3+" || | |
1123 uplicense == "LGPLV3+") | |
1124 { | |
1125 pluginString += " doap:license <http://spdx.org/licenses/LGPL-3.0-or-later.html> ;\n\n"; | |
1126 } | |
1127 else if (uplicense == "MIT") | |
1128 { | |
1129 pluginString += " doap:license <http://spdx.org/licenses/MIT.html> ;\n\n"; | |
1130 } | |
1131 | |
1132 // generic fallbacks | |
1133 else if (uplicense.startsWith("GPL")) | |
1134 { | |
1135 pluginString += " doap:license <http://opensource.org/licenses/gpl-license> ;\n\n"; | |
1136 } | |
1137 else if (uplicense.startsWith("LGPL")) | |
1138 { | |
1139 pluginString += " doap:license <http://opensource.org/licenses/lgpl-license> ;\n\n"; | |
1140 } | |
1141 | |
1142 // unknown or not handled yet, log a warning | |
1143 else | |
1144 { | |
1145 d_stderr("Unknown license string '%s'", license.buffer()); | |
1146 pluginString += " doap:license \"" + license + "\" ;\n\n"; | |
1147 } | |
1148 } | |
1149 } | |
1150 | |
1151 // developer | |
1152 { | |
1153 const String homepage(plugin.getHomePage()); | |
1154 const String maker(plugin.getMaker()); | |
1155 | |
1156 pluginString += " doap:maintainer [\n"; | |
1157 | |
1158 if (maker.contains('"')) | |
1159 pluginString += " foaf:name \"\"\"" + maker + "\"\"\" ;\n"; | |
1160 else | |
1161 pluginString += " foaf:name \"" + maker + "\" ;\n"; | |
1162 | |
1163 if (homepage.isNotEmpty()) | |
1164 pluginString += " foaf:homepage <" + homepage + "> ;\n"; | |
1165 | |
1166 pluginString += " ] ;\n\n"; | |
1167 } | |
1168 | |
1169 { | |
1170 const uint32_t version(plugin.getVersion()); | |
1171 | |
1172 const uint32_t majorVersion = (version & 0xFF0000) >> 16; | |
1173 /* */ uint32_t minorVersion = (version & 0x00FF00) >> 8; | |
1174 const uint32_t microVersion = (version & 0x0000FF) >> 0; | |
1175 | |
1176 // NOTE: LV2 ignores 'major' version and says 0 for minor is pre-release/unstable. | |
1177 if (majorVersion > 0) | |
1178 minorVersion += 2; | |
1179 | |
1180 pluginString += " lv2:microVersion " + String(microVersion) + " ;\n"; | |
1181 pluginString += " lv2:minorVersion " + String(minorVersion) + " .\n"; | |
1182 } | |
1183 | |
1184 // port groups | |
1185 if (const uint32_t portGroupCount = plugin.getPortGroupCount()) | |
1186 { | |
1187 bool isInput, isOutput; | |
1188 | |
1189 for (uint32_t i = 0; i < portGroupCount; ++i) | |
1190 { | |
1191 const PortGroupWithId& portGroup(plugin.getPortGroupByIndex(i)); | |
1192 DISTRHO_SAFE_ASSERT_CONTINUE(portGroup.groupId != kPortGroupNone); | |
1193 DISTRHO_SAFE_ASSERT_CONTINUE(portGroup.symbol.isNotEmpty()); | |
1194 | |
1195 pluginString += "\n<" DISTRHO_PLUGIN_URI "#portGroup_" + portGroup.symbol + ">\n"; | |
1196 isInput = isOutput = false; | |
1197 | |
1198 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
1199 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS && !isInput; ++i) | |
1200 isInput = plugin.getAudioPort(true, i).groupId == portGroup.groupId; | |
1201 #endif | |
1202 | |
1203 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |
1204 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS && !isOutput; ++i) | |
1205 isOutput = plugin.getAudioPort(false, i).groupId == portGroup.groupId; | |
1206 #endif | |
1207 | |
1208 for (uint32_t i=0, count=plugin.getParameterCount(); i < count && (!isInput || !isOutput); ++i) | |
1209 { | |
1210 if (plugin.getParameterGroupId(i) == portGroup.groupId) | |
1211 { | |
1212 isInput = isInput || plugin.isParameterInput(i); | |
1213 isOutput = isOutput || plugin.isParameterOutput(i); | |
1214 } | |
1215 } | |
1216 | |
1217 pluginString += " a "; | |
1218 | |
1219 if (isInput && !isOutput) | |
1220 pluginString += "pg:InputGroup"; | |
1221 else if (isOutput && !isInput) | |
1222 pluginString += "pg:OutputGroup"; | |
1223 else | |
1224 pluginString += "pg:Group"; | |
1225 | |
1226 switch (portGroup.groupId) | |
1227 { | |
1228 case kPortGroupMono: | |
1229 pluginString += " , pg:MonoGroup"; | |
1230 break; | |
1231 case kPortGroupStereo: | |
1232 pluginString += " , pg:StereoGroup"; | |
1233 break; | |
1234 } | |
1235 | |
1236 pluginString += " ;\n"; | |
1237 | |
1238 // pluginString += " rdfs:label \"" + portGroup.name + "\" ;\n"; | |
1239 pluginString += " lv2:name \"" + portGroup.name + "\" ;\n"; | |
1240 pluginString += " lv2:symbol \"" + portGroup.symbol + "\" .\n"; | |
1241 } | |
1242 } | |
1243 | |
1244 pluginFile << pluginString; | |
1245 pluginFile.close(); | |
1246 std::cout << " done!" << std::endl; | |
1247 } | |
1248 | |
1249 #if DISTRHO_PLUGIN_USES_MODGUI && !DISTRHO_PLUGIN_USES_CUSTOM_MODGUI | |
1250 { | |
1251 std::cout << "Writing modgui.ttl..."; std::cout.flush(); | |
1252 std::fstream modguiFile("modgui.ttl", std::ios::out); | |
1253 | |
1254 String modguiString; | |
1255 modguiString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |
1256 modguiString += "@prefix modgui: <http://moddevices.com/ns/modgui#> .\n"; | |
1257 modguiString += "\n"; | |
1258 | |
1259 modguiString += "<" DISTRHO_PLUGIN_URI ">\n"; | |
1260 modguiString += " modgui:gui [\n"; | |
1261 #ifdef DISTRHO_PLUGIN_BRAND | |
1262 modguiString += " modgui:brand \"" DISTRHO_PLUGIN_BRAND "\" ;\n"; | |
1263 #endif | |
1264 modguiString += " modgui:label \"" DISTRHO_PLUGIN_NAME "\" ;\n"; | |
1265 modguiString += " modgui:resourcesDirectory <modgui> ;\n"; | |
1266 modguiString += " modgui:iconTemplate <modgui/icon.html> ;\n"; | |
1267 modguiString += " modgui:javascript <modgui/javascript.js> ;\n"; | |
1268 modguiString += " modgui:stylesheet <modgui/stylesheet.css> ;\n"; | |
1269 modguiString += " modgui:screenshot <modgui/screenshot.png> ;\n"; | |
1270 modguiString += " modgui:thumbnail <modgui/thumbnail.png> ;\n"; | |
1271 | |
1272 uint32_t numParametersOutputs = 0; | |
1273 for (uint32_t i=0, count=plugin.getParameterCount(); i < count; ++i) | |
1274 { | |
1275 if (plugin.isParameterOutput(i)) | |
1276 ++numParametersOutputs; | |
1277 } | |
1278 if (numParametersOutputs != 0) | |
1279 { | |
1280 modguiString += " modgui:monitoredOutputs [\n"; | |
1281 for (uint32_t i=0, j=0, count=plugin.getParameterCount(); i < count; ++i) | |
1282 { | |
1283 if (!plugin.isParameterOutput(i)) | |
1284 continue; | |
1285 modguiString += " lv2:symbol \"" + plugin.getParameterSymbol(i) + "\" ;\n"; | |
1286 if (++j != numParametersOutputs) | |
1287 modguiString += " ] , [\n"; | |
1288 } | |
1289 modguiString += " ] ;\n"; | |
1290 } | |
1291 | |
1292 modguiString += " ] .\n"; | |
1293 | |
1294 modguiFile << modguiString; | |
1295 modguiFile.close(); | |
1296 std::cout << " done!" << std::endl; | |
1297 } | |
1298 | |
1299 #ifdef DISTRHO_OS_WINDOWS | |
1300 ::_mkdir("modgui"); | |
1301 #else | |
1302 ::mkdir("modgui", 0755); | |
1303 #endif | |
1304 | |
1305 { | |
1306 std::cout << "Writing modgui/javascript.js..."; std::cout.flush(); | |
1307 std::fstream jsFile("modgui/javascript.js", std::ios::out); | |
1308 | |
1309 String jsString; | |
1310 jsString += "function(e,f){\n"; | |
1311 jsString += "'use strict';\nvar ps=["; | |
1312 | |
1313 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |
1314 jsString += "'lv2_" + plugin.getAudioPort(false, i).symbol + "',"; | |
1315 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |
1316 jsString += "'lv2_" + plugin.getAudioPort(true, i).symbol + "',"; | |
1317 #if DISTRHO_LV2_USE_EVENTS_IN | |
1318 jsString += "'lv2_events_in',"; | |
1319 #endif | |
1320 #if DISTRHO_LV2_USE_EVENTS_OUT | |
1321 jsString += "'lv2_events_out',"; | |
1322 #endif | |
1323 #if DISTRHO_PLUGIN_WANT_LATENCY | |
1324 jsString += "'lv2_latency',"; | |
1325 #endif | |
1326 | |
1327 int32_t enabledIndex = INT32_MAX; | |
1328 for (uint32_t i=0, count=plugin.getParameterCount(); i < count; ++i) | |
1329 { | |
1330 jsString += "'" + plugin.getParameterSymbol(i) + "',"; | |
1331 if (plugin.getParameterDesignation(i) == kParameterDesignationBypass) | |
1332 enabledIndex = i; | |
1333 } | |
1334 jsString += "];\n"; | |
1335 jsString += "var ei=" + String(enabledIndex != INT32_MAX ? enabledIndex : -1) + ";\n\n"; | |
1336 jsString += "if(e.type==='start'){\n"; | |
1337 jsString += "e.data.p={p:{},c:{},};\n\n"; | |
1338 jsString += "var err=[];\n"; | |
1339 jsString += "if(typeof(WebAssembly)==='undefined'){err.push('WebAssembly unsupported');}\n"; | |
1340 jsString += "else{\n"; | |
1341 jsString += "if(!WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,2,1,0,5,3,1,0,1,10,14,1,12,0,65,0,65,0,65,0,252,10,0,0,11])))"; | |
1342 jsString += "err.push('Bulk Memory Operations unsupported');\n"; | |
1343 jsString += "if(!WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,2,8,1,1,97,1,98,3,127,1,6,6,1,127,1,65,0,11,7,5,1,1,97,3,1])))"; | |
1344 jsString += "err.push('Importable/Exportable mutable globals unsupported');\n"; | |
1345 jsString += "}\n"; | |
1346 jsString += "if(err.length!==0){e.icon.find('.canvas_wrapper').html('<h2>'+err.join('<br>')+'</h2>');return;}\n\n"; | |
1347 jsString += "var s=document.createElement('script');\n"; | |
1348 jsString += "s.setAttribute('async',true);\n"; | |
1349 jsString += "s.setAttribute('src',e.api_version>=3?f.get_custom_resource_filename('module.js'):('/resources/module.js?uri='+escape(\"" DISTRHO_PLUGIN_URI "\")+'&r='+VERSION));\n"; | |
1350 jsString += "s.setAttribute('type','text/javascript');\n"; | |
1351 jsString += "s.onload=function(){\n"; | |
1352 jsString += " Module_" DISTRHO_PLUGIN_MODGUI_CLASS_NAME "({\n"; | |
1353 jsString += " locateFile: function(p,_){return e.api_version>=3?f.get_custom_resource_filename(p):('/resources/'+p+'?uri='+escape(\"" DISTRHO_PLUGIN_URI "\")+'&r='+VERSION)},\n"; | |
1354 jsString += " postRun:function(m){\n"; | |
1355 jsString += " var cn=e.icon.attr('mod-instance').replaceAll('/','_');\n"; | |
1356 jsString += " var cnl=m.lengthBytesUTF8(cn) + 1;\n"; | |
1357 jsString += " var cna=m._malloc(cnl);\n"; | |
1358 jsString += " m.stringToUTF8(cn, cna, cnl);\n"; | |
1359 jsString += " e.icon.find('canvas')[0].id=cn;\n"; | |
1360 jsString += " var a=m.addFunction(function(i,v){f.set_port_value(ps[i],v);},'vif');\n"; | |
1361 jsString += " var b=m.addFunction(function(u,v){f.patch_set(m.UTF8ToString(u),'s',m.UTF8ToString(v));},'vpp');\n"; | |
1362 jsString += " var h=m._modgui_init(cna,a,b);\n"; | |
1363 jsString += " m._free(cna);\n"; | |
1364 jsString += " e.data.h=h;\n"; | |
1365 jsString += " e.data.m=m;\n"; | |
1366 jsString += " for(var u in e.data.p.p){\n"; | |
1367 jsString += " var ul=m.lengthBytesUTF8(u)+1,ua=m._malloc(ul),v=e.data.p.p[u],vl=m.lengthBytesUTF8(v)+1,va=m._malloc(vl);\n"; | |
1368 jsString += " m.stringToUTF8(u,ua,ul);\n"; | |
1369 jsString += " m.stringToUTF8(v,va,vl);\n"; | |
1370 jsString += " m._modgui_patch_set(h, ua, va);\n"; | |
1371 jsString += " m._free(ua);\n"; | |
1372 jsString += " m._free(va);\n"; | |
1373 jsString += " }\n"; | |
1374 jsString += " for(var symbol in e.data.p.c){m._modgui_param_set(h,ps.indexOf(symbol),e.data.p.c[symbol]);}\n"; | |
1375 jsString += " delete e.data.p;\n"; | |
1376 jsString += " window.dispatchEvent(new Event('resize'));\n"; | |
1377 jsString += " },\n"; | |
1378 jsString += " canvas:(function(){var c=e.icon.find('canvas')[0];c.addEventListener('webglcontextlost',function(e2){alert('WebGL context lost. You will need to reload the page.');e2.preventDefault();},false);return c;})(),\n"; | |
1379 jsString += " });\n"; | |
1380 jsString += "};\n"; | |
1381 jsString += "document.head.appendChild(s);\n\n"; | |
1382 jsString += "}else if(e.type==='change'){\n\n"; | |
1383 jsString += "if(e.data.h && e.data.m){\n"; | |
1384 jsString += " var m=e.data.m;\n"; | |
1385 jsString += " if(e.uri){\n"; | |
1386 jsString += " var ul=m.lengthBytesUTF8(e.uri)+1,ua=m._malloc(ul),vl=m.lengthBytesUTF8(e.value)+1,va=m._malloc(vl);\n"; | |
1387 jsString += " m.stringToUTF8(e.uri,ua,ul);\n"; | |
1388 jsString += " m.stringToUTF8(e.value,va,vl);\n"; | |
1389 jsString += " m._modgui_patch_set(e.data.h,ua,va);\n"; | |
1390 jsString += " m._free(ua);\n"; | |
1391 jsString += " m._free(va);\n"; | |
1392 jsString += " }else if(e.symbol===':bypass'){return;\n"; | |
1393 jsString += " }else{m._modgui_param_set(e.data.h,ps.indexOf(e.symbol),e.value);}\n"; | |
1394 jsString += "}else{\n"; | |
1395 jsString += " if(e.symbol===':bypass')return;\n"; | |
1396 jsString += " if(e.uri){e.data.p.p[e.uri]=e.value;}else{e.data.p.c[e.symbol]=e.value;}\n"; | |
1397 jsString += "}\n\n"; | |
1398 jsString += "}else if(e.type==='end'){\n"; | |
1399 jsString += " if(e.data.h && e.data.m){\n"; | |
1400 jsString += " var h = e.data.h;\n"; | |
1401 jsString += " var m = e.data.m;\n"; | |
1402 jsString += " e.data.h = e.data.m = null;\n"; | |
1403 jsString += " m._modgui_cleanup(h);\n"; | |
1404 jsString += "}\n\n"; | |
1405 jsString += "}\n}\n"; | |
1406 jsFile << jsString; | |
1407 jsFile.close(); | |
1408 std::cout << " done!" << std::endl; | |
1409 } | |
1410 | |
1411 { | |
1412 std::cout << "Writing modgui/icon.html..."; std::cout.flush(); | |
1413 std::fstream iconFile("modgui/icon.html", std::ios::out); | |
1414 | |
1415 iconFile << "<div class='" DISTRHO_PLUGIN_MODGUI_CLASS_NAME " mod-pedal'>" << std::endl; | |
1416 iconFile << " <div mod-role='drag-handle' class='mod-drag-handle'></div>" << std::endl; | |
1417 iconFile << " <div class='mod-plugin-title'><h1>{{#brand}}{{brand}} | {{/brand}}{{label}}</h1></div>" << std::endl; | |
1418 iconFile << " <div class='mod-light on' mod-role='bypass-light'></div>" << std::endl; | |
1419 iconFile << " <div class='mod-control-group mod-switch'>" << std::endl; | |
1420 iconFile << " <div class='mod-control-group mod-switch-image mod-port transport' mod-role='bypass' mod-widget='film'></div>" << std::endl; | |
1421 iconFile << " </div>" << std::endl; | |
1422 iconFile << " <div class='canvas_wrapper'>" << std::endl; | |
1423 iconFile << " <canvas oncontextmenu='event.preventDefault()' tabindex=-1></canvas>" << std::endl; | |
1424 iconFile << " </div>" << std::endl; | |
1425 iconFile << " <div class='mod-pedal-input'>" << std::endl; | |
1426 iconFile << " {{#effect.ports.audio.input}}" << std::endl; | |
1427 iconFile << " <div class='mod-input mod-input-disconnected' title='{{name}}' mod-role='input-audio-port' mod-port-symbol='{{symbol}}'>" << std::endl; | |
1428 iconFile << " <div class='mod-pedal-input-image'></div>" << std::endl; | |
1429 iconFile << " </div>" << std::endl; | |
1430 iconFile << " {{/effect.ports.audio.input}}" << std::endl; | |
1431 iconFile << " {{#effect.ports.midi.input}}" << std::endl; | |
1432 iconFile << " <div class='mod-input mod-input-disconnected' title='{{name}}' mod-role='input-midi-port' mod-port-symbol='{{symbol}}'>" << std::endl; | |
1433 iconFile << " <div class='mod-pedal-input-image'></div>" << std::endl; | |
1434 iconFile << " </div>" << std::endl; | |
1435 iconFile << " {{/effect.ports.midi.input}}" << std::endl; | |
1436 iconFile << " {{#effect.ports.cv.input}}" << std::endl; | |
1437 iconFile << " <div class='mod-input mod-input-disconnected' title='{{name}}' mod-role='input-cv-port' mod-port-symbol='{{symbol}}'>" << std::endl; | |
1438 iconFile << " <div class='mod-pedal-input-image'></div>" << std::endl; | |
1439 iconFile << " </div>" << std::endl; | |
1440 iconFile << " {{/effect.ports.cv.input}}" << std::endl; | |
1441 iconFile << " </div>" << std::endl; | |
1442 iconFile << " <div class='mod-pedal-output'>" << std::endl; | |
1443 iconFile << " {{#effect.ports.audio.output}}" << std::endl; | |
1444 iconFile << " <div class='mod-output mod-output-disconnected' title='{{name}}' mod-role='output-audio-port' mod-port-symbol='{{symbol}}'>" << std::endl; | |
1445 iconFile << " <div class='mod-pedal-output-image'></div>" << std::endl; | |
1446 iconFile << " </div>" << std::endl; | |
1447 iconFile << " {{/effect.ports.audio.output}}" << std::endl; | |
1448 iconFile << " {{#effect.ports.midi.output}}" << std::endl; | |
1449 iconFile << " <div class='mod-output mod-output-disconnected' title='{{name}}' mod-role='output-midi-port' mod-port-symbol='{{symbol}}'>" << std::endl; | |
1450 iconFile << " <div class='mod-pedal-output-image'></div>" << std::endl; | |
1451 iconFile << " </div>" << std::endl; | |
1452 iconFile << " {{/effect.ports.midi.output}}" << std::endl; | |
1453 iconFile << " {{#effect.ports.cv.output}}" << std::endl; | |
1454 iconFile << " <div class='mod-output mod-output-disconnected' title='{{name}}' mod-role='output-cv-port' mod-port-symbol='{{symbol}}'>" << std::endl; | |
1455 iconFile << " <div class='mod-pedal-output-image'></div>" << std::endl; | |
1456 iconFile << " </div>" << std::endl; | |
1457 iconFile << " {{/effect.ports.cv.output}}" << std::endl; | |
1458 iconFile << " </div>" << std::endl; | |
1459 iconFile << "</div>" << std::endl; | |
1460 | |
1461 iconFile.close(); | |
1462 std::cout << " done!" << std::endl; | |
1463 } | |
1464 | |
1465 { | |
1466 std::cout << "Writing modgui/stylesheet.css..."; std::cout.flush(); | |
1467 std::fstream stylesheetFile("modgui/stylesheet.css", std::ios::out); | |
1468 | |
1469 stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal{" << std::endl; | |
1470 stylesheetFile << " padding:0;" << std::endl; | |
1471 stylesheetFile << " margin:0;" << std::endl; | |
1472 stylesheetFile << " width:" + String(DISTRHO_UI_DEFAULT_WIDTH) + "px;" << std::endl; | |
1473 stylesheetFile << " height:" + String(DISTRHO_UI_DEFAULT_HEIGHT + 50) + "px;" << std::endl; | |
1474 stylesheetFile << " background:#2a2e32;" << std::endl; | |
1475 stylesheetFile << " border-radius:20px 20px 0 0;" << std::endl; | |
1476 stylesheetFile << " color:#fff;" << std::endl; | |
1477 stylesheetFile << "}" << std::endl; | |
1478 stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .canvas_wrapper{" << std::endl; | |
1479 stylesheetFile << " --device-pixel-ratio:1;" << std::endl; | |
1480 stylesheetFile << " /*image-rendering:pixelated;*/" << std::endl; | |
1481 stylesheetFile << " /*image-rendering:crisp-edges;*/" << std::endl; | |
1482 stylesheetFile << " background:#000;" << std::endl; | |
1483 stylesheetFile << " position:absolute;" << std::endl; | |
1484 stylesheetFile << " top:50px;" << std::endl; | |
1485 stylesheetFile << " transform-origin:0 0 0;" << std::endl; | |
1486 stylesheetFile << " transform:scale(calc(1/var(--device-pixel-ratio)));" << std::endl; | |
1487 stylesheetFile << " width:" + String(DISTRHO_UI_DEFAULT_WIDTH) + "px;" << std::endl; | |
1488 stylesheetFile << " height:" + String(DISTRHO_UI_DEFAULT_HEIGHT) + "px;" << std::endl; | |
1489 stylesheetFile << " text-align:center;" << std::endl; | |
1490 stylesheetFile << " z-index:21;" << std::endl; | |
1491 stylesheetFile << "}" << std::endl; | |
1492 stylesheetFile << "/*" << std::endl; | |
1493 stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .canvas_wrapper:focus-within{" << std::endl; | |
1494 stylesheetFile << " z-index:21;" << std::endl; | |
1495 stylesheetFile << "}" << std::endl; | |
1496 stylesheetFile << "*/" << std::endl; | |
1497 stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-plugin-title{" << std::endl; | |
1498 stylesheetFile << " position:absolute;" << std::endl; | |
1499 stylesheetFile << " text-align:center;" << std::endl; | |
1500 stylesheetFile << " width:100%;" << std::endl; | |
1501 stylesheetFile << "}" << std::endl; | |
1502 stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal h1{" << std::endl; | |
1503 stylesheetFile << " font-size:20px;" << std::endl; | |
1504 stylesheetFile << " font-weight:bold;" << std::endl; | |
1505 stylesheetFile << " line-height:50px;" << std::endl; | |
1506 stylesheetFile << " margin:0;" << std::endl; | |
1507 stylesheetFile << "}" << std::endl; | |
1508 stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-control-group{" << std::endl; | |
1509 stylesheetFile << " position:absolute;" << std::endl; | |
1510 stylesheetFile << " left:5px;" << std::endl; | |
1511 stylesheetFile << " z-index:35;" << std::endl; | |
1512 stylesheetFile << "}" << std::endl; | |
1513 stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-pedal-input," << std::endl; | |
1514 stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-pedal-output{" << std::endl; | |
1515 stylesheetFile << " top:75px;" << std::endl; | |
1516 stylesheetFile << "}" << std::endl; | |
1517 stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-audio-input," << std::endl; | |
1518 stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-audio-output{" << std::endl; | |
1519 stylesheetFile << " margin-bottom:25px;" << std::endl; | |
1520 stylesheetFile << "}" << std::endl; | |
1521 stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .jack-disconnected{" << std::endl; | |
1522 stylesheetFile << " top:0px!important;" << std::endl; | |
1523 stylesheetFile << "}" << std::endl; | |
1524 stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-switch-image{" << std::endl; | |
1525 stylesheetFile << " background-image: url(/img/switch.png);" << std::endl; | |
1526 stylesheetFile << " background-position: left center;" << std::endl; | |
1527 stylesheetFile << " background-repeat: no-repeat;" << std::endl; | |
1528 stylesheetFile << " background-size: auto 50px;" << std::endl; | |
1529 stylesheetFile << " font-weight: bold;" << std::endl; | |
1530 stylesheetFile << " width: 100px;" << std::endl; | |
1531 stylesheetFile << " height: 50px;" << std::endl; | |
1532 stylesheetFile << " cursor: pointer;" << std::endl; | |
1533 stylesheetFile << "}" << std::endl; | |
1534 stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-switch-image.off{" << std::endl; | |
1535 stylesheetFile << " background-position: right center !important;" << std::endl; | |
1536 stylesheetFile << "}" << std::endl; | |
1537 stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-switch-image.on{" << std::endl; | |
1538 stylesheetFile << " background-position: left center !important;" << std::endl; | |
1539 stylesheetFile << "}" << std::endl; | |
1540 | |
1541 stylesheetFile.close(); | |
1542 std::cout << " done!" << std::endl; | |
1543 } | |
1544 #endif // DISTRHO_PLUGIN_USES_MODGUI && !DISTRHO_PLUGIN_USES_CUSTOM_MODGUI | |
1545 | |
1546 // --------------------------------------------- | |
1547 | |
1548 #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |
1549 { | |
1550 std::cout << "Writing " << uiTTL << "..."; std::cout.flush(); | |
1551 std::fstream uiFile(uiTTL, std::ios::out); | |
1552 | |
1553 String uiString; | |
1554 uiString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |
1555 uiString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |
1556 uiString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; | |
1557 uiString += "\n"; | |
1558 | |
1559 uiString += "<" DISTRHO_UI_URI ">\n"; | |
1560 | |
1561 addAttribute(uiString, "lv2:extensionData", lv2ManifestUiExtensionData, 4); | |
1562 addAttribute(uiString, "lv2:optionalFeature", lv2ManifestUiOptionalFeatures, 4); | |
1563 addAttribute(uiString, "lv2:requiredFeature", lv2ManifestUiRequiredFeatures, 4); | |
1564 addAttribute(uiString, "opts:supportedOption", lv2ManifestUiSupportedOptions, 4, true); | |
1565 | |
1566 uiFile << uiString; | |
1567 uiFile.close(); | |
1568 std::cout << " done!" << std::endl; | |
1569 } | |
1570 #endif | |
1571 | |
1572 // --------------------------------------------- | |
1573 | |
1574 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
1575 { | |
1576 std::cout << "Writing presets.ttl..."; std::cout.flush(); | |
1577 std::fstream presetsFile("presets.ttl", std::ios::out); | |
1578 | |
1579 String presetsString; | |
1580 presetsString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |
1581 presetsString += "@prefix pset: <" LV2_PRESETS_PREFIX "> .\n"; | |
1582 # if DISTRHO_PLUGIN_WANT_STATE | |
1583 presetsString += "@prefix owl: <http://www.w3.org/2002/07/owl#> .\n"; | |
1584 presetsString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; | |
1585 presetsString += "@prefix state: <" LV2_STATE_PREFIX "> .\n"; | |
1586 presetsString += "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n"; | |
1587 # endif | |
1588 presetsString += "\n"; | |
1589 | |
1590 const uint32_t numParameters = plugin.getParameterCount(); | |
1591 const uint32_t numPrograms = plugin.getProgramCount(); | |
1592 # if DISTRHO_PLUGIN_WANT_FULL_STATE | |
1593 const uint32_t numStates = plugin.getStateCount(); | |
1594 const bool valid = numParameters != 0 || numStates != 0; | |
1595 # else | |
1596 const bool valid = numParameters != 0; | |
1597 # endif | |
1598 | |
1599 DISTRHO_CUSTOM_SAFE_ASSERT_RETURN("Programs require parameters or full state", valid, presetsFile.close()); | |
1600 | |
1601 const String presetSeparator(std::strstr(DISTRHO_PLUGIN_URI, "#") != nullptr ? ":" : "#"); | |
1602 | |
1603 char strBuf[0xff+1]; | |
1604 strBuf[0xff] = '\0'; | |
1605 | |
1606 String presetString; | |
1607 | |
1608 # if DISTRHO_PLUGIN_WANT_FULL_STATE | |
1609 for (uint32_t i=0; i<numStates; ++i) | |
1610 { | |
1611 if (plugin.getStateHints(i) & kStateIsHostReadable) | |
1612 continue; | |
1613 | |
1614 // readable states are defined as lv2 parameters. | |
1615 // non-readable states have no definition, but one is needed for presets and ttl validation. | |
1616 presetString = "<" DISTRHO_PLUGIN_LV2_STATE_PREFIX + plugin.getStateKey(i) + ">\n"; | |
1617 presetString += " a owl:DatatypeProperty ;\n"; | |
1618 presetString += " rdfs:label \"Plugin state key-value string pair\" ;\n"; | |
1619 presetString += " rdfs:domain state:State ;\n"; | |
1620 presetString += " rdfs:range xsd:string .\n\n"; | |
1621 presetsString += presetString; | |
1622 } | |
1623 # endif | |
1624 | |
1625 for (uint32_t i=0; i<numPrograms; ++i) | |
1626 { | |
1627 std::snprintf(strBuf, 0xff, "%03i", i+1); | |
1628 | |
1629 plugin.loadProgram(i); | |
1630 | |
1631 presetString = "<" DISTRHO_PLUGIN_URI + presetSeparator + "preset" + strBuf + ">\n"; | |
1632 | |
1633 # if DISTRHO_PLUGIN_WANT_FULL_STATE | |
1634 presetString += " state:state [\n"; | |
1635 for (uint32_t j=0; j<numStates; ++j) | |
1636 { | |
1637 const String key = plugin.getStateKey(j); | |
1638 const String value = plugin.getStateValue(key); | |
1639 | |
1640 presetString += " <"; | |
1641 | |
1642 if (plugin.getStateHints(j) & kStateIsHostReadable) | |
1643 presetString += DISTRHO_PLUGIN_URI "#"; | |
1644 else | |
1645 presetString += DISTRHO_PLUGIN_LV2_STATE_PREFIX; | |
1646 | |
1647 presetString += key + ">"; | |
1648 | |
1649 if (value.length() < 10) | |
1650 presetString += " \"" + value + "\" ;\n"; | |
1651 else | |
1652 presetString += "\n\"\"\"" + value + "\"\"\" ;\n"; | |
1653 } | |
1654 | |
1655 if (numParameters > 0) | |
1656 presetString += " ] ;\n\n"; | |
1657 else | |
1658 presetString += " ] .\n\n"; | |
1659 # endif | |
1660 | |
1661 bool firstParameter = true; | |
1662 | |
1663 for (uint32_t j=0; j <numParameters; ++j) | |
1664 { | |
1665 if (plugin.isParameterOutput(j)) | |
1666 continue; | |
1667 | |
1668 if (firstParameter) | |
1669 { | |
1670 presetString += " lv2:port [\n"; | |
1671 firstParameter = false; | |
1672 } | |
1673 else | |
1674 { | |
1675 presetString += " [\n"; | |
1676 } | |
1677 | |
1678 String parameterSymbol = plugin.getParameterSymbol(j); | |
1679 float parameterValue = plugin.getParameterValue(j); | |
1680 | |
1681 if (plugin.getParameterDesignation(j) == kParameterDesignationBypass) | |
1682 { | |
1683 parameterSymbol = DISTRHO_BYPASS_PARAMETER_NAME; | |
1684 parameterValue = 1.0f - parameterValue; | |
1685 } | |
1686 | |
1687 presetString += " lv2:symbol \"" + parameterSymbol + "\" ;\n"; | |
1688 | |
1689 if (plugin.getParameterHints(j) & kParameterIsInteger) | |
1690 presetString += " pset:value " + String(int(parameterValue)) + " ;\n"; | |
1691 else | |
1692 presetString += " pset:value " + String(parameterValue) + " ;\n"; | |
1693 | |
1694 if (j+1 == numParameters || plugin.isParameterOutput(j+1)) | |
1695 presetString += " ] .\n\n"; | |
1696 else | |
1697 presetString += " ] ,\n"; | |
1698 } | |
1699 | |
1700 presetsString += presetString; | |
1701 } | |
1702 | |
1703 presetsFile << presetsString; | |
1704 presetsFile.close(); | |
1705 std::cout << " done!" << std::endl; | |
1706 } | |
1707 #endif | |
1708 } |