The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

2173 lines
69KB

  1. /*
  2. ==============================================================================
  3. Juce LV2 Wrapper
  4. ==============================================================================
  5. */
  6. // Your project must contain an AppConfig.h file with your project-specific settings in it,
  7. // and your header search path must make it accessible to the module's files.
  8. #include "AppConfig.h"
  9. #include "../utility/juce_CheckSettingMacros.h"
  10. #include "../../juce_core/system/juce_TargetPlatform.h" // for JUCE_LINUX
  11. #if JucePlugin_Build_LV2
  12. /** Plugin requires processing with a fixed/constant block size */
  13. #ifndef JucePlugin_WantsLV2FixedBlockSize
  14. #define JucePlugin_WantsLV2FixedBlockSize 0
  15. #endif
  16. /** Enable latency port */
  17. #ifndef JucePlugin_WantsLV2Latency
  18. #define JucePlugin_WantsLV2Latency 1
  19. #endif
  20. /** Use non-parameter states */
  21. #ifndef JucePlugin_WantsLV2State
  22. #define JucePlugin_WantsLV2State 1
  23. #endif
  24. /** States are strings, needs custom get/setStateInformationString */
  25. #ifndef JucePlugin_WantsLV2StateString
  26. #define JucePlugin_WantsLV2StateString 0
  27. #endif
  28. /** Export presets */
  29. #ifndef JucePlugin_WantsLV2Presets
  30. #define JucePlugin_WantsLV2Presets 1
  31. #endif
  32. /** Request time position */
  33. #ifndef JucePlugin_WantsLV2TimePos
  34. #define JucePlugin_WantsLV2TimePos 1
  35. #endif
  36. /** Using string states require enabling states first */
  37. #if JucePlugin_WantsLV2StateString && ! JucePlugin_WantsLV2State
  38. #undef JucePlugin_WantsLV2State
  39. #define JucePlugin_WantsLV2State 1
  40. #endif
  41. #if JUCE_LINUX && ! JUCE_AUDIOPROCESSOR_NO_GUI
  42. #include <X11/Xlib.h>
  43. #undef KeyPress
  44. #endif
  45. #include <fstream>
  46. #include <iostream>
  47. // LV2 includes..
  48. #include "includes/lv2.h"
  49. #include "includes/atom.h"
  50. #include "includes/atom-util.h"
  51. #include "includes/buf-size.h"
  52. #include "includes/instance-access.h"
  53. #include "includes/midi.h"
  54. #include "includes/options.h"
  55. #include "includes/port-props.h"
  56. #include "includes/presets.h"
  57. #include "includes/state.h"
  58. #include "includes/time.h"
  59. #include "includes/ui.h"
  60. #include "includes/urid.h"
  61. #include "includes/lv2_external_ui.h"
  62. #include "includes/lv2_programs.h"
  63. #include "../utility/juce_IncludeModuleHeaders.h"
  64. #if JUCE_LINUX && ! JUCE_AUDIOPROCESSOR_NO_GUI
  65. namespace juce
  66. {
  67. extern Display* display;
  68. }
  69. #endif
  70. #define JUCE_LV2_STATE_STRING_URI "urn:juce:stateString"
  71. #define JUCE_LV2_STATE_BINARY_URI "urn:juce:stateBinary"
  72. //==============================================================================
  73. // Various helper functions for creating the ttl files
  74. #if JUCE_MAC
  75. #define PLUGIN_EXT ".dylib"
  76. #elif JUCE_LINUX
  77. #define PLUGIN_EXT ".so"
  78. #elif JUCE_WINDOWS
  79. #define PLUGIN_EXT ".dll"
  80. #endif
  81. /** Returns plugin type, defined in AppConfig.h or JucePluginCharacteristics.h */
  82. const String getPluginType()
  83. {
  84. String pluginType;
  85. #ifdef JucePlugin_LV2Category
  86. pluginType = "lv2:" JucePlugin_LV2Category;
  87. pluginType += ", ";
  88. #elif JucePlugin_IsSynth
  89. pluginType = "lv2:InstrumentPlugin, ";
  90. #endif
  91. pluginType += "lv2:Plugin";
  92. return pluginType;
  93. }
  94. /** Returns plugin URI */
  95. static const String& getPluginURI()
  96. {
  97. // JucePlugin_LV2URI might be defined as a function (eg. allowing dynamic URIs based on filename)
  98. static const String pluginURI(JucePlugin_LV2URI);
  99. return pluginURI;
  100. }
  101. static Array<String> usedSymbols;
  102. /** Converts a parameter name to an LV2 compatible symbol. */
  103. const String nameToSymbol (const String& name, const uint32 portIndex)
  104. {
  105. String symbol, trimmedName = name.trimStart().trimEnd().toLowerCase();
  106. if (trimmedName.isEmpty())
  107. {
  108. symbol += "lv2_port_";
  109. symbol += String(portIndex+1);
  110. }
  111. else
  112. {
  113. for (int i=0; i < trimmedName.length(); ++i)
  114. {
  115. const juce_wchar c = trimmedName[i];
  116. if (i == 0 && std::isdigit(c))
  117. symbol += "_";
  118. else if (std::isalpha(c) || std::isdigit(c))
  119. symbol += c;
  120. else
  121. symbol += "_";
  122. }
  123. }
  124. // Do not allow identical symbols
  125. if (usedSymbols.contains(symbol))
  126. {
  127. int offset = 2;
  128. String offsetStr = "_2";
  129. symbol += offsetStr;
  130. while (usedSymbols.contains(symbol))
  131. {
  132. offset += 1;
  133. String newOffsetStr = "_" + String(offset);
  134. symbol = symbol.replace(offsetStr, newOffsetStr);
  135. offsetStr = newOffsetStr;
  136. }
  137. }
  138. usedSymbols.add(symbol);
  139. return symbol;
  140. }
  141. /** Prevents NaN or out of 0.0<->1.0 bounds parameter values. */
  142. float safeParamValue (float value)
  143. {
  144. if (std::isnan(value))
  145. value = 0.0f;
  146. else if (value < 0.0f)
  147. value = 0.0f;
  148. else if (value > 1.0f)
  149. value = 1.0f;
  150. return value;
  151. }
  152. /** Create the manifest.ttl file contents */
  153. const String makeManifestFile (AudioProcessor* const filter, const String& binary)
  154. {
  155. const String& pluginURI(getPluginURI());
  156. String text;
  157. // Header
  158. text += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n";
  159. text += "@prefix pset: <" LV2_PRESETS_PREFIX "> .\n";
  160. text += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
  161. text += "@prefix ui: <" LV2_UI_PREFIX "> .\n";
  162. text += "\n";
  163. // Plugin
  164. text += "<" + pluginURI + ">\n";
  165. text += " a lv2:Plugin ;\n";
  166. text += " lv2:binary <" + binary + PLUGIN_EXT "> ;\n";
  167. text += " rdfs:seeAlso <" + binary + ".ttl> .\n";
  168. text += "\n";
  169. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  170. // UIs
  171. if (filter->hasEditor())
  172. {
  173. text += "<" + pluginURI + "#ExternalUI>\n";
  174. text += " a <" LV2_EXTERNAL_UI__Widget "> ;\n";
  175. text += " ui:binary <" + binary + PLUGIN_EXT "> ;\n";
  176. text += " lv2:requiredFeature <" LV2_INSTANCE_ACCESS_URI "> ;\n";
  177. text += " lv2:extensionData <" LV2_PROGRAMS__UIInterface "> .\n";
  178. text += "\n";
  179. text += "<" + pluginURI + "#ParentUI>\n";
  180. #if JUCE_MAC
  181. text += " a ui:CocoaUI ;\n";
  182. #elif JUCE_LINUX
  183. text += " a ui:X11UI ;\n";
  184. #elif JUCE_WINDOWS
  185. text += " a ui:WindowsUI ;\n";
  186. #endif
  187. text += " ui:binary <" + binary + PLUGIN_EXT "> ;\n";
  188. text += " lv2:requiredFeature <" LV2_INSTANCE_ACCESS_URI "> ;\n";
  189. text += " lv2:optionalFeature ui:noUserResize ;\n";
  190. text += " lv2:extensionData <" LV2_PROGRAMS__UIInterface "> .\n";
  191. text += "\n";
  192. }
  193. #endif
  194. #if JucePlugin_WantsLV2Presets
  195. const String presetSeparator(pluginURI.contains("#") ? ":" : "#");
  196. // Presets
  197. for (int i = 0; i < filter->getNumPrograms(); ++i)
  198. {
  199. text += "<" + pluginURI + presetSeparator + "preset" + String::formatted("%03i", i+1) + ">\n";
  200. text += " a pset:Preset ;\n";
  201. text += " lv2:appliesTo <" + pluginURI + "> ;\n";
  202. text += " rdfs:label \"" + filter->getProgramName(i) + "\" ;\n";
  203. text += " rdfs:seeAlso <presets.ttl> .\n";
  204. text += "\n";
  205. }
  206. #endif
  207. return text;
  208. }
  209. /** Create the -plugin-.ttl file contents */
  210. const String makePluginFile (AudioProcessor* const filter, const int maxNumInputChannels, const int maxNumOutputChannels)
  211. {
  212. const String& pluginURI(getPluginURI());
  213. String text;
  214. // Header
  215. text += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n";
  216. text += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n";
  217. text += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n";
  218. text += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n";
  219. text += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
  220. text += "@prefix ui: <" LV2_UI_PREFIX "> .\n";
  221. text += "\n";
  222. // Plugin
  223. text += "<" + pluginURI + ">\n";
  224. text += " a " + getPluginType() + " ;\n";
  225. text += " lv2:requiredFeature <" LV2_BUF_SIZE__boundedBlockLength "> ,\n";
  226. #if JucePlugin_WantsLV2FixedBlockSize
  227. text += " <" LV2_BUF_SIZE__fixedBlockLength "> ,\n";
  228. #endif
  229. text += " <" LV2_URID__map "> ;\n";
  230. text += " lv2:extensionData <" LV2_OPTIONS__interface "> ,\n";
  231. #if JucePlugin_WantsLV2State
  232. text += " <" LV2_STATE__interface "> ,\n";
  233. #endif
  234. text += " <" LV2_PROGRAMS__Interface "> ;\n";
  235. text += "\n";
  236. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  237. // UIs
  238. if (filter->hasEditor())
  239. {
  240. text += " ui:ui <" + pluginURI + "#ExternalUI> ,\n";
  241. text += " <" + pluginURI + "#ParentUI> ;\n";
  242. text += "\n";
  243. }
  244. #endif
  245. uint32 portIndex = 0;
  246. #if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
  247. // MIDI input
  248. text += " lv2:port [\n";
  249. text += " a lv2:InputPort, atom:AtomPort ;\n";
  250. text += " atom:bufferType atom:Sequence ;\n";
  251. #if JucePlugin_WantsMidiInput
  252. text += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n";
  253. #endif
  254. #if JucePlugin_WantsLV2TimePos
  255. text += " atom:supports <" LV2_TIME__Position "> ;\n";
  256. #endif
  257. text += " lv2:index " + String(portIndex++) + " ;\n";
  258. text += " lv2:symbol \"lv2_events_in\" ;\n";
  259. text += " lv2:name \"Events Input\" ;\n";
  260. text += " lv2:designation lv2:control ;\n";
  261. #if ! JucePlugin_IsSynth
  262. text += " lv2:portProperty lv2:connectionOptional ;\n";
  263. #endif
  264. text += " ] ;\n";
  265. text += "\n";
  266. #endif
  267. #if JucePlugin_ProducesMidiOutput
  268. // MIDI output
  269. text += " lv2:port [\n";
  270. text += " a lv2:OutputPort, atom:AtomPort ;\n";
  271. text += " atom:bufferType atom:Sequence ;\n";
  272. text += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n";
  273. text += " lv2:index " + String(portIndex++) + " ;\n";
  274. text += " lv2:symbol \"lv2_midi_out\" ;\n";
  275. text += " lv2:name \"MIDI Output\" ;\n";
  276. text += " ] ;\n";
  277. text += "\n";
  278. #endif
  279. // Freewheel port
  280. text += " lv2:port [\n";
  281. text += " a lv2:InputPort, lv2:ControlPort ;\n";
  282. text += " lv2:index " + String(portIndex++) + " ;\n";
  283. text += " lv2:symbol \"lv2_freewheel\" ;\n";
  284. text += " lv2:name \"Freewheel\" ;\n";
  285. text += " lv2:default 0.0 ;\n";
  286. text += " lv2:minimum 0.0 ;\n";
  287. text += " lv2:maximum 1.0 ;\n";
  288. text += " lv2:designation <" LV2_CORE__freeWheeling "> ;\n";
  289. text += " lv2:portProperty lv2:toggled, <" LV2_PORT_PROPS__notOnGUI "> ;\n";
  290. text += " ] ;\n";
  291. text += "\n";
  292. #if JucePlugin_WantsLV2Latency
  293. // Latency port
  294. text += " lv2:port [\n";
  295. text += " a lv2:OutputPort, lv2:ControlPort ;\n";
  296. text += " lv2:index " + String(portIndex++) + " ;\n";
  297. text += " lv2:symbol \"lv2_latency\" ;\n";
  298. text += " lv2:name \"Latency\" ;\n";
  299. text += " lv2:designation <" LV2_CORE__latency "> ;\n";
  300. text += " lv2:portProperty lv2:reportsLatency, lv2:integer ;\n";
  301. text += " ] ;\n";
  302. text += "\n";
  303. #endif
  304. // Audio inputs
  305. for (int i=0; i < maxNumInputChannels; ++i)
  306. {
  307. if (i == 0)
  308. text += " lv2:port [\n";
  309. else
  310. text += " [\n";
  311. text += " a lv2:InputPort, lv2:AudioPort ;\n";
  312. text += " lv2:index " + String(portIndex++) + " ;\n";
  313. text += " lv2:symbol \"lv2_audio_in_" + String(i+1) + "\" ;\n";
  314. text += " lv2:name \"Audio Input " + String(i+1) + "\" ;\n";
  315. if (i+1 == maxNumInputChannels)
  316. text += " ] ;\n\n";
  317. else
  318. text += " ] ,\n";
  319. }
  320. // Audio outputs
  321. for (int i=0; i < maxNumOutputChannels; ++i)
  322. {
  323. if (i == 0)
  324. text += " lv2:port [\n";
  325. else
  326. text += " [\n";
  327. text += " a lv2:OutputPort, lv2:AudioPort ;\n";
  328. text += " lv2:index " + String(portIndex++) + " ;\n";
  329. text += " lv2:symbol \"lv2_audio_out_" + String(i+1) + "\" ;\n";
  330. text += " lv2:name \"Audio Output " + String(i+1) + "\" ;\n";
  331. if (i+1 == maxNumOutputChannels)
  332. text += " ] ;\n\n";
  333. else
  334. text += " ] ,\n";
  335. }
  336. // Parameters
  337. for (int i=0; i < filter->getNumParameters(); ++i)
  338. {
  339. if (i == 0)
  340. text += " lv2:port [\n";
  341. else
  342. text += " [\n";
  343. text += " a lv2:InputPort, lv2:ControlPort ;\n";
  344. text += " lv2:index " + String(portIndex++) + " ;\n";
  345. text += " lv2:symbol \"" + nameToSymbol(filter->getParameterName(i), i) + "\" ;\n";
  346. if (filter->getParameterName(i).isNotEmpty())
  347. text += " lv2:name \"" + filter->getParameterName(i) + "\" ;\n";
  348. else
  349. text += " lv2:name \"Port " + String(i+1) + "\" ;\n";
  350. text += " lv2:default " + String::formatted("%f", safeParamValue(filter->getParameter(i))) + " ;\n";
  351. text += " lv2:minimum 0.0 ;\n";
  352. text += " lv2:maximum 1.0 ;\n";
  353. if (! filter->isParameterAutomatable(i))
  354. text += " lv2:portProperty <" LV2_PORT_PROPS__expensive "> ;\n";
  355. if (i+1 == filter->getNumParameters())
  356. text += " ] ;\n\n";
  357. else
  358. text += " ] ,\n";
  359. }
  360. text += " doap:name \"" + filter->getName() + "\" ;\n";
  361. text += " doap:maintainer [ foaf:name \"" JucePlugin_Manufacturer "\" ] .\n";
  362. return text;
  363. }
  364. /** Create the presets.ttl file contents */
  365. const String makePresetsFile (AudioProcessor* const filter)
  366. {
  367. const String& pluginURI(getPluginURI());
  368. String text;
  369. // Header
  370. text += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n";
  371. text += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n";
  372. text += "@prefix pset: <" LV2_PRESETS_PREFIX "> .\n";
  373. text += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n";
  374. text += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
  375. text += "@prefix state: <" LV2_STATE_PREFIX "> .\n";
  376. text += "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n";
  377. text += "\n";
  378. // Presets
  379. const int numPrograms = filter->getNumPrograms();
  380. const String presetSeparator(pluginURI.contains("#") ? ":" : "#");
  381. for (int i = 0; i < numPrograms; ++i)
  382. {
  383. std::cout << "\nSaving preset " << i+1 << "/" << numPrograms+1 << "...";
  384. std::cout.flush();
  385. String preset;
  386. // Label
  387. filter->setCurrentProgram(i);
  388. preset += "<" + pluginURI + presetSeparator + "preset" + String::formatted("%03i", i+1) + "> a pset:Preset ;\n";
  389. // State
  390. #if JucePlugin_WantsLV2State
  391. preset += " state:state [\n";
  392. #if JucePlugin_WantsLV2StateString
  393. preset += " <" JUCE_LV2_STATE_STRING_URI ">\n";
  394. preset += "\"\"\"\n";
  395. preset += filter->getStateInformationString().replace("\r\n","\n");
  396. preset += "\"\"\"\n";
  397. #else
  398. MemoryBlock chunkMemory;
  399. filter->getCurrentProgramStateInformation(chunkMemory);
  400. const String chunkString(Base64::toBase64(chunkMemory.getData(), chunkMemory.getSize()));
  401. preset += " <" JUCE_LV2_STATE_BINARY_URI "> [\n";
  402. preset += " a atom:Chunk ;\n";
  403. preset += " rdf:value \"" + chunkString + "\"^^xsd:base64Binary ;\n";
  404. preset += " ] ;\n";
  405. #endif
  406. if (filter->getNumParameters() == 0)
  407. {
  408. preset += " ] .\n\n";
  409. continue;
  410. }
  411. preset += " ] ;\n\n";
  412. #endif
  413. // Port values
  414. usedSymbols.clear();
  415. for (int j=0; j < filter->getNumParameters(); ++j)
  416. {
  417. if (j == 0)
  418. preset += " lv2:port [\n";
  419. else
  420. preset += " [\n";
  421. preset += " lv2:symbol \"" + nameToSymbol(filter->getParameterName(j), j) + "\" ;\n";
  422. preset += " pset:value " + String::formatted("%f", safeParamValue(filter->getParameter(j))) + " ;\n";
  423. if (j+1 == filter->getNumParameters())
  424. preset += " ] ";
  425. else
  426. preset += " ] ,\n";
  427. }
  428. preset += ".\n\n";
  429. text += preset;
  430. }
  431. return text;
  432. }
  433. /** Creates manifest.ttl, plugin.ttl and presets.ttl files */
  434. void createLv2Files(const char* basename)
  435. {
  436. const ScopedJuceInitialiser_GUI juceInitialiser;
  437. ScopedPointer<AudioProcessor> filter (createPluginFilterOfType (AudioProcessor::wrapperType_LV2));
  438. String binary(basename);
  439. String binaryTTL(binary + ".ttl");
  440. std::cout << "Writing manifest.ttl..."; std::cout.flush();
  441. std::fstream manifest("manifest.ttl", std::ios::out);
  442. manifest << makeManifestFile(filter, binary) << std::endl;
  443. manifest.close();
  444. std::cout << " done!" << std::endl;
  445. std::cout << "Writing " << binary << ".ttl..."; std::cout.flush();
  446. std::fstream plugin(binaryTTL.toUTF8(), std::ios::out);
  447. plugin << makePluginFile(filter, JucePlugin_MaxNumInputChannels, JucePlugin_MaxNumOutputChannels) << std::endl;
  448. plugin.close();
  449. std::cout << " done!" << std::endl;
  450. #if JucePlugin_WantsLV2Presets
  451. std::cout << "Writing presets.ttl..."; std::cout.flush();
  452. std::fstream presets("presets.ttl", std::ios::out);
  453. presets << makePresetsFile(filter) << std::endl;
  454. presets.close();
  455. std::cout << " done!" << std::endl;
  456. #endif
  457. }
  458. //==============================================================================
  459. #if JUCE_LINUX
  460. class SharedMessageThread : public Thread
  461. {
  462. public:
  463. SharedMessageThread()
  464. : Thread ("Lv2MessageThread"),
  465. initialised (false)
  466. {
  467. startThread (7);
  468. while (! initialised)
  469. sleep (1);
  470. }
  471. ~SharedMessageThread()
  472. {
  473. MessageManager::getInstance()->stopDispatchLoop();
  474. waitForThreadToExit (5000);
  475. }
  476. void run() override
  477. {
  478. const ScopedJuceInitialiser_GUI juceInitialiser;
  479. MessageManager::getInstance()->setCurrentThreadAsMessageThread();
  480. initialised = true;
  481. MessageManager::getInstance()->runDispatchLoop();
  482. }
  483. private:
  484. volatile bool initialised;
  485. };
  486. #endif
  487. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  488. //==============================================================================
  489. /**
  490. Lightweight DocumentWindow subclass for external ui
  491. */
  492. class JuceLv2ExternalUIWindow : public DocumentWindow
  493. {
  494. public:
  495. /** Creates a Document Window wrapper */
  496. JuceLv2ExternalUIWindow (AudioProcessorEditor* editor, const String& title) :
  497. DocumentWindow (title, Colours::white, DocumentWindow::minimiseButton | DocumentWindow::closeButton, false),
  498. closed (false),
  499. lastPos (0, 0)
  500. {
  501. setOpaque (true);
  502. setContentNonOwned (editor, true);
  503. setSize (editor->getWidth(), editor->getHeight());
  504. setUsingNativeTitleBar (true);
  505. }
  506. /** Close button handler */
  507. void closeButtonPressed()
  508. {
  509. saveLastPos();
  510. removeFromDesktop();
  511. closed = true;
  512. }
  513. void saveLastPos()
  514. {
  515. lastPos = getScreenPosition();
  516. }
  517. void restoreLastPos()
  518. {
  519. setTopLeftPosition (lastPos.getX(), lastPos.getY());
  520. }
  521. Point<int> getLastPos()
  522. {
  523. return lastPos;
  524. }
  525. bool isClosed()
  526. {
  527. return closed;
  528. }
  529. void reset()
  530. {
  531. closed = false;
  532. }
  533. private:
  534. bool closed;
  535. Point<int> lastPos;
  536. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ExternalUIWindow);
  537. };
  538. //==============================================================================
  539. /**
  540. Juce LV2 External UI handle
  541. */
  542. class JuceLv2ExternalUIWrapper : public LV2_External_UI_Widget
  543. {
  544. public:
  545. JuceLv2ExternalUIWrapper (AudioProcessorEditor* editor, const String& title)
  546. : window (editor, title)
  547. {
  548. // external UI calls
  549. run = doRun;
  550. show = doShow;
  551. hide = doHide;
  552. }
  553. ~JuceLv2ExternalUIWrapper()
  554. {
  555. if (window.isOnDesktop())
  556. window.removeFromDesktop();
  557. }
  558. void close()
  559. {
  560. window.closeButtonPressed();
  561. }
  562. bool isClosed()
  563. {
  564. return window.isClosed();
  565. }
  566. void reset(const String& title)
  567. {
  568. window.reset();
  569. window.setName(title);
  570. }
  571. void repaint()
  572. {
  573. window.repaint();
  574. }
  575. Point<int> getScreenPosition()
  576. {
  577. if (window.isClosed())
  578. return window.getLastPos();
  579. else
  580. return window.getScreenPosition();
  581. }
  582. void setScreenPos (int x, int y)
  583. {
  584. if (! window.isClosed())
  585. window.setTopLeftPosition(x, y);
  586. }
  587. //==============================================================================
  588. static void doRun (LV2_External_UI_Widget* _this_)
  589. {
  590. const MessageManagerLock mmLock;
  591. JuceLv2ExternalUIWrapper* self = (JuceLv2ExternalUIWrapper*) _this_;
  592. if (! self->isClosed())
  593. self->window.repaint();
  594. }
  595. static void doShow (LV2_External_UI_Widget* _this_)
  596. {
  597. const MessageManagerLock mmLock;
  598. JuceLv2ExternalUIWrapper* self = (JuceLv2ExternalUIWrapper*) _this_;
  599. if (! self->isClosed())
  600. {
  601. if (! self->window.isOnDesktop())
  602. self->window.addToDesktop();
  603. self->window.restoreLastPos();
  604. self->window.setVisible(true);
  605. }
  606. }
  607. static void doHide (LV2_External_UI_Widget* _this_)
  608. {
  609. const MessageManagerLock mmLock;
  610. JuceLv2ExternalUIWrapper* self = (JuceLv2ExternalUIWrapper*) _this_;
  611. if (! self->isClosed())
  612. {
  613. self->window.saveLastPos();
  614. self->window.setVisible(false);
  615. }
  616. }
  617. private:
  618. JuceLv2ExternalUIWindow window;
  619. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ExternalUIWrapper);
  620. };
  621. //==============================================================================
  622. /**
  623. Juce LV2 Parent UI container, listens for resize events and passes them to ui-resize
  624. */
  625. class JuceLv2ParentContainer : public Component
  626. {
  627. public:
  628. JuceLv2ParentContainer (AudioProcessorEditor* editor, const LV2UI_Resize* uiResize_)
  629. : uiResize(uiResize_)
  630. {
  631. setOpaque (true);
  632. editor->setOpaque (true);
  633. setBounds (editor->getBounds());
  634. editor->setTopLeftPosition (0, 0);
  635. addAndMakeVisible (editor);
  636. }
  637. ~JuceLv2ParentContainer()
  638. {
  639. }
  640. void paint (Graphics&) {}
  641. void paintOverChildren (Graphics&) {}
  642. void childBoundsChanged (Component* child)
  643. {
  644. const int cw = child->getWidth();
  645. const int ch = child->getHeight();
  646. #if JUCE_LINUX
  647. XResizeWindow (display, (Window) getWindowHandle(), cw, ch);
  648. #else
  649. setSize (cw, ch);
  650. #endif
  651. if (uiResize != nullptr)
  652. uiResize->ui_resize (uiResize->handle, cw, ch);
  653. }
  654. void reset (const LV2UI_Resize* uiResize_)
  655. {
  656. uiResize = uiResize_;
  657. if (uiResize != nullptr)
  658. uiResize->ui_resize (uiResize->handle, getWidth(), getHeight());
  659. }
  660. private:
  661. //==============================================================================
  662. const LV2UI_Resize* uiResize;
  663. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ParentContainer);
  664. };
  665. //==============================================================================
  666. /**
  667. Juce LV2 UI handle
  668. */
  669. class JuceLv2UIWrapper : public AudioProcessorListener,
  670. public Timer
  671. {
  672. public:
  673. JuceLv2UIWrapper (AudioProcessor* filter_, LV2UI_Write_Function writeFunction_, LV2UI_Controller controller_,
  674. LV2UI_Widget* widget, const LV2_Feature* const* features, bool isExternal_)
  675. : filter (filter_),
  676. writeFunction (writeFunction_),
  677. controller (controller_),
  678. isExternal (isExternal_),
  679. controlPortOffset (0),
  680. lastProgramCount (0),
  681. uiTouch (nullptr),
  682. programsHost (nullptr),
  683. externalUIHost (nullptr),
  684. lastExternalUIPos (-1, -1),
  685. uiResize (nullptr)
  686. {
  687. jassert (filter != nullptr);
  688. filter->addListener(this);
  689. if (filter->hasEditor())
  690. {
  691. editor = filter->createEditorIfNeeded();
  692. if (editor == nullptr)
  693. {
  694. *widget = nullptr;
  695. return;
  696. }
  697. }
  698. for (int i = 0; features[i] != nullptr; ++i)
  699. {
  700. if (strcmp(features[i]->URI, LV2_UI__touch) == 0)
  701. uiTouch = (const LV2UI_Touch*)features[i]->data;
  702. else if (strcmp(features[i]->URI, LV2_PROGRAMS__Host) == 0)
  703. programsHost = (const LV2_Programs_Host*)features[i]->data;
  704. }
  705. if (isExternal)
  706. {
  707. resetExternalUI (features);
  708. if (externalUIHost != nullptr)
  709. {
  710. String title (filter->getName());
  711. if (externalUIHost->plugin_human_id != nullptr)
  712. title = externalUIHost->plugin_human_id;
  713. externalUI = new JuceLv2ExternalUIWrapper (editor, title);
  714. *widget = externalUI;
  715. startTimer (100);
  716. }
  717. else
  718. {
  719. *widget = nullptr;
  720. }
  721. }
  722. else
  723. {
  724. resetParentUI (features);
  725. if (parentContainer != nullptr)
  726. *widget = parentContainer->getWindowHandle();
  727. else
  728. *widget = nullptr;
  729. }
  730. #if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
  731. controlPortOffset += 1;
  732. #endif
  733. #if JucePlugin_ProducesMidiOutput
  734. controlPortOffset += 1;
  735. #endif
  736. controlPortOffset += 1; // freewheel
  737. #if JucePlugin_WantsLV2Latency
  738. controlPortOffset += 1;
  739. #endif
  740. controlPortOffset += JucePlugin_MaxNumInputChannels;
  741. controlPortOffset += JucePlugin_MaxNumOutputChannels;
  742. lastProgramCount = filter->getNumPrograms();
  743. }
  744. ~JuceLv2UIWrapper()
  745. {
  746. PopupMenu::dismissAllActiveMenus();
  747. filter->removeListener(this);
  748. parentContainer = nullptr;
  749. externalUI = nullptr;
  750. externalUIHost = nullptr;
  751. if (editor != nullptr)
  752. {
  753. filter->editorBeingDeleted (editor);
  754. editor = nullptr;
  755. }
  756. }
  757. //==============================================================================
  758. // LV2 core calls
  759. void lv2Cleanup()
  760. {
  761. const MessageManagerLock mmLock;
  762. if (isExternal)
  763. {
  764. if (isTimerRunning())
  765. stopTimer();
  766. externalUIHost = nullptr;
  767. if (externalUI != nullptr)
  768. {
  769. lastExternalUIPos = externalUI->getScreenPosition();
  770. externalUI->close();
  771. }
  772. }
  773. else
  774. {
  775. if (parentContainer)
  776. {
  777. parentContainer->setVisible (false);
  778. if (parentContainer->isOnDesktop())
  779. parentContainer->removeFromDesktop();
  780. }
  781. }
  782. }
  783. //==============================================================================
  784. // Juce calls
  785. void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue)
  786. {
  787. if (writeFunction != nullptr && controller != nullptr)
  788. writeFunction (controller, index + controlPortOffset, sizeof (float), 0, &newValue);
  789. }
  790. void audioProcessorChanged (AudioProcessor*)
  791. {
  792. if (filter != nullptr && programsHost != nullptr)
  793. {
  794. if (filter->getNumPrograms() != lastProgramCount)
  795. {
  796. programsHost->program_changed (programsHost->handle, -1);
  797. lastProgramCount = filter->getNumPrograms();
  798. }
  799. else
  800. programsHost->program_changed (programsHost->handle, filter->getCurrentProgram());
  801. }
  802. }
  803. void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int parameterIndex)
  804. {
  805. if (uiTouch != nullptr)
  806. uiTouch->touch (uiTouch->handle, parameterIndex + controlPortOffset, true);
  807. }
  808. void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int parameterIndex)
  809. {
  810. if (uiTouch != nullptr)
  811. uiTouch->touch (uiTouch->handle, parameterIndex + controlPortOffset, false);
  812. }
  813. void timerCallback()
  814. {
  815. if (externalUI != nullptr && externalUI->isClosed())
  816. {
  817. if (externalUIHost != nullptr)
  818. externalUIHost->ui_closed (controller);
  819. if (isTimerRunning())
  820. stopTimer();
  821. }
  822. }
  823. //==============================================================================
  824. void resetIfNeeded (LV2UI_Write_Function writeFunction_, LV2UI_Controller controller_, LV2UI_Widget* widget,
  825. const LV2_Feature* const* features)
  826. {
  827. writeFunction = writeFunction_;
  828. controller = controller_;
  829. uiTouch = nullptr;
  830. programsHost = nullptr;
  831. for (int i = 0; features[i] != nullptr; ++i)
  832. {
  833. if (strcmp(features[i]->URI, LV2_UI__touch) == 0)
  834. uiTouch = (const LV2UI_Touch*)features[i]->data;
  835. else if (strcmp(features[i]->URI, LV2_PROGRAMS__Host) == 0)
  836. programsHost = (const LV2_Programs_Host*)features[i]->data;
  837. }
  838. if (isExternal)
  839. {
  840. resetExternalUI (features);
  841. *widget = externalUI;
  842. }
  843. else
  844. {
  845. resetParentUI (features);
  846. *widget = parentContainer->getWindowHandle();
  847. }
  848. }
  849. void repaint()
  850. {
  851. const MessageManagerLock mmLock;
  852. if (editor != nullptr)
  853. editor->repaint();
  854. if (parentContainer != nullptr)
  855. parentContainer->repaint();
  856. if (externalUI != nullptr)
  857. externalUI->repaint();
  858. }
  859. private:
  860. AudioProcessor* const filter;
  861. ScopedPointer<AudioProcessorEditor> editor;
  862. LV2UI_Write_Function writeFunction;
  863. LV2UI_Controller controller;
  864. const bool isExternal;
  865. uint32 controlPortOffset;
  866. int lastProgramCount;
  867. const LV2UI_Touch* uiTouch;
  868. const LV2_Programs_Host* programsHost;
  869. ScopedPointer<JuceLv2ExternalUIWrapper> externalUI;
  870. const LV2_External_UI_Host* externalUIHost;
  871. Point<int> lastExternalUIPos;
  872. ScopedPointer<JuceLv2ParentContainer> parentContainer;
  873. const LV2UI_Resize* uiResize;
  874. //==============================================================================
  875. void resetExternalUI (const LV2_Feature* const* features)
  876. {
  877. externalUIHost = nullptr;
  878. for (int i = 0; features[i] != nullptr; ++i)
  879. {
  880. if (strcmp(features[i]->URI, LV2_EXTERNAL_UI__Host) == 0)
  881. {
  882. externalUIHost = (const LV2_External_UI_Host*)features[i]->data;
  883. break;
  884. }
  885. }
  886. if (externalUI != nullptr)
  887. {
  888. String title(filter->getName());
  889. if (externalUIHost->plugin_human_id != nullptr)
  890. title = externalUIHost->plugin_human_id;
  891. if (lastExternalUIPos.getX() != -1 && lastExternalUIPos.getY() != -1)
  892. externalUI->setScreenPos(lastExternalUIPos.getX(), lastExternalUIPos.getY());
  893. externalUI->reset(title);
  894. startTimer (100);
  895. }
  896. }
  897. void resetParentUI (const LV2_Feature* const* features)
  898. {
  899. void* parent = nullptr;
  900. uiResize = nullptr;
  901. for (int i = 0; features[i] != nullptr; ++i)
  902. {
  903. if (strcmp(features[i]->URI, LV2_UI__parent) == 0)
  904. parent = features[i]->data;
  905. else if (strcmp(features[i]->URI, LV2_UI__resize) == 0)
  906. uiResize = (const LV2UI_Resize*)features[i]->data;
  907. }
  908. if (parent != nullptr)
  909. {
  910. if (parentContainer == nullptr)
  911. parentContainer = new JuceLv2ParentContainer (editor, uiResize);
  912. parentContainer->setVisible (false);
  913. if (parentContainer->isOnDesktop())
  914. parentContainer->removeFromDesktop();
  915. parentContainer->addToDesktop (0, parent);
  916. #if JUCE_LINUX
  917. Window hostWindow = (Window) parent;
  918. Window editorWnd = (Window) parentContainer->getWindowHandle();
  919. XReparentWindow (display, editorWnd, hostWindow, 0, 0);
  920. #endif
  921. parentContainer->reset (uiResize);
  922. parentContainer->setVisible (true);
  923. }
  924. }
  925. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2UIWrapper)
  926. };
  927. #endif /* JUCE_AUDIOPROCESSOR_NO_GUI */
  928. //==============================================================================
  929. /**
  930. Juce LV2 handle
  931. */
  932. class JuceLv2Wrapper : public AudioPlayHead
  933. {
  934. public:
  935. //==============================================================================
  936. JuceLv2Wrapper (double sampleRate_, const LV2_Feature* const* features)
  937. : numInChans (JucePlugin_MaxNumInputChannels),
  938. numOutChans (JucePlugin_MaxNumOutputChannels),
  939. bufferSize (2048),
  940. sampleRate (sampleRate_),
  941. uridMap (nullptr),
  942. uridAtomBlank (0),
  943. uridAtomObject (0),
  944. uridAtomDouble (0),
  945. uridAtomFloat (0),
  946. uridAtomInt (0),
  947. uridAtomLong (0),
  948. uridAtomSequence (0),
  949. uridMidiEvent (0),
  950. uridTimePos (0),
  951. uridTimeBar (0),
  952. uridTimeBarBeat (0),
  953. uridTimeBeatsPerBar (0),
  954. uridTimeBeatsPerMinute (0),
  955. uridTimeBeatUnit (0),
  956. uridTimeFrame (0),
  957. uridTimeSpeed (0),
  958. usingNominalBlockLength (false)
  959. {
  960. {
  961. const MessageManagerLock mmLock;
  962. filter = createPluginFilterOfType (AudioProcessor::wrapperType_LV2);
  963. }
  964. jassert (filter != nullptr);
  965. filter->setPlayConfigDetails (numInChans, numOutChans, 0, 0);
  966. filter->setPlayHead (this);
  967. #if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
  968. portEventsIn = nullptr;
  969. #endif
  970. #if JucePlugin_ProducesMidiOutput
  971. portMidiOut = nullptr;
  972. #endif
  973. portFreewheel = nullptr;
  974. #if JucePlugin_WantsLV2Latency
  975. portLatency = nullptr;
  976. #endif
  977. for (int i=0; i < numInChans; ++i)
  978. portAudioIns[i] = nullptr;
  979. for (int i=0; i < numOutChans; ++i)
  980. portAudioOuts[i] = nullptr;
  981. portControls.insertMultiple (0, nullptr, filter->getNumParameters());
  982. for (int i=0; i < filter->getNumParameters(); ++i)
  983. lastControlValues.add (filter->getParameter(i));
  984. curPosInfo.resetToDefault();
  985. // we need URID_Map first
  986. for (int i=0; features[i] != nullptr; ++i)
  987. {
  988. if (strcmp(features[i]->URI, LV2_URID__map) == 0)
  989. {
  990. uridMap = (const LV2_URID_Map*)features[i]->data;
  991. break;
  992. }
  993. }
  994. // we require uridMap to work properly (it's set as required feature)
  995. jassert (uridMap != nullptr);
  996. if (uridMap != nullptr)
  997. {
  998. uridAtomBlank = uridMap->map(uridMap->handle, LV2_ATOM__Blank);
  999. uridAtomObject = uridMap->map(uridMap->handle, LV2_ATOM__Object);
  1000. uridAtomDouble = uridMap->map(uridMap->handle, LV2_ATOM__Double);
  1001. uridAtomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float);
  1002. uridAtomInt = uridMap->map(uridMap->handle, LV2_ATOM__Int);
  1003. uridAtomLong = uridMap->map(uridMap->handle, LV2_ATOM__Long);
  1004. uridAtomSequence = uridMap->map(uridMap->handle, LV2_ATOM__Sequence);
  1005. uridMidiEvent = uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent);
  1006. uridTimePos = uridMap->map(uridMap->handle, LV2_TIME__Position);
  1007. uridTimeBar = uridMap->map(uridMap->handle, LV2_TIME__bar);
  1008. uridTimeBarBeat = uridMap->map(uridMap->handle, LV2_TIME__barBeat);
  1009. uridTimeBeatsPerBar = uridMap->map(uridMap->handle, LV2_TIME__beatsPerBar);
  1010. uridTimeBeatsPerMinute = uridMap->map(uridMap->handle, LV2_TIME__beatsPerMinute);
  1011. uridTimeBeatUnit = uridMap->map(uridMap->handle, LV2_TIME__beatUnit);
  1012. uridTimeFrame = uridMap->map(uridMap->handle, LV2_TIME__frame);
  1013. uridTimeSpeed = uridMap->map(uridMap->handle, LV2_TIME__speed);
  1014. for (int i=0; features[i] != nullptr; ++i)
  1015. {
  1016. if (strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
  1017. {
  1018. const LV2_Options_Option* options = (const LV2_Options_Option*)features[i]->data;
  1019. for (int j=0; options[j].key != 0; ++j)
  1020. {
  1021. if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__nominalBlockLength))
  1022. {
  1023. if (options[j].type == uridAtomInt)
  1024. {
  1025. bufferSize = *(int*)options[j].value;
  1026. usingNominalBlockLength = true;
  1027. }
  1028. else
  1029. {
  1030. std::cerr << "Host provides nominalBlockLength but has wrong value type" << std::endl;
  1031. }
  1032. break;
  1033. }
  1034. if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength))
  1035. {
  1036. if (options[j].type == uridAtomInt)
  1037. bufferSize = *(int*)options[j].value;
  1038. else
  1039. std::cerr << "Host provides maxBlockLength but has wrong value type" << std::endl;
  1040. // no break, continue in case host supports nominalBlockLength
  1041. }
  1042. }
  1043. break;
  1044. }
  1045. }
  1046. }
  1047. progDesc.bank = 0;
  1048. progDesc.program = 0;
  1049. progDesc.name = nullptr;
  1050. }
  1051. ~JuceLv2Wrapper ()
  1052. {
  1053. const MessageManagerLock mmLock;
  1054. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  1055. ui = nullptr;
  1056. #endif
  1057. filter = nullptr;
  1058. if (progDesc.name != nullptr)
  1059. free((void*)progDesc.name);
  1060. portControls.clear();
  1061. lastControlValues.clear();
  1062. }
  1063. //==============================================================================
  1064. // LV2 core calls
  1065. void lv2ConnectPort (uint32 portId, void* dataLocation)
  1066. {
  1067. uint32 index = 0;
  1068. #if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
  1069. if (portId == index++)
  1070. {
  1071. portEventsIn = (LV2_Atom_Sequence*)dataLocation;
  1072. return;
  1073. }
  1074. #endif
  1075. #if JucePlugin_ProducesMidiOutput
  1076. if (portId == index++)
  1077. {
  1078. portMidiOut = (LV2_Atom_Sequence*)dataLocation;
  1079. return;
  1080. }
  1081. #endif
  1082. if (portId == index++)
  1083. {
  1084. portFreewheel = (float*)dataLocation;
  1085. return;
  1086. }
  1087. #if JucePlugin_WantsLV2Latency
  1088. if (portId == index++)
  1089. {
  1090. portLatency = (float*)dataLocation;
  1091. return;
  1092. }
  1093. #endif
  1094. for (int i=0; i < numInChans; ++i)
  1095. {
  1096. if (portId == index++)
  1097. {
  1098. portAudioIns[i] = (float*)dataLocation;
  1099. return;
  1100. }
  1101. }
  1102. for (int i=0; i < numOutChans; ++i)
  1103. {
  1104. if (portId == index++)
  1105. {
  1106. portAudioOuts[i] = (float*)dataLocation;
  1107. return;
  1108. }
  1109. }
  1110. for (int i=0; i < filter->getNumParameters(); ++i)
  1111. {
  1112. if (portId == index++)
  1113. {
  1114. portControls.set(i, (float*)dataLocation);
  1115. return;
  1116. }
  1117. }
  1118. }
  1119. void lv2Activate()
  1120. {
  1121. jassert (filter != nullptr);
  1122. filter->prepareToPlay (sampleRate, bufferSize);
  1123. filter->setPlayConfigDetails (numInChans, numOutChans, sampleRate, bufferSize);
  1124. channels.calloc (numInChans + numOutChans);
  1125. #if (JucePlugin_WantsMidiInput || JucePlugin_ProducesMidiOutput)
  1126. midiEvents.ensureSize (2048);
  1127. midiEvents.clear();
  1128. #endif
  1129. }
  1130. void lv2Deactivate()
  1131. {
  1132. jassert (filter != nullptr);
  1133. filter->releaseResources();
  1134. channels.free();
  1135. }
  1136. void lv2Run (uint32 sampleCount)
  1137. {
  1138. jassert (filter != nullptr);
  1139. #if JucePlugin_WantsLV2Latency
  1140. if (portLatency != nullptr)
  1141. *portLatency = filter->getLatencySamples();
  1142. #endif
  1143. if (portFreewheel != nullptr)
  1144. filter->setNonRealtime (*portFreewheel >= 0.5f);
  1145. if (sampleCount == 0)
  1146. {
  1147. /**
  1148. LV2 pre-roll
  1149. Hosts might use this to force plugins to update its output control ports.
  1150. (plugins can only access port locations during run) */
  1151. return;
  1152. }
  1153. // Check for updated parameters
  1154. {
  1155. float curValue;
  1156. for (int i = 0; i < portControls.size(); ++i)
  1157. {
  1158. if (portControls[i] != nullptr)
  1159. {
  1160. curValue = *portControls[i];
  1161. if (lastControlValues[i] != curValue)
  1162. {
  1163. filter->setParameter (i, curValue);
  1164. lastControlValues.setUnchecked (i, curValue);
  1165. }
  1166. }
  1167. }
  1168. }
  1169. {
  1170. const ScopedLock sl (filter->getCallbackLock());
  1171. if (filter->isSuspended() && false)
  1172. {
  1173. for (int i = 0; i < numOutChans; ++i)
  1174. zeromem (portAudioOuts[i], sizeof (float) * sampleCount);
  1175. }
  1176. else
  1177. {
  1178. int i;
  1179. for (i = 0; i < numOutChans; ++i)
  1180. {
  1181. channels[i] = portAudioOuts[i];
  1182. if (i < numInChans && portAudioIns[i] != portAudioOuts[i])
  1183. FloatVectorOperations::copy (portAudioOuts [i], portAudioIns[i], sampleCount);
  1184. }
  1185. for (; i < numInChans; ++i)
  1186. channels [i] = portAudioIns[i];
  1187. #if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
  1188. if (portEventsIn != nullptr)
  1189. {
  1190. midiEvents.clear();
  1191. LV2_ATOM_SEQUENCE_FOREACH(portEventsIn, iter)
  1192. {
  1193. const LV2_Atom_Event* event = (const LV2_Atom_Event*)iter;
  1194. if (event == nullptr)
  1195. continue;
  1196. if (event->time.frames >= sampleCount)
  1197. break;
  1198. #if JucePlugin_WantsMidiInput
  1199. if (event->body.type == uridMidiEvent)
  1200. {
  1201. const uint8* data = (const uint8*)(event + 1);
  1202. midiEvents.addEvent(data, event->body.size, event->time.frames);
  1203. continue;
  1204. }
  1205. #endif
  1206. #if JucePlugin_WantsLV2TimePos
  1207. if (event->body.type == uridAtomBlank || event->body.type == uridAtomObject)
  1208. {
  1209. const LV2_Atom_Object* obj = (LV2_Atom_Object*)&event->body;
  1210. if (obj->body.otype != uridTimePos)
  1211. continue;
  1212. LV2_Atom* bar = nullptr;
  1213. LV2_Atom* barBeat = nullptr;
  1214. LV2_Atom* beatUnit = nullptr;
  1215. LV2_Atom* beatsPerBar = nullptr;
  1216. LV2_Atom* beatsPerMinute = nullptr;
  1217. LV2_Atom* frame = nullptr;
  1218. LV2_Atom* speed = nullptr;
  1219. lv2_atom_object_get (obj,
  1220. uridTimeBar, &bar,
  1221. uridTimeBarBeat, &barBeat,
  1222. uridTimeBeatUnit, &beatUnit,
  1223. uridTimeBeatsPerBar, &beatsPerBar,
  1224. uridTimeBeatsPerMinute, &beatsPerMinute,
  1225. uridTimeFrame, &frame,
  1226. uridTimeSpeed, &speed,
  1227. nullptr);
  1228. // need to handle this first as other values depend on it
  1229. if (speed != nullptr)
  1230. {
  1231. /**/ if (speed->type == uridAtomDouble)
  1232. lastPositionData.speed = ((LV2_Atom_Double*)speed)->body;
  1233. else if (speed->type == uridAtomFloat)
  1234. lastPositionData.speed = ((LV2_Atom_Float*)speed)->body;
  1235. else if (speed->type == uridAtomInt)
  1236. lastPositionData.speed = ((LV2_Atom_Int*)speed)->body;
  1237. else if (speed->type == uridAtomLong)
  1238. lastPositionData.speed = ((LV2_Atom_Long*)speed)->body;
  1239. curPosInfo.isPlaying = lastPositionData.speed != 0.0;
  1240. }
  1241. if (bar != nullptr)
  1242. {
  1243. /**/ if (bar->type == uridAtomDouble)
  1244. lastPositionData.bar = ((LV2_Atom_Double*)bar)->body;
  1245. else if (bar->type == uridAtomFloat)
  1246. lastPositionData.bar = ((LV2_Atom_Float*)bar)->body;
  1247. else if (bar->type == uridAtomInt)
  1248. lastPositionData.bar = ((LV2_Atom_Int*)bar)->body;
  1249. else if (bar->type == uridAtomLong)
  1250. lastPositionData.bar = ((LV2_Atom_Long*)bar)->body;
  1251. }
  1252. if (barBeat != nullptr)
  1253. {
  1254. /**/ if (barBeat->type == uridAtomDouble)
  1255. lastPositionData.barBeat = ((LV2_Atom_Double*)barBeat)->body;
  1256. else if (barBeat->type == uridAtomFloat)
  1257. lastPositionData.barBeat = ((LV2_Atom_Float*)barBeat)->body;
  1258. else if (barBeat->type == uridAtomInt)
  1259. lastPositionData.barBeat = ((LV2_Atom_Int*)barBeat)->body;
  1260. else if (barBeat->type == uridAtomLong)
  1261. lastPositionData.barBeat = ((LV2_Atom_Long*)barBeat)->body;
  1262. }
  1263. if (beatUnit != nullptr)
  1264. {
  1265. /**/ if (beatUnit->type == uridAtomDouble)
  1266. lastPositionData.beatUnit = ((LV2_Atom_Double*)beatUnit)->body;
  1267. else if (beatUnit->type == uridAtomFloat)
  1268. lastPositionData.beatUnit = ((LV2_Atom_Float*)beatUnit)->body;
  1269. else if (beatUnit->type == uridAtomInt)
  1270. lastPositionData.beatUnit = ((LV2_Atom_Int*)beatUnit)->body;
  1271. else if (beatUnit->type == uridAtomLong)
  1272. lastPositionData.beatUnit = ((LV2_Atom_Long*)beatUnit)->body;
  1273. if (lastPositionData.beatUnit > 0)
  1274. curPosInfo.timeSigDenominator = lastPositionData.beatUnit;
  1275. }
  1276. if (beatsPerBar != nullptr)
  1277. {
  1278. /**/ if (beatsPerBar->type == uridAtomDouble)
  1279. lastPositionData.beatsPerBar = ((LV2_Atom_Double*)beatsPerBar)->body;
  1280. else if (beatsPerBar->type == uridAtomFloat)
  1281. lastPositionData.beatsPerBar = ((LV2_Atom_Float*)beatsPerBar)->body;
  1282. else if (beatsPerBar->type == uridAtomInt)
  1283. lastPositionData.beatsPerBar = ((LV2_Atom_Int*)beatsPerBar)->body;
  1284. else if (beatsPerBar->type == uridAtomLong)
  1285. lastPositionData.beatsPerBar = ((LV2_Atom_Long*)beatsPerBar)->body;
  1286. if (lastPositionData.beatsPerBar > 0.0f)
  1287. curPosInfo.timeSigNumerator = lastPositionData.beatsPerBar;
  1288. }
  1289. if (beatsPerMinute != nullptr)
  1290. {
  1291. /**/ if (beatsPerMinute->type == uridAtomDouble)
  1292. lastPositionData.beatsPerMinute = ((LV2_Atom_Double*)beatsPerMinute)->body;
  1293. else if (beatsPerMinute->type == uridAtomFloat)
  1294. lastPositionData.beatsPerMinute = ((LV2_Atom_Float*)beatsPerMinute)->body;
  1295. else if (beatsPerMinute->type == uridAtomInt)
  1296. lastPositionData.beatsPerMinute = ((LV2_Atom_Int*)beatsPerMinute)->body;
  1297. else if (beatsPerMinute->type == uridAtomLong)
  1298. lastPositionData.beatsPerMinute = ((LV2_Atom_Long*)beatsPerMinute)->body;
  1299. if (lastPositionData.beatsPerMinute > 0.0f)
  1300. {
  1301. curPosInfo.bpm = lastPositionData.beatsPerMinute;
  1302. if (lastPositionData.speed != 0)
  1303. curPosInfo.bpm *= std::abs(lastPositionData.speed);
  1304. }
  1305. }
  1306. if (frame != nullptr)
  1307. {
  1308. /**/ if (frame->type == uridAtomDouble)
  1309. lastPositionData.frame = ((LV2_Atom_Double*)frame)->body;
  1310. else if (frame->type == uridAtomFloat)
  1311. lastPositionData.frame = ((LV2_Atom_Float*)frame)->body;
  1312. else if (frame->type == uridAtomInt)
  1313. lastPositionData.frame = ((LV2_Atom_Int*)frame)->body;
  1314. else if (frame->type == uridAtomLong)
  1315. lastPositionData.frame = ((LV2_Atom_Long*)frame)->body;
  1316. if (lastPositionData.frame >= 0)
  1317. {
  1318. curPosInfo.timeInSamples = lastPositionData.frame;
  1319. curPosInfo.timeInSeconds = double(curPosInfo.timeInSamples)/sampleRate;
  1320. }
  1321. }
  1322. if (lastPositionData.bar >= 0 && lastPositionData.beatsPerBar > 0.0f)
  1323. {
  1324. curPosInfo.ppqPositionOfLastBarStart = lastPositionData.bar * lastPositionData.beatsPerBar;
  1325. if (lastPositionData.barBeat >= 0.0f)
  1326. curPosInfo.ppqPosition = curPosInfo.ppqPositionOfLastBarStart + lastPositionData.barBeat;
  1327. }
  1328. lastPositionData.extraValid = (lastPositionData.beatsPerMinute > 0.0 &&
  1329. lastPositionData.beatUnit > 0 &&
  1330. lastPositionData.beatsPerBar > 0.0f);
  1331. }
  1332. #endif
  1333. }
  1334. }
  1335. #endif
  1336. {
  1337. AudioSampleBuffer chans (channels, jmax (numInChans, numOutChans), sampleCount);
  1338. filter->processBlock (chans, midiEvents);
  1339. }
  1340. }
  1341. }
  1342. #if JucePlugin_WantsLV2TimePos
  1343. // update timePos for next callback
  1344. if (lastPositionData.speed != 0.0)
  1345. {
  1346. if (lastPositionData.speed > 0.0)
  1347. {
  1348. // playing forwards
  1349. lastPositionData.frame += sampleCount;
  1350. }
  1351. else
  1352. {
  1353. // playing backwards
  1354. lastPositionData.frame -= sampleCount;
  1355. if (lastPositionData.frame < 0)
  1356. lastPositionData.frame = 0;
  1357. }
  1358. curPosInfo.timeInSamples = lastPositionData.frame;
  1359. curPosInfo.timeInSeconds = double(curPosInfo.timeInSamples)/sampleRate;
  1360. if (lastPositionData.extraValid)
  1361. {
  1362. const double beatsPerMinute = lastPositionData.beatsPerMinute * lastPositionData.speed;
  1363. const double framesPerBeat = 60.0 * sampleRate / beatsPerMinute;
  1364. const double addedBarBeats = double(sampleCount) / framesPerBeat;
  1365. if (lastPositionData.bar >= 0 && lastPositionData.barBeat >= 0.0f)
  1366. {
  1367. lastPositionData.bar += std::floor((lastPositionData.barBeat+addedBarBeats)/
  1368. lastPositionData.beatsPerBar);
  1369. lastPositionData.barBeat = std::fmod(lastPositionData.barBeat+addedBarBeats,
  1370. lastPositionData.beatsPerBar);
  1371. if (lastPositionData.bar < 0)
  1372. lastPositionData.bar = 0;
  1373. curPosInfo.ppqPositionOfLastBarStart = lastPositionData.bar * lastPositionData.beatsPerBar;
  1374. curPosInfo.ppqPosition = curPosInfo.ppqPositionOfLastBarStart + lastPositionData.barBeat;
  1375. }
  1376. curPosInfo.bpm = std::abs(beatsPerMinute);
  1377. }
  1378. }
  1379. #endif
  1380. #if JucePlugin_ProducesMidiOutput
  1381. if (portMidiOut != nullptr)
  1382. {
  1383. const uint32_t capacity = portMidiOut->atom.size;
  1384. portMidiOut->atom.size = sizeof(LV2_Atom_Sequence_Body);
  1385. portMidiOut->atom.type = uridAtomSequence;
  1386. portMidiOut->body.unit = 0;
  1387. portMidiOut->body.pad = 0;
  1388. if (! midiEvents.isEmpty())
  1389. {
  1390. const uint8* midiEventData;
  1391. int midiEventSize, midiEventPosition;
  1392. MidiBuffer::Iterator i (midiEvents);
  1393. uint32_t size, offset = 0;
  1394. LV2_Atom_Event* aev;
  1395. while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
  1396. {
  1397. jassert (midiEventPosition >= 0 && midiEventPosition < (int)sampleCount);
  1398. if (sizeof(LV2_Atom_Event) + midiEventSize > capacity - offset)
  1399. break;
  1400. aev = (LV2_Atom_Event*)((char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, portMidiOut) + offset);
  1401. aev->time.frames = midiEventPosition;
  1402. aev->body.type = uridMidiEvent;
  1403. aev->body.size = midiEventSize;
  1404. memcpy(LV2_ATOM_BODY(&aev->body), midiEventData, midiEventSize);
  1405. size = lv2_atom_pad_size(sizeof(LV2_Atom_Event) + midiEventSize);
  1406. offset += size;
  1407. portMidiOut->atom.size += size;
  1408. }
  1409. midiEvents.clear();
  1410. }
  1411. } else
  1412. #endif
  1413. if (! midiEvents.isEmpty())
  1414. {
  1415. midiEvents.clear();
  1416. }
  1417. }
  1418. //==============================================================================
  1419. // LV2 extended calls
  1420. uint32_t lv2GetOptions (LV2_Options_Option* options)
  1421. {
  1422. // currently unused
  1423. ignoreUnused(options);
  1424. return LV2_OPTIONS_SUCCESS;
  1425. }
  1426. uint32_t lv2SetOptions (const LV2_Options_Option* options)
  1427. {
  1428. for (int j=0; options[j].key != 0; ++j)
  1429. {
  1430. if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__nominalBlockLength))
  1431. {
  1432. if (options[j].type == uridAtomInt)
  1433. bufferSize = *(int*)options[j].value;
  1434. else
  1435. std::cerr << "Host changed nominalBlockLength but with wrong value type" << std::endl;
  1436. }
  1437. else if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength) && ! usingNominalBlockLength)
  1438. {
  1439. if (options[j].type == uridAtomInt)
  1440. bufferSize = *(int*)options[j].value;
  1441. else
  1442. std::cerr << "Host changed maxBlockLength but with wrong value type" << std::endl;
  1443. }
  1444. else if (options[j].key == uridMap->map(uridMap->handle, LV2_CORE__sampleRate))
  1445. {
  1446. if (options[j].type == uridAtomDouble)
  1447. sampleRate = *(double*)options[j].value;
  1448. else
  1449. std::cerr << "Host changed sampleRate but with wrong value type" << std::endl;
  1450. }
  1451. }
  1452. return LV2_OPTIONS_SUCCESS;
  1453. }
  1454. const LV2_Program_Descriptor* lv2GetProgram (uint32_t index)
  1455. {
  1456. jassert (filter != nullptr);
  1457. if (progDesc.name != nullptr)
  1458. {
  1459. free((void*)progDesc.name);
  1460. progDesc.name = nullptr;
  1461. }
  1462. if ((int)index < filter->getNumPrograms())
  1463. {
  1464. progDesc.bank = index / 128;
  1465. progDesc.program = index % 128;
  1466. progDesc.name = strdup(filter->getProgramName(index).toUTF8());
  1467. return &progDesc;
  1468. }
  1469. return nullptr;
  1470. }
  1471. void lv2SelectProgram (uint32_t bank, uint32_t program)
  1472. {
  1473. jassert (filter != nullptr);
  1474. int realProgram = bank * 128 + program;
  1475. if (realProgram < filter->getNumPrograms())
  1476. {
  1477. filter->setCurrentProgram(realProgram);
  1478. // update input control ports now
  1479. for (int i = 0; i < portControls.size(); ++i)
  1480. {
  1481. float value = filter->getParameter(i);
  1482. if (portControls[i] != nullptr)
  1483. *portControls[i] = value;
  1484. lastControlValues.set(i, value);
  1485. }
  1486. }
  1487. }
  1488. LV2_State_Status lv2SaveState (LV2_State_Store_Function store, LV2_State_Handle stateHandle)
  1489. {
  1490. jassert (filter != nullptr);
  1491. #if JucePlugin_WantsLV2StateString
  1492. String stateData(filter->getStateInformationString().replace("\r\n","\n"));
  1493. CharPointer_UTF8 charData(stateData.toUTF8());
  1494. store (stateHandle,
  1495. uridMap->map(uridMap->handle, JUCE_LV2_STATE_STRING_URI),
  1496. charData.getAddress(),
  1497. charData.sizeInBytes(),
  1498. uridMap->map(uridMap->handle, LV2_ATOM__String),
  1499. LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE);
  1500. #else
  1501. MemoryBlock chunkMemory;
  1502. filter->getCurrentProgramStateInformation (chunkMemory);
  1503. store (stateHandle,
  1504. uridMap->map(uridMap->handle, JUCE_LV2_STATE_BINARY_URI),
  1505. chunkMemory.getData(),
  1506. chunkMemory.getSize(),
  1507. uridMap->map(uridMap->handle, LV2_ATOM__Chunk),
  1508. LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE);
  1509. #endif
  1510. return LV2_STATE_SUCCESS;
  1511. }
  1512. LV2_State_Status lv2RestoreState (LV2_State_Retrieve_Function retrieve, LV2_State_Handle stateHandle, uint32_t flags)
  1513. {
  1514. jassert (filter != nullptr);
  1515. size_t size = 0;
  1516. uint32 type = 0;
  1517. const void* data = retrieve (stateHandle,
  1518. #if JucePlugin_WantsLV2StateString
  1519. uridMap->map(uridMap->handle, JUCE_LV2_STATE_STRING_URI),
  1520. #else
  1521. uridMap->map(uridMap->handle, JUCE_LV2_STATE_BINARY_URI),
  1522. #endif
  1523. &size, &type, &flags);
  1524. if (data == nullptr || size == 0 || type == 0)
  1525. return LV2_STATE_ERR_UNKNOWN;
  1526. #if JucePlugin_WantsLV2StateString
  1527. if (type == uridMap->map (uridMap->handle, LV2_ATOM__String))
  1528. {
  1529. String stateData (CharPointer_UTF8(static_cast<const char*>(data)));
  1530. filter->setStateInformationString (stateData);
  1531. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  1532. if (ui != nullptr)
  1533. ui->repaint();
  1534. #endif
  1535. return LV2_STATE_SUCCESS;
  1536. }
  1537. #else
  1538. if (type == uridMap->map (uridMap->handle, LV2_ATOM__Chunk))
  1539. {
  1540. filter->setCurrentProgramStateInformation (data, size);
  1541. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  1542. if (ui != nullptr)
  1543. ui->repaint();
  1544. #endif
  1545. return LV2_STATE_SUCCESS;
  1546. }
  1547. #endif
  1548. return LV2_STATE_ERR_BAD_TYPE;
  1549. }
  1550. //==============================================================================
  1551. // Juce calls
  1552. bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info)
  1553. {
  1554. #if JucePlugin_WantsLV2TimePos
  1555. info = curPosInfo;
  1556. return true;
  1557. #else
  1558. ignoreUnused(info);
  1559. return false;
  1560. #endif
  1561. }
  1562. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  1563. //==============================================================================
  1564. JuceLv2UIWrapper* getUI (LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget,
  1565. const LV2_Feature* const* features, bool isExternal)
  1566. {
  1567. const MessageManagerLock mmLock;
  1568. if (ui != nullptr)
  1569. ui->resetIfNeeded (writeFunction, controller, widget, features);
  1570. else
  1571. ui = new JuceLv2UIWrapper (filter, writeFunction, controller, widget, features, isExternal);
  1572. return ui;
  1573. }
  1574. #endif
  1575. private:
  1576. #if JUCE_LINUX
  1577. SharedResourcePointer<SharedMessageThread> msgThread;
  1578. #else
  1579. SharedResourcePointer<ScopedJuceInitialiser_GUI> sharedJuceGUI;
  1580. #endif
  1581. ScopedPointer<AudioProcessor> filter;
  1582. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  1583. ScopedPointer<JuceLv2UIWrapper> ui;
  1584. #endif
  1585. HeapBlock<float*> channels;
  1586. MidiBuffer midiEvents;
  1587. int numInChans, numOutChans;
  1588. #if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
  1589. LV2_Atom_Sequence* portEventsIn;
  1590. #endif
  1591. #if JucePlugin_ProducesMidiOutput
  1592. LV2_Atom_Sequence* portMidiOut;
  1593. #endif
  1594. float* portFreewheel;
  1595. #if JucePlugin_WantsLV2Latency
  1596. float* portLatency;
  1597. #endif
  1598. float* portAudioIns[JucePlugin_MaxNumInputChannels];
  1599. float* portAudioOuts[JucePlugin_MaxNumOutputChannels];
  1600. Array<float*> portControls;
  1601. uint32 bufferSize;
  1602. double sampleRate;
  1603. Array<float> lastControlValues;
  1604. AudioPlayHead::CurrentPositionInfo curPosInfo;
  1605. struct Lv2PositionData {
  1606. int64_t bar;
  1607. float barBeat;
  1608. uint32_t beatUnit;
  1609. float beatsPerBar;
  1610. float beatsPerMinute;
  1611. int64_t frame;
  1612. double speed;
  1613. bool extraValid;
  1614. Lv2PositionData()
  1615. : bar(-1),
  1616. barBeat(-1.0f),
  1617. beatUnit(0),
  1618. beatsPerBar(0.0f),
  1619. beatsPerMinute(0.0f),
  1620. frame(-1),
  1621. speed(0.0),
  1622. extraValid(false) {}
  1623. };
  1624. Lv2PositionData lastPositionData;
  1625. const LV2_URID_Map* uridMap;
  1626. LV2_URID uridAtomBlank;
  1627. LV2_URID uridAtomObject;
  1628. LV2_URID uridAtomDouble;
  1629. LV2_URID uridAtomFloat;
  1630. LV2_URID uridAtomInt;
  1631. LV2_URID uridAtomLong;
  1632. LV2_URID uridAtomSequence;
  1633. LV2_URID uridMidiEvent;
  1634. LV2_URID uridTimePos;
  1635. LV2_URID uridTimeBar;
  1636. LV2_URID uridTimeBarBeat;
  1637. LV2_URID uridTimeBeatsPerBar; // timeSigNumerator
  1638. LV2_URID uridTimeBeatsPerMinute; // bpm
  1639. LV2_URID uridTimeBeatUnit; // timeSigDenominator
  1640. LV2_URID uridTimeFrame; // timeInSamples
  1641. LV2_URID uridTimeSpeed;
  1642. bool usingNominalBlockLength; // if false use maxBlockLength
  1643. LV2_Program_Descriptor progDesc;
  1644. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2Wrapper)
  1645. };
  1646. //==============================================================================
  1647. // LV2 descriptor functions
  1648. static LV2_Handle juceLV2_Instantiate (const LV2_Descriptor*, double sampleRate, const char*, const LV2_Feature* const* features)
  1649. {
  1650. return new JuceLv2Wrapper (sampleRate, features);
  1651. }
  1652. #define handlePtr ((JuceLv2Wrapper*)handle)
  1653. static void juceLV2_ConnectPort (LV2_Handle handle, uint32 port, void* dataLocation)
  1654. {
  1655. handlePtr->lv2ConnectPort (port, dataLocation);
  1656. }
  1657. static void juceLV2_Activate (LV2_Handle handle)
  1658. {
  1659. handlePtr->lv2Activate();
  1660. }
  1661. static void juceLV2_Run( LV2_Handle handle, uint32 sampleCount)
  1662. {
  1663. handlePtr->lv2Run (sampleCount);
  1664. }
  1665. static void juceLV2_Deactivate (LV2_Handle handle)
  1666. {
  1667. handlePtr->lv2Deactivate();
  1668. }
  1669. static void juceLV2_Cleanup (LV2_Handle handle)
  1670. {
  1671. delete handlePtr;
  1672. }
  1673. //==============================================================================
  1674. // LV2 extended functions
  1675. static uint32_t juceLV2_getOptions (LV2_Handle handle, LV2_Options_Option* options)
  1676. {
  1677. return handlePtr->lv2GetOptions(options);
  1678. }
  1679. static uint32_t juceLV2_setOptions (LV2_Handle handle, const LV2_Options_Option* options)
  1680. {
  1681. return handlePtr->lv2SetOptions(options);
  1682. }
  1683. static const LV2_Program_Descriptor* juceLV2_getProgram (LV2_Handle handle, uint32_t index)
  1684. {
  1685. return handlePtr->lv2GetProgram(index);
  1686. }
  1687. static void juceLV2_selectProgram (LV2_Handle handle, uint32_t bank, uint32_t program)
  1688. {
  1689. handlePtr->lv2SelectProgram(bank, program);
  1690. }
  1691. static LV2_State_Status juceLV2_SaveState (LV2_Handle handle, LV2_State_Store_Function store, LV2_State_Handle stateHandle,
  1692. uint32_t, const LV2_Feature* const*)
  1693. {
  1694. return handlePtr->lv2SaveState(store, stateHandle);
  1695. }
  1696. static LV2_State_Status juceLV2_RestoreState (LV2_Handle handle, LV2_State_Retrieve_Function retrieve, LV2_State_Handle stateHandle,
  1697. uint32_t flags, const LV2_Feature* const*)
  1698. {
  1699. return handlePtr->lv2RestoreState(retrieve, stateHandle, flags);
  1700. }
  1701. #undef handlePtr
  1702. static const void* juceLV2_ExtensionData (const char* uri)
  1703. {
  1704. static const LV2_Options_Interface options = { juceLV2_getOptions, juceLV2_setOptions };
  1705. static const LV2_Programs_Interface programs = { juceLV2_getProgram, juceLV2_selectProgram };
  1706. static const LV2_State_Interface state = { juceLV2_SaveState, juceLV2_RestoreState };
  1707. if (strcmp(uri, LV2_OPTIONS__interface) == 0)
  1708. return &options;
  1709. if (strcmp(uri, LV2_PROGRAMS__Interface) == 0)
  1710. return &programs;
  1711. if (strcmp(uri, LV2_STATE__interface) == 0)
  1712. return &state;
  1713. return nullptr;
  1714. }
  1715. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  1716. //==============================================================================
  1717. // LV2 UI descriptor functions
  1718. static LV2UI_Handle juceLV2UI_Instantiate (LV2UI_Write_Function writeFunction, LV2UI_Controller controller,
  1719. LV2UI_Widget* widget, const LV2_Feature* const* features, bool isExternal)
  1720. {
  1721. for (int i = 0; features[i] != nullptr; ++i)
  1722. {
  1723. if (strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0 && features[i]->data != nullptr)
  1724. {
  1725. JuceLv2Wrapper* wrapper = (JuceLv2Wrapper*)features[i]->data;
  1726. return wrapper->getUI(writeFunction, controller, widget, features, isExternal);
  1727. }
  1728. }
  1729. std::cerr << "Host does not support instance-access, cannot use UI" << std::endl;
  1730. return nullptr;
  1731. }
  1732. static LV2UI_Handle juceLV2UI_InstantiateExternal (const LV2UI_Descriptor*, const char*, const char*, LV2UI_Write_Function writeFunction,
  1733. LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features)
  1734. {
  1735. return juceLV2UI_Instantiate(writeFunction, controller, widget, features, true);
  1736. }
  1737. static LV2UI_Handle juceLV2UI_InstantiateParent (const LV2UI_Descriptor*, const char*, const char*, LV2UI_Write_Function writeFunction,
  1738. LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features)
  1739. {
  1740. return juceLV2UI_Instantiate(writeFunction, controller, widget, features, false);
  1741. }
  1742. static void juceLV2UI_Cleanup (LV2UI_Handle handle)
  1743. {
  1744. ((JuceLv2UIWrapper*)handle)->lv2Cleanup();
  1745. }
  1746. #endif
  1747. //==============================================================================
  1748. // static LV2 Descriptor objects
  1749. static const LV2_Descriptor JuceLv2Plugin = {
  1750. strdup(getPluginURI().toRawUTF8()),
  1751. juceLV2_Instantiate,
  1752. juceLV2_ConnectPort,
  1753. juceLV2_Activate,
  1754. juceLV2_Run,
  1755. juceLV2_Deactivate,
  1756. juceLV2_Cleanup,
  1757. juceLV2_ExtensionData
  1758. };
  1759. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  1760. static const LV2UI_Descriptor JuceLv2UI_External = {
  1761. strdup(String(getPluginURI() + "#ExternalUI").toRawUTF8()),
  1762. juceLV2UI_InstantiateExternal,
  1763. juceLV2UI_Cleanup,
  1764. nullptr,
  1765. nullptr
  1766. };
  1767. static const LV2UI_Descriptor JuceLv2UI_Parent = {
  1768. strdup(String(getPluginURI() + "#ParentUI").toRawUTF8()),
  1769. juceLV2UI_InstantiateParent,
  1770. juceLV2UI_Cleanup,
  1771. nullptr,
  1772. nullptr
  1773. };
  1774. #endif
  1775. static const struct DescriptorCleanup {
  1776. DescriptorCleanup() {}
  1777. ~DescriptorCleanup()
  1778. {
  1779. free((void*)JuceLv2Plugin.URI);
  1780. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  1781. free((void*)JuceLv2UI_External.URI);
  1782. free((void*)JuceLv2UI_Parent.URI);
  1783. #endif
  1784. }
  1785. } _descCleanup;
  1786. #if JUCE_WINDOWS
  1787. #define JUCE_EXPORTED_FUNCTION extern "C" __declspec (dllexport)
  1788. #else
  1789. #define JUCE_EXPORTED_FUNCTION extern "C" __attribute__ ((visibility("default")))
  1790. #endif
  1791. //==============================================================================
  1792. // startup code..
  1793. JUCE_EXPORTED_FUNCTION void lv2_generate_ttl (const char* basename);
  1794. JUCE_EXPORTED_FUNCTION void lv2_generate_ttl (const char* basename)
  1795. {
  1796. createLv2Files (basename);
  1797. }
  1798. JUCE_EXPORTED_FUNCTION const LV2_Descriptor* lv2_descriptor (uint32 index);
  1799. JUCE_EXPORTED_FUNCTION const LV2_Descriptor* lv2_descriptor (uint32 index)
  1800. {
  1801. return (index == 0) ? &JuceLv2Plugin : nullptr;
  1802. }
  1803. #if ! JUCE_AUDIOPROCESSOR_NO_GUI
  1804. JUCE_EXPORTED_FUNCTION const LV2UI_Descriptor* lv2ui_descriptor (uint32 index);
  1805. JUCE_EXPORTED_FUNCTION const LV2UI_Descriptor* lv2ui_descriptor (uint32 index)
  1806. {
  1807. switch (index)
  1808. {
  1809. case 0:
  1810. return &JuceLv2UI_External;
  1811. case 1:
  1812. return &JuceLv2UI_Parent;
  1813. default:
  1814. return nullptr;
  1815. }
  1816. }
  1817. #endif
  1818. #endif