Misc fixes
[juce-lv2.git] / juce / source / src / audio / plugin_client / LV2 / juce_LV2_Wrapper.cpp
blobfdf1d58248e545007e814aa227e0be2c2119d8a0
1 /*
2 * JUCE LV2 wrapper
3 */
5 #include "../juce_IncludeCharacteristics.h"
7 #if JucePlugin_Build_LV2
9 #include <fstream>
10 #include <iostream>
11 #include <stdint.h>
13 #include "juce.h"
15 // LV2 includes
16 #include "lv2/lv2.h"
17 #include "lv2/event.h"
18 #include "lv2/event_helpers.h"
19 #include "lv2/instance_access.h"
20 #include "lv2/uri_map.h"
21 #include "lv2/ui.h"
22 #include "lv2/lv2_external_ui.h"
24 // These are dummy values!
25 enum FakePlugCategory
27 kPlugCategUnknown,
28 kPlugCategEffect,
29 kPlugCategSynth,
30 kPlugCategAnalysis,
31 kPlugCategMastering,
32 kPlugCategSpacializer,
33 kPlugCategRoomFx,
34 kPlugSurroundFx,
35 kPlugCategRestoration,
36 kPlugCategOfflineProcess,
37 kPlugCategGenerator
40 extern AudioProcessor* JUCE_CALLTYPE createPluginFilter();
42 String name_to_symbol(String Name, uint32_t port_index)
44 String Symbol = Name.trimStart().trimEnd().replace(" ", "_").toLowerCase();
46 if (Symbol.isEmpty())
48 Symbol += String(port_index);
50 else
52 for (int i=0; i < Symbol.length(); i++)
54 if (std::isalpha(Symbol[i]) || std::isdigit(Symbol[i]) || Symbol[i] == '_') {
55 // nothing
56 } else {
57 Symbol[i] == '_';
62 return Symbol;
65 String float_to_string(float value)
67 if (value < 0.0f || value > 1.0f) {
68 std::cerr << "WARNING - Parameter uses out-of-bounds value -> " << value << std::endl;
70 String string(value);
71 if (!string.contains(".")) {
72 string += ".0";
74 return string;
77 String get_uri()
79 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":" JucePlugin_VersionString).replace(" ", "_");
82 String get_juce_ui_uri()
84 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":JUCE-Native-UI").replace(" ", "_");
87 String get_external_ui_uri()
89 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":JUCE-External-UI").replace(" ", "_");
92 String get_binary_name()
94 return String(JucePlugin_Name).replace(" ", "_");
97 String get_plugin_type()
99 String ptype;
101 switch (JucePlugin_VSTCategory) {
102 case kPlugCategSynth:
103 ptype += "lv2:InstrumentPlugin";
104 break;
105 case kPlugCategAnalysis:
106 ptype += "lv2:AnalyserPlugin";
107 break;
108 case kPlugCategMastering:
109 ptype += "lv2:DynamicsPlugin";
110 break;
111 case kPlugCategSpacializer:
112 ptype += "lv2:SpatialPlugin";
113 break;
114 case kPlugCategRoomFx:
115 ptype += "lv2:ModulatorPlugin";
116 break;
117 case kPlugCategRestoration:
118 ptype += "lv2:UtilityPlugin";
119 break;
120 case kPlugCategGenerator:
121 ptype += "lv2:GeneratorPlugin";
122 break;
125 if (ptype.isNotEmpty()) {
126 ptype += ", ";
129 ptype += "lv2:Plugin";
130 return ptype;
133 String get_manifest_ttl(String URI, String Binary)
135 String manifest;
136 manifest += "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n";
137 manifest += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
138 manifest += "\n";
139 manifest += "<" + URI + ">\n";
140 manifest += " a lv2:Plugin ;\n";
141 manifest += " lv2:binary <" + Binary + ".so> ;\n";
142 manifest += " rdfs:seeAlso <" + Binary +".ttl> .\n";
143 return manifest;
146 String get_plugin_ttl(String URI, String Binary)
148 uint32_t i, port_index = 0;
149 AudioProcessor* filter = createPluginFilter();
151 String plugin;
152 plugin += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n";
153 //plugin += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n";
154 //plugin += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
155 plugin += "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n";
156 plugin += "@prefix lv2ev: <http://lv2plug.in/ns/ext/event#> .\n";
157 plugin += "@prefix lv2ui: <http://lv2plug.in/ns/extensions/ui#> .\n";
158 plugin += "\n";
160 if (filter->hasEditor()) {
161 #if 0
162 plugin += "<" + get_juce_ui_uri() + ">\n";
163 plugin += " a lv2ui:JUCEUI ;\n";
164 plugin += " lv2ui:binary <" + Binary + ".so> .\n";
165 #endif
166 plugin += "<" + get_external_ui_uri() + ">\n";
167 plugin += " a lv2ui:external ;\n";
168 plugin += " lv2ui:binary <" + Binary + ".so> .\n";
169 plugin += "\n";
172 plugin += "<" + URI + ">\n";
173 plugin += " a " + get_plugin_type() + " ;\n";
175 if (filter->hasEditor()) {
176 #if 0
177 plugin += " lv2ui:ui <" + get_juce_ui_uri() + ">;\n";
178 #endif
179 plugin += " lv2ui:ui <" + get_external_ui_uri() + ">;\n";
182 plugin += "\n";
184 #if JucePlugin_WantsMidiInput
185 plugin += " lv2:port [\n";
186 plugin += " a lv2:InputPort, lv2ev:EventPort;\n";
187 plugin += " lv2ev:supportsEvent <http://lv2plug.in/ns/ext/midi#MidiEvent> ;\n";
188 plugin += " lv2:index " + String(port_index++) + ";\n";
189 plugin += " lv2:symbol \"midi_in\";\n";
190 plugin += " lv2:name \"MIDI Input\";\n";
191 plugin += " ] ;\n\n";
192 #endif
194 #if JucePlugin_ProducesMidiOutput
195 plugin += " lv2:port [\n";
196 plugin += " a lv2:OutputPort, lv2ev:EventPort;\n";
197 plugin += " lv2ev:supportsEvent <http://lv2plug.in/ns/ext/midi#MidiEvent> ;\n";
198 plugin += " lv2:index " + String(port_index++) + ";\n";
199 plugin += " lv2:symbol \"midi_out\";\n";
200 plugin += " lv2:name \"MIDI Output\";\n";
201 plugin += " ] ;\n\n";
202 #endif
204 for (i=0; i<JucePlugin_MaxNumInputChannels; i++) {
205 if (i == 0) {
206 plugin += " lv2:port [\n";
207 } else {
208 plugin += " [\n";
211 plugin += " a lv2:InputPort, lv2:AudioPort;\n";
212 //plugin += " lv2:datatype lv2:float;\n";
213 plugin += " lv2:index " + String(port_index++) + ";\n";
214 plugin += " lv2:symbol \"audio_in_" + String(i+1) + "\";\n";
215 plugin += " lv2:name \"Audio Input " + String(i+1) + "\";\n";
217 if (i == JucePlugin_MaxNumInputChannels-1) {
218 plugin += " ] ;\n\n";
219 } else {
220 plugin += " ],\n";
224 for (i=0; i<JucePlugin_MaxNumOutputChannels; i++) {
225 if (i == 0) {
226 plugin += " lv2:port [\n";
227 } else {
228 plugin += " [\n";
231 plugin += " a lv2:OutputPort, lv2:AudioPort;\n";
232 //plugin += " lv2:datatype lv2:float;\n";
233 plugin += " lv2:index " + String(port_index++) + ";\n";
234 plugin += " lv2:symbol \"audio_out_" + String(i+1) + "\";\n";
235 plugin += " lv2:name \"Audio Output " + String(i+1) + "\";\n";
237 if (i == JucePlugin_MaxNumOutputChannels-1) {
238 plugin += " ] ;\n\n";
239 } else {
240 plugin += " ],\n";
244 for (i=0; i < filter->getNumParameters(); i++) {
245 if (i == 0) {
246 plugin += " lv2:port [\n";
247 } else {
248 plugin += " [\n";
251 plugin += " a lv2:InputPort;\n";
252 plugin += " a lv2:ControlPort;\n";
253 //plugin += " lv2:datatype lv2:float;\n";
254 plugin += " lv2:index " + String(port_index++) + ";\n";
255 plugin += " lv2:symbol \"" + name_to_symbol(filter->getParameterName(i), i) + "\";\n";
256 plugin += " lv2:name \"" + filter->getParameterName(i) + "\";\n";
257 plugin += " lv2:default " + float_to_string(filter->getParameter(i)) + ";\n";
258 plugin += " lv2:minimum 0.0;\n";
259 plugin += " lv2:maximum 1.0;\n";
261 if (i == filter->getNumParameters()-1) {
262 plugin += " ] ;\n\n";
263 } else {
264 plugin += " ],\n";
268 plugin += " doap:name \"" + String(JucePlugin_Name) + "\" ;\n";
269 plugin += " doap:creator \"" + String(JucePlugin_Manufacturer) + "\" .\n";
271 delete filter;
272 return plugin;
275 void generate_ttl()
277 String URI = get_uri();
278 String Binary = get_binary_name();
279 String BinaryTTL = Binary + ".ttl";
281 std::cout << "Writing manifest.ttl...";
282 std::fstream manifest("manifest.ttl", std::ios::out);
283 manifest << get_manifest_ttl(URI, Binary) << std::endl;
284 manifest.close();
285 std::cout << " done!" << std::endl;
287 std::cout << "Writing " << BinaryTTL << "...";
288 std::fstream plugin(BinaryTTL.toUTF8(), std::ios::out);
289 plugin << get_plugin_ttl(URI, Binary) << std::endl;
290 plugin.close();
291 std::cout << " done!" << std::endl;
294 //==============================================================================
295 BEGIN_JUCE_NAMESPACE
296 extern void juce_callAnyTimersSynchronously();
298 #if JUCE_MAC
299 extern void initialiseMac();
300 #endif
301 END_JUCE_NAMESPACE
303 //==============================================================================
304 #if JUCE_LINUX
306 class SharedMessageThread : public Thread
308 public:
309 SharedMessageThread()
310 : Thread ("Lv2MessageThread"),
311 initialised (false)
313 startThread (7);
315 while (! initialised)
316 sleep (1);
319 ~SharedMessageThread()
321 signalThreadShouldExit();
322 JUCEApplication::quit();
323 waitForThreadToExit (5000);
324 clearSingletonInstance();
327 void run()
329 initialiseJuce_GUI();
330 initialised = true;
332 MessageManager::getInstance()->setCurrentThreadAsMessageThread();
334 while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250))
338 juce_DeclareSingleton (SharedMessageThread, false);
340 private:
341 bool initialised;
344 juce_ImplementSingleton (SharedMessageThread)
346 #endif
348 static Array<void*> activePlugins;
350 //==============================================================================
351 // Create a new JUCE LV2 Plugin
352 class JuceLV2Wrapper :
353 private Timer,
354 public AudioProcessorListener
356 public:
357 JuceLV2Wrapper(const LV2_Descriptor* descriptor_, double sample_rate_, const LV2_Feature* const* features) :
358 chunkMemoryTime (0),
359 numInChans (JucePlugin_MaxNumInputChannels),
360 numOutChans (JucePlugin_MaxNumOutputChannels),
361 isProcessing (false),
362 firstProcessCallback (true),
363 descriptor (descriptor_),
364 sample_rate (sample_rate_),
365 buffer_size (512),
366 midi_uri_id (0),
367 port_count (0)
369 printf("JuceLV2Wrapper()\n");
371 JUCE_AUTORELEASEPOOL;
372 initialiseJuce_GUI();
374 #if JUCE_LINUX
375 MessageManagerLock mmLock;
376 #endif
378 filter = createPluginFilter();
379 jassert(filter != nullptr);
381 filter->setPlayConfigDetails(numInChans, numOutChans, 0, 0);
383 // Port count
384 #if JucePlugin_WantsMidiInput
385 port_count += 1;
386 #endif
387 #if JucePlugin_ProducesMidiOutput
388 port_count += 1;
389 #endif
390 port_count += numInChans;
391 port_count += numOutChans;
392 port_count += filter->getNumParameters();
394 // Set Port data
395 port_min = nullptr;
396 port_mout = nullptr;
397 ports_ctrl.insertMultiple(0, nullptr, filter->getNumParameters());
398 ports_ctrl_last.insertMultiple(0, 0.0f, filter->getNumParameters());
400 for (int i=0; i < numInChans; i++) {
401 ports_ain[i] = nullptr;
404 for (int i=0; i < numOutChans; i++) {
405 ports_aout[i] = nullptr;
408 for (int i=0; i < filter->getNumParameters(); i++) {
409 ports_ctrl_last.set(i, filter->getParameter(i));
412 // Get MIDI URI Id
413 for (uint16_t j = 0; features[j]; j++)
415 if (strcmp(features[j]->URI, LV2_URI_MAP_URI) == 0)
417 LV2_URI_Map_Feature* uri_feature = (LV2_URI_Map_Feature*)features[j]->data;
418 midi_uri_id = uri_feature->uri_to_id(uri_feature->callback_data, LV2_EVENT_URI, "http://lv2plug.in/ns/ext/midi#MidiEvent");
419 break;
423 activePlugins.add (this);
425 startTimer(1000);
428 ~JuceLV2Wrapper()
430 JUCE_AUTORELEASEPOOL
433 #if JUCE_LINUX
434 MessageManagerLock mmLock;
435 #endif
436 stopTimer();
438 delete filter;
439 filter = nullptr;
441 channels.free();
442 deleteTempChannels();
444 ports_ctrl.clear();
445 ports_ctrl_last.clear();
447 jassert (activePlugins.contains (this));
448 activePlugins.removeValue (this);
451 if (activePlugins.size() == 0)
453 #if JUCE_LINUX
454 SharedMessageThread::deleteInstance();
455 #endif
456 shutdownJuce_GUI();
460 //==============================================================================
461 // LV2 Descriptor Calls
462 void do_connect_port(uint32_t port, void* data_location)
464 if (port < port_count)
466 int i;
467 uint32_t index = 0;
469 #if JucePlugin_WantsMidiInput
470 if (port == index) {
471 port_min = (LV2_Event_Buffer*)data_location;
472 return;
474 index += 1;
475 #endif
477 #if JucePlugin_ProducesMidiOutput
478 if (port == index) {
479 port_mout = (LV2_Event_Buffer*)data_location;
480 return;
482 index += 1;
483 #endif
485 for (i=0; i < numInChans; i++) {
486 if (port == index) {
487 ports_ain[i] = (float*)data_location;
488 return;
490 index += 1;
493 for (i=0; i < numOutChans; i++) {
494 if (port == index) {
495 ports_aout[i] = (float*)data_location;
496 return;
498 index += 1;
501 for (i=0; i < filter->getNumParameters(); i++) {
502 if (port == index) {
503 ports_ctrl.set(i, (float*)data_location);
504 return;
506 index += 1;
511 void do_activate()
513 if (filter != nullptr)
515 isProcessing = true;
516 channels.calloc (numInChans + numOutChans);
518 jassert (sample_rate > 0);
519 if (sample_rate <= 0.0)
520 sample_rate = 44100.0;
522 jassert (buffer_size > 0);
524 firstProcessCallback = true;
526 filter->setNonRealtime (false);
527 filter->setPlayConfigDetails (numInChans, numOutChans, sample_rate, buffer_size);
529 deleteTempChannels();
531 filter->prepareToPlay (sample_rate, buffer_size);
533 midiEvents.ensureSize (2048);
534 midiEvents.clear();
538 void do_deactivate()
540 if (filter != nullptr)
542 filter->releaseResources();
544 isProcessing = false;
545 channels.free();
547 deleteTempChannels();
551 void do_run(uint32_t sample_count)
553 if (firstProcessCallback)
555 firstProcessCallback = false;
557 // if this fails, the host hasn't called resume() before processing
558 jassert (isProcessing);
560 // (tragically, some hosts actually need this, although it's stupid to have
561 // to do it here..)
562 if (! isProcessing)
563 do_activate();
565 filter->setNonRealtime (false);
567 #if JUCE_WINDOWS
568 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
569 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST)
570 filter->setNonRealtime (true);
571 #endif
574 // Check if buffer size changed
575 if (buffer_size != sample_count)
577 buffer_size = sample_count;
578 filter->setPlayConfigDetails(numInChans, numOutChans, sample_rate, buffer_size);
579 filter->prepareToPlay(sample_rate, buffer_size);
582 // Check for updated parameters
583 float cur_value;
584 for (int i = 0; i < ports_ctrl.size(); i++)
586 if (ports_ctrl[i] != nullptr)
588 cur_value = *(float*)ports_ctrl[i];
589 if (ports_ctrl_last[i] != cur_value) {
590 filter->setParameter(i, cur_value);
591 ports_ctrl_last.setUnchecked(i, cur_value);
596 jassert (activePlugins.contains (this));
599 const ScopedLock sl (filter->getCallbackLock());
601 const int numIn = numInChans;
602 const int numOut = numOutChans;
604 if (filter->isSuspended())
606 for (int i = 0; i < numOut; ++i)
607 zeromem (ports_aout[i], sizeof (float) * sample_count);
609 else
611 int i;
612 for (i = 0; i < numOut; ++i)
614 float* chan = tempChannels.getUnchecked(i);
616 if (chan == 0)
618 chan = ports_aout[i];
620 // if some output channels are disabled, some hosts supply the same buffer
621 // for multiple channels - this buggers up our method of copying the
622 // inputs over the outputs, so we need to create unique temp buffers in this case..
623 for (int j = i; --j >= 0;)
625 if (ports_aout[j] == chan)
627 chan = new float [buffer_size * 2];
628 tempChannels.set (i, chan);
629 break;
634 if (i < numIn && chan != ports_ain[i])
635 memcpy (chan, ports_ain[i], sizeof (float) * sample_count);
637 channels[i] = chan;
640 // LV2 MIDI Input
641 if (port_min != nullptr)
643 LV2_Event_Iterator iter;
644 lv2_event_begin(&iter, port_min);
646 lv2_event_buffer_reset(port_min, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(port_min + 1));
648 for (uint32_t i=0; i < iter.buf->event_count; ++i) {
649 uint8_t* data;
650 LV2_Event* ev = lv2_event_get(&iter, &data);
651 midiEvents.addEvent(data, ev->size, ev->frames);
652 lv2_event_increment(&iter);
656 for (; i < numIn; ++i)
657 channels[i] = ports_ain[i];
659 AudioSampleBuffer chans (channels, jmax (numIn, numOut), sample_count);
661 filter->processBlock (chans, midiEvents);
665 if (! midiEvents.isEmpty() && port_mout != nullptr)
667 #if JucePlugin_ProducesMidiOutput
668 const int numEvents = midiEvents.getNumEvents();
670 LV2_Event_Iterator iter;
671 lv2_event_buffer_reset(port_mout, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(port_mout + 1));
672 lv2_event_begin(&iter, port_mout);
674 const JUCE_NAMESPACE::uint8* midiEventData;
675 int midiEventSize, midiEventPosition;
676 MidiBuffer::Iterator i (midiEvents);
678 while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
680 jassert (midiEventPosition >= 0 && midiEventPosition < sample_count);
682 lv2_event_write(&iter, midiEventPosition, 0, midi_uri_id, midiEventSize, midiEventData);
684 #endif
685 midiEvents.clear();
689 void do_cleanup()
691 stopTimer();
693 //if (descriptor)
695 // free((void*)descriptor->URI);
696 // delete descriptor;
700 //==============================================================================
701 // JUCE Stuff
703 AudioProcessor* getFilter() { return filter; }
705 void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue)
707 // TODO
710 void audioProcessorChanged (AudioProcessor*)
712 // TODO
715 //==============================================================================
716 int32_t getChunk (void** data)
718 if (filter == nullptr)
719 return 0;
721 chunkMemory.setSize (0);
722 filter->getCurrentProgramStateInformation (chunkMemory);
724 *data = (void*) chunkMemory.getData();
726 // because the chunk is only needed temporarily by the host (or at least you'd
727 // hope so) we'll give it a while and then free it in the timer callback.
728 chunkMemoryTime = JUCE_NAMESPACE::Time::getApproximateMillisecondCounter();
730 return (int32_t) chunkMemory.getSize();
733 void setChunk (void* data, int32_t byteSize)
735 if (filter == nullptr)
736 return;
738 chunkMemory.setSize (0);
739 chunkMemoryTime = 0;
741 if (byteSize > 0 && data != nullptr)
743 filter->setCurrentProgramStateInformation (data, byteSize);
747 void timerCallback()
749 if (chunkMemoryTime > 0 && chunkMemoryTime < JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000)
751 chunkMemoryTime = 0;
752 chunkMemory.setSize (0);
756 void doIdleCallback()
758 if (MessageManager::getInstance()->isThisTheMessageThread())
760 JUCE_AUTORELEASEPOOL
761 juce_callAnyTimersSynchronously();
763 for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
764 ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow();
768 //==============================================================================
769 private:
770 AudioProcessor* filter;
771 JUCE_NAMESPACE::MemoryBlock chunkMemory;
772 JUCE_NAMESPACE::uint32 chunkMemoryTime;
773 MidiBuffer midiEvents;
774 int numInChans, numOutChans;
775 bool isProcessing, firstProcessCallback;
776 HeapBlock<float*> channels;
777 Array<float*> tempChannels; // see note in do_run()
779 const LV2_Descriptor* descriptor;
781 double sample_rate;
782 int buffer_size;
783 uint16_t midi_uri_id;
784 uint32_t port_count;
786 LV2_Event_Buffer* port_min;
787 LV2_Event_Buffer* port_mout;
788 float* ports_ain[JucePlugin_MaxNumInputChannels];
789 float* ports_aout[JucePlugin_MaxNumOutputChannels];
790 Array<float*> ports_ctrl;
791 Array<float> ports_ctrl_last;
793 //==============================================================================
794 void deleteTempChannels()
796 for (int i = tempChannels.size(); --i >= 0;)
797 delete[] (tempChannels.getUnchecked(i));
799 tempChannels.clear();
801 if (filter != nullptr)
802 tempChannels.insertMultiple (0, 0, filter->getNumInputChannels() + filter->getNumOutputChannels());
805 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLV2Wrapper);
808 //==============================================================================
809 // LV2 descriptor functions
810 LV2_Handle juce_lv2_instantiate(const LV2_Descriptor* descriptor, double sample_rate, const char* bundle_path, const LV2_Feature* const* features)
812 JuceLV2Wrapper* wrapper = new JuceLV2Wrapper(descriptor, sample_rate, features);
813 return wrapper;
816 void juce_lv2_connect_port(LV2_Handle instance, uint32_t port, void* data_location)
818 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
819 wrapper->do_connect_port(port, data_location);
822 void juce_lv2_activate(LV2_Handle instance)
824 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
825 wrapper->do_activate();
828 void juce_lv2_run(LV2_Handle instance, uint32_t sample_count)
830 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
831 wrapper->do_run(sample_count);
834 void juce_lv2_deactivate(LV2_Handle instance)
836 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
837 wrapper->do_deactivate();
840 void juce_lv2_cleanup(LV2_Handle instance)
842 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
843 wrapper->do_cleanup();
844 delete wrapper;
847 const void* juce_lv2_extension_data(const char* uri)
849 printf("TODO :: juce_lv2_extension_data()\n");
850 return nullptr;
853 //==============================================================================
854 // Create new LV2 objects
855 LV2_Descriptor* getNewLv2Plugin()
857 LV2_Descriptor* const Lv2Plugin = new LV2_Descriptor;
858 Lv2Plugin->URI = strdup((const char*)get_uri().toUTF8());
859 Lv2Plugin->instantiate = juce_lv2_instantiate;
860 Lv2Plugin->connect_port = juce_lv2_connect_port;
861 Lv2Plugin->activate = juce_lv2_activate;
862 Lv2Plugin->run = juce_lv2_run;
863 Lv2Plugin->deactivate = juce_lv2_deactivate;
864 Lv2Plugin->cleanup = juce_lv2_cleanup;
865 Lv2Plugin->extension_data = juce_lv2_extension_data;
866 return Lv2Plugin;
869 //==============================================================================
870 // Mac startup code..
871 #if JUCE_MAC
873 extern "C" __attribute__ ((visibility("default"))) void juce_lv2_ttl_generator()
875 generate_ttl();
878 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
880 initialiseMac();
881 return (index == 0) ? getNewLv2Plugin() : nullptr;
884 //==============================================================================
885 // Linux startup code..
886 #elif JUCE_LINUX
888 extern "C" __attribute__ ((visibility("default"))) void juce_lv2_ttl_generator()
890 generate_ttl();
893 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
895 SharedMessageThread::getInstance();
896 return (index == 0) ? getNewLv2Plugin() : nullptr;
899 // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash!
900 __attribute__((constructor)) void myPluginInit() {}
901 __attribute__((destructor)) void myPluginFini() {}
903 //==============================================================================
904 // Win32 startup code..
905 #else
907 extern "C" __declspec (dllexport) void juce_lv2_ttl_generator()
909 generate_ttl();
912 extern "C" __declspec (dllexport) const LV2_Descriptor* lv2_descriptor(uint32_t index)
914 return (index == 0) ? getNewLv2Plugin() : nullptr;
917 #endif
919 #endif