DISTRHO Plugin Framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

850 lines
31KB

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