Another MIDI fix (now output)
[juce-lv2.git] / juce / source / src / audio / plugin_client / LV2 / juce_LV2_Wrapper.cpp
blob6539c77822c0da836817195dd3c59444edd4af1b
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";
267 port_index++;
270 plugin += " doap:name \"" + String(JucePlugin_Name) + "\" ;\n";
271 plugin += " doap:creator \"" + String(JucePlugin_Manufacturer) + "\" .\n";
273 delete filter;
274 return plugin;
277 void generate_ttl()
279 String URI = get_uri();
280 String Binary = get_binary_name();
281 String BinaryTTL = Binary + ".ttl";
283 std::cout << "Writing manifest.ttl...";
284 std::fstream manifest("manifest.ttl", std::ios::out);
285 manifest << get_manifest_ttl(URI, Binary) << std::endl;
286 manifest.close();
287 std::cout << " done!" << std::endl;
289 std::cout << "Writing " << BinaryTTL << "...";
290 std::fstream plugin(BinaryTTL.toUTF8(), std::ios::out);
291 plugin << get_plugin_ttl(URI, Binary) << std::endl;
292 plugin.close();
293 std::cout << " done!" << std::endl;
296 //==============================================================================
297 BEGIN_JUCE_NAMESPACE
298 extern void juce_callAnyTimersSynchronously();
300 #if JUCE_MAC
301 extern void initialiseMac();
302 #endif
303 END_JUCE_NAMESPACE
305 //==============================================================================
306 #if JUCE_LINUX
308 class SharedMessageThread : public Thread
310 public:
311 SharedMessageThread()
312 : Thread ("Lv2MessageThread"),
313 initialised (false)
315 startThread (7);
317 while (! initialised)
318 sleep (1);
321 ~SharedMessageThread()
323 signalThreadShouldExit();
324 JUCEApplication::quit();
325 waitForThreadToExit (5000);
326 clearSingletonInstance();
329 void run()
331 initialiseJuce_GUI();
332 initialised = true;
334 MessageManager::getInstance()->setCurrentThreadAsMessageThread();
336 while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250))
340 juce_DeclareSingleton (SharedMessageThread, false);
342 private:
343 bool initialised;
346 juce_ImplementSingleton (SharedMessageThread)
348 #endif
350 static Array<void*> activePlugins;
352 //==============================================================================
353 // Create a new JUCE LV2 Plugin
354 class JuceLV2Wrapper :
355 private Timer,
356 public AudioProcessorListener
358 public:
359 JuceLV2Wrapper(const LV2_Descriptor* descriptor_, double sample_rate_, const LV2_Feature* const* features) :
360 chunkMemoryTime (0),
361 numInChans (JucePlugin_MaxNumInputChannels),
362 numOutChans (JucePlugin_MaxNumOutputChannels),
363 isProcessing (false),
364 firstProcessCallback (true),
365 descriptor (descriptor_),
366 sample_rate (sample_rate_),
367 buffer_size (512),
368 midi_uri_id (0),
369 port_count (0)
371 printf("JuceLV2Wrapper()\n");
373 filter = createPluginFilter();
374 jassert(filter != nullptr);
376 filter->setPlayConfigDetails(numInChans, numOutChans, 0, 0);
378 // Port count
379 #if JucePlugin_WantsMidiInput
380 port_count += 1;
381 #endif
382 #if JucePlugin_ProducesMidiOutput
383 port_count += 1;
384 #endif
385 port_count += numInChans;
386 port_count += numOutChans;
387 port_count += filter->getNumParameters();
389 // Set Port data
390 port_min = nullptr;
391 port_mout = nullptr;
392 ports_ctrl.insertMultiple(0, nullptr, filter->getNumParameters());
393 ports_ctrl_last.insertMultiple(0, 0.0f, filter->getNumParameters());
395 for (int i=0; i < numInChans; i++) {
396 ports_ain[i] = nullptr;
399 for (int i=0; i < numOutChans; i++) {
400 ports_aout[i] = nullptr;
403 for (int i=0; i < filter->getNumParameters(); i++) {
404 ports_ctrl_last.set(i, filter->getParameter(i));
407 // Get MIDI URI Id
408 for (uint16_t j = 0; features[j]; j++)
410 if (strcmp(features[j]->URI, LV2_URI_MAP_URI) == 0)
412 LV2_URI_Map_Feature* uri_feature = (LV2_URI_Map_Feature*)features[j]->data;
413 midi_uri_id = uri_feature->uri_to_id(uri_feature->callback_data, LV2_EVENT_URI, "http://lv2plug.in/ns/ext/midi#MidiEvent");
414 break;
418 activePlugins.add (this);
420 startTimer(1000);
423 ~JuceLV2Wrapper()
425 stopTimer();
427 delete filter;
428 filter = nullptr;
430 channels.free();
431 deleteTempChannels();
433 ports_ctrl.clear();
434 ports_ctrl_last.clear();
436 jassert (activePlugins.contains (this));
437 activePlugins.removeValue (this);
440 //==============================================================================
441 // LV2 Descriptor Calls
442 void do_connect_port(uint32_t port, void* data_location)
444 if (port < port_count)
446 int i;
447 uint32_t index = 0;
449 #if JucePlugin_WantsMidiInput
450 if (port == index) {
451 port_min = (LV2_Event_Buffer*)data_location;
452 return;
454 index += 1;
455 #endif
457 #if JucePlugin_ProducesMidiOutput
458 if (port == index) {
459 port_mout = (LV2_Event_Buffer*)data_location;
460 return;
462 index += 1;
463 #endif
465 for (i=0; i < numInChans; i++) {
466 if (port == index) {
467 ports_ain[i] = (float*)data_location;
468 return;
470 index += 1;
473 for (i=0; i < numOutChans; i++) {
474 if (port == index) {
475 ports_aout[i] = (float*)data_location;
476 return;
478 index += 1;
481 for (i=0; i < filter->getNumParameters(); i++) {
482 if (port == index) {
483 ports_ctrl.set(i, (float*)data_location);
484 return;
486 index += 1;
491 void do_activate()
493 if (filter != nullptr)
495 isProcessing = true;
496 channels.calloc (numInChans + numOutChans);
498 jassert (sample_rate > 0);
499 if (sample_rate <= 0.0)
500 sample_rate = 44100.0;
502 jassert (buffer_size > 0);
504 firstProcessCallback = true;
506 filter->setNonRealtime (false);
507 filter->setPlayConfigDetails (numInChans, numOutChans, sample_rate, buffer_size);
509 deleteTempChannels();
511 filter->prepareToPlay (sample_rate, buffer_size);
513 midiEvents.ensureSize (2048);
514 midiEvents.clear();
518 void do_deactivate()
520 if (filter != nullptr)
522 filter->releaseResources();
524 isProcessing = false;
525 channels.free();
527 deleteTempChannels();
531 void do_run(uint32_t sample_count)
533 if (firstProcessCallback)
535 firstProcessCallback = false;
537 // if this fails, the host hasn't called resume() before processing
538 jassert (isProcessing);
540 // (tragically, some hosts actually need this, although it's stupid to have
541 // to do it here..)
542 if (! isProcessing)
543 do_activate();
545 filter->setNonRealtime (false);
547 #if JUCE_WINDOWS
548 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
549 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST)
550 filter->setNonRealtime (true);
551 #endif
554 // Check if buffer size changed
555 if (buffer_size != sample_count)
557 buffer_size = sample_count;
558 filter->setPlayConfigDetails(numInChans, numOutChans, sample_rate, buffer_size);
559 filter->prepareToPlay(sample_rate, buffer_size);
562 // Check for updated parameters
563 float cur_value;
564 for (int i = 0; i < ports_ctrl.size(); i++)
566 if (ports_ctrl[i] != nullptr)
568 cur_value = *(float*)ports_ctrl[i];
569 if (ports_ctrl_last[i] != cur_value) {
570 filter->setParameter(i, cur_value);
571 ports_ctrl_last.setUnchecked(i, cur_value);
576 jassert (activePlugins.contains (this));
579 const ScopedLock sl (filter->getCallbackLock());
581 const int numIn = numInChans;
582 const int numOut = numOutChans;
584 if (filter->isSuspended())
586 for (int i = 0; i < numOut; ++i)
587 zeromem (ports_aout[i], sizeof (float) * sample_count);
589 else
591 int i;
592 for (i = 0; i < numOut; ++i)
594 float* chan = tempChannels.getUnchecked(i);
596 if (chan == 0)
598 chan = ports_aout[i];
600 // if some output channels are disabled, some hosts supply the same buffer
601 // for multiple channels - this buggers up our method of copying the
602 // inputs over the outputs, so we need to create unique temp buffers in this case..
603 for (int j = i; --j >= 0;)
605 if (ports_aout[j] == chan)
607 chan = new float [buffer_size * 2];
608 tempChannels.set (i, chan);
609 break;
614 if (i < numIn && chan != ports_ain[i])
615 memcpy (chan, ports_ain[i], sizeof (float) * sample_count);
617 channels[i] = chan;
620 // LV2 MIDI Input
621 if (port_min != nullptr)
623 LV2_Event_Iterator iter;
624 lv2_event_begin(&iter, port_min);
626 lv2_event_buffer_reset(port_min, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(port_min + 1));
628 for (uint32_t i=0; i < iter.buf->event_count; ++i) {
629 uint8_t* data;
630 LV2_Event* ev = lv2_event_get(&iter, &data);
631 midiEvents.addEvent(data, ev->size, ev->frames);
632 lv2_event_increment(&iter);
636 for (; i < numIn; ++i)
637 channels[i] = ports_ain[i];
639 AudioSampleBuffer chans (channels, jmax (numIn, numOut), sample_count);
641 filter->processBlock (chans, midiEvents);
645 if (! midiEvents.isEmpty() && port_mout != nullptr)
647 #if JucePlugin_ProducesMidiOutput
648 const int numEvents = midiEvents.getNumEvents();
650 LV2_Event_Iterator iter;
651 lv2_event_buffer_reset(port_mout, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(port_mout + 1));
652 lv2_event_begin(&iter, port_mout);
654 const JUCE_NAMESPACE::uint8* midiEventData;
655 int midiEventSize, midiEventPosition;
656 MidiBuffer::Iterator i (midiEvents);
658 while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
660 jassert (midiEventPosition >= 0 && midiEventPosition < sample_count);
662 lv2_event_write(&iter, midiEventPosition, 0, midi_uri_id, midiEventSize, midiEventData);
664 #endif
665 midiEvents.clear();
669 void do_cleanup()
671 stopTimer();
673 //if (descriptor)
675 // free((void*)descriptor->URI);
676 // delete descriptor;
680 //==============================================================================
681 // JUCE Stuff
683 AudioProcessor* getFilter() { return filter; }
685 void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue)
687 // TODO
690 void audioProcessorChanged (AudioProcessor*)
692 // TODO
695 //==============================================================================
696 int32_t getChunk (void** data)
698 if (filter == nullptr)
699 return 0;
701 chunkMemory.setSize (0);
702 filter->getCurrentProgramStateInformation (chunkMemory);
704 *data = (void*) chunkMemory.getData();
706 // because the chunk is only needed temporarily by the host (or at least you'd
707 // hope so) we'll give it a while and then free it in the timer callback.
708 chunkMemoryTime = JUCE_NAMESPACE::Time::getApproximateMillisecondCounter();
710 return (int32_t) chunkMemory.getSize();
713 void setChunk (void* data, int32_t byteSize)
715 if (filter == nullptr)
716 return;
718 chunkMemory.setSize (0);
719 chunkMemoryTime = 0;
721 if (byteSize > 0 && data != nullptr)
723 filter->setCurrentProgramStateInformation (data, byteSize);
727 void timerCallback()
729 if (chunkMemoryTime > 0 && chunkMemoryTime < JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000)
731 chunkMemoryTime = 0;
732 chunkMemory.setSize (0);
736 void doIdleCallback()
738 if (MessageManager::getInstance()->isThisTheMessageThread())
740 JUCE_AUTORELEASEPOOL
741 juce_callAnyTimersSynchronously();
743 for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
744 ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow();
748 //==============================================================================
749 private:
750 AudioProcessor* filter;
751 JUCE_NAMESPACE::MemoryBlock chunkMemory;
752 JUCE_NAMESPACE::uint32 chunkMemoryTime;
753 MidiBuffer midiEvents;
754 int numInChans, numOutChans;
755 bool isProcessing, firstProcessCallback;
756 HeapBlock<float*> channels;
757 Array<float*> tempChannels; // see note in do_run()
759 const LV2_Descriptor* descriptor;
761 double sample_rate;
762 int buffer_size;
763 uint16_t midi_uri_id;
764 uint32_t port_count;
766 LV2_Event_Buffer* port_min;
767 LV2_Event_Buffer* port_mout;
768 float* ports_ain[JucePlugin_MaxNumInputChannels];
769 float* ports_aout[JucePlugin_MaxNumOutputChannels];
770 Array<float*> ports_ctrl;
771 Array<float> ports_ctrl_last;
773 //==============================================================================
774 void deleteTempChannels()
776 for (int i = tempChannels.size(); --i >= 0;)
777 delete[] (tempChannels.getUnchecked(i));
779 tempChannels.clear();
781 if (filter != nullptr)
782 tempChannels.insertMultiple (0, 0, filter->getNumInputChannels() + filter->getNumOutputChannels());
785 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLV2Wrapper);
788 //==============================================================================
789 // LV2 descriptor functions
790 LV2_Handle juce_lv2_instantiate(const LV2_Descriptor* descriptor, double sample_rate, const char* bundle_path, const LV2_Feature* const* features)
792 JUCE_AUTORELEASEPOOL;
793 initialiseJuce_GUI();
795 #if JUCE_LINUX
796 MessageManagerLock mmLock;
797 #endif
799 JuceLV2Wrapper* wrapper = new JuceLV2Wrapper(descriptor, sample_rate, features);
800 return wrapper;
803 void juce_lv2_connect_port(LV2_Handle instance, uint32_t port, void* data_location)
805 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
806 wrapper->do_connect_port(port, data_location);
809 void juce_lv2_activate(LV2_Handle instance)
811 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
812 wrapper->do_activate();
815 void juce_lv2_run(LV2_Handle instance, uint32_t sample_count)
817 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
818 wrapper->do_run(sample_count);
821 void juce_lv2_deactivate(LV2_Handle instance)
823 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
824 wrapper->do_deactivate();
827 void juce_lv2_cleanup(LV2_Handle instance)
829 JUCE_AUTORELEASEPOOL;
831 #if JUCE_LINUX
832 MessageManagerLock mmLock;
833 #endif
835 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
836 wrapper->do_cleanup();
837 delete wrapper;
839 if (activePlugins.size() == 0)
841 #if JUCE_LINUX
842 SharedMessageThread::deleteInstance();
843 #endif
844 shutdownJuce_GUI();
848 const void* juce_lv2_extension_data(const char* uri)
850 printf("TODO :: juce_lv2_extension_data()\n");
851 return nullptr;
854 //==============================================================================
855 // Create new LV2 objects
856 LV2_Descriptor* getNewLv2Plugin()
858 LV2_Descriptor* const Lv2Plugin = new LV2_Descriptor;
859 Lv2Plugin->URI = strdup((const char*)get_uri().toUTF8());
860 Lv2Plugin->instantiate = juce_lv2_instantiate;
861 Lv2Plugin->connect_port = juce_lv2_connect_port;
862 Lv2Plugin->activate = juce_lv2_activate;
863 Lv2Plugin->run = juce_lv2_run;
864 Lv2Plugin->deactivate = juce_lv2_deactivate;
865 Lv2Plugin->cleanup = juce_lv2_cleanup;
866 Lv2Plugin->extension_data = juce_lv2_extension_data;
867 return Lv2Plugin;
870 //==============================================================================
871 // Mac startup code..
872 #if JUCE_MAC
874 extern "C" __attribute__ ((visibility("default"))) void juce_lv2_ttl_generator()
876 generate_ttl();
879 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
881 initialiseMac();
882 return (index == 0) ? getNewLv2Plugin() : nullptr;
885 //==============================================================================
886 // Linux startup code..
887 #elif JUCE_LINUX
889 extern "C" __attribute__ ((visibility("default"))) void juce_lv2_ttl_generator()
891 generate_ttl();
894 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
896 SharedMessageThread::getInstance();
897 return (index == 0) ? getNewLv2Plugin() : nullptr;
900 // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash!
901 __attribute__((constructor)) void myPluginInit() {}
902 __attribute__((destructor)) void myPluginFini() {}
904 //==============================================================================
905 // Win32 startup code..
906 #else
908 extern "C" __declspec (dllexport) void juce_lv2_ttl_generator()
910 generate_ttl();
913 extern "C" __declspec (dllexport) const LV2_Descriptor* lv2_descriptor(uint32_t index)
915 return (index == 0) ? getNewLv2Plugin() : nullptr;
918 #endif
920 #endif