Misc fixes
[juce-lv2.git] / juce / source / src / audio / plugin_client / LV2 / juce_LV2_Wrapper.cpp
blob571e42506c3032ef78adb26ba3ce5a60ee1c8c83
1 /*
2 * JUCE LV2 wrapper
3 */
5 #include "../juce_IncludeCharacteristics.h"
7 #if JucePlugin_Build_LV2
9 // LV2 includes..
10 #include "lv2/lv2.h"
11 #include "lv2/event.h"
12 #include "lv2/event_helpers.h"
13 #include "lv2/instance_access.h"
14 #include "lv2/uri_map.h"
15 #include "lv2/ui.h"
16 #include "lv2/lv2_external_ui.h"
18 #include "../juce_PluginHeaders.h"
19 #include "../juce_PluginHostType.h"
21 static bool recursionCheck = false;
23 BEGIN_JUCE_NAMESPACE
24 extern void juce_callAnyTimersSynchronously();
25 END_JUCE_NAMESPACE
27 extern AudioProcessor* JUCE_CALLTYPE createPluginFilter();
29 static Array<void*> activePlugins;
31 //==============================================================================
32 // Same as juce_lv2_gen.cpp
33 String get_uri()
35 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":" JucePlugin_VersionString).replace(" ", "_");
38 String get_juce_ui_uri()
40 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":JUCE-Native-UI").replace(" ", "_");
43 String get_external_ui_uri()
45 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":JUCE-External-UI").replace(" ", "_");
48 //==============================================================================
49 // Create a new JUCE LV2 Plugin
50 class JuceLV2Wrapper : private Timer, public AudioProcessorListener
52 public:
53 JuceLV2Wrapper(const LV2_Descriptor* descriptor_, double sample_rate_, const LV2_Feature* const* features) :
54 chunkMemoryTime (0),
55 editor (nullptr),
56 numInChans (JucePlugin_MaxNumInputChannels),
57 numOutChans (JucePlugin_MaxNumOutputChannels),
58 isProcessing (false),
59 hasShutdown (false),
60 firstProcessCallback (true),
61 shouldDeleteEditor (false),
62 descriptor (descriptor_),
63 ui_descriptor (nullptr),
64 ui_controller (nullptr),
65 ui_write_func (nullptr),
66 sample_rate (sample_rate_),
67 buffer_size (512),
68 midi_uri_id (0),
69 port_count (0)
71 printf("JuceLV2Wrapper()\n");
72 filter = createPluginFilter();
73 filter->setPlayConfigDetails(numInChans, numOutChans, 0, 0);
74 filter->addListener (this);
76 // Port count
77 #if JucePlugin_WantsMidiInput
78 port_count += 1;
79 #endif
80 #if JucePlugin_ProducesMidiOutput
81 port_count += 1;
82 #endif
83 port_count += numInChans;
84 port_count += numOutChans;
85 port_count += filter->getNumParameters();
87 // Set Port data
88 port_min = nullptr;
89 port_mout = nullptr;
90 ports_ctrl.insertMultiple(0, nullptr, filter->getNumParameters());
91 ports_ctrl_last.insertMultiple(0, 0.0f, filter->getNumParameters());
93 for (int i=0; i < numInChans; i++) {
94 ports_ain[i] = nullptr;
97 for (int i=0; i < numOutChans; i++) {
98 ports_aout[i] = nullptr;
101 for (int i=0; i < filter->getNumParameters(); i++) {
102 ports_ctrl_last.set(i, filter->getParameter(i));
105 // Get MIDI URI Id
106 uint16_t j = 0;
107 while(features[j]) {
108 if (strcmp(features[j]->URI, LV2_URI_MAP_URI) == 0) {
109 LV2_URI_Map_Feature* uri_feature = (LV2_URI_Map_Feature*)features[j]->data;
110 midi_uri_id = uri_feature->uri_to_id(uri_feature->callback_data, LV2_EVENT_URI, "http://lv2plug.in/ns/ext/midi#MidiEvent");
111 break;
113 j++;
116 activePlugins.add (this);
119 ~JuceLV2Wrapper()
121 JUCE_AUTORELEASEPOOL
124 #if JUCE_LINUX
125 MessageManagerLock mmLock;
126 #endif
127 stopTimer();
128 deleteEditor (false);
130 hasShutdown = true;
132 delete filter;
133 filter = 0;
135 jassert (editor == 0);
137 channels.free();
138 deleteTempChannels();
140 ports_ctrl.clear();
141 ports_ctrl_last.clear();
143 jassert (activePlugins.contains (this));
144 activePlugins.removeValue (this);
147 if (activePlugins.size() == 0)
149 shutdownJuce_GUI();
153 //==============================================================================
154 // LV2 Descriptor Calls
155 void do_connect_port(uint32_t port, void* data_location)
157 if (port < port_count) {
158 int i;
159 uint32_t index = 0;
161 #if JucePlugin_WantsMidiInput
162 if (port == index) {
163 port_min = data_location;
164 return;
166 index += 1;
167 #endif
169 #if JucePlugin_ProducesMidiOutput
170 if (port == index) {
171 port_mout = data_location;
172 return;
174 index += 1;
175 #endif
177 for (i=0; i < numInChans; i++) {
178 if (port == index) {
179 ports_ain[i] = (float*)data_location;
180 return;
182 index += 1;
185 for (i=0; i < numOutChans; i++) {
186 if (port == index) {
187 ports_aout[i] = (float*)data_location;
188 return;
190 index += 1;
193 for (i=0; i < filter->getNumParameters(); i++) {
194 if (port == index) {
195 ports_ctrl.set(i, (float*)data_location);
196 return;
198 index += 1;
203 void do_activate()
205 if (filter != nullptr) {
206 isProcessing = true;
207 channels.calloc (numInChans + numOutChans);
209 jassert (sample_rate > 0);
210 if (sample_rate <= 0.0)
211 sample_rate = 44100.0;
213 jassert (buffer_size > 0);
215 firstProcessCallback = true;
217 filter->setNonRealtime (false);
218 filter->setPlayConfigDetails (numInChans, numOutChans, sample_rate, buffer_size);
220 deleteTempChannels();
222 filter->prepareToPlay (sample_rate, buffer_size);
224 midiEvents.ensureSize (2048);
225 midiEvents.clear();
229 void do_run(uint32_t sample_count)
231 if (firstProcessCallback)
233 firstProcessCallback = false;
235 // if this fails, the host hasn't called resume() before processing
236 jassert (isProcessing);
238 // (tragically, some hosts actually need this, although it's stupid to have
239 // to do it here..)
240 if (! isProcessing)
241 do_activate();
243 filter->setNonRealtime (false);
245 #if JUCE_WINDOWS
246 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
247 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST)
248 filter->setNonRealtime (true);
249 #endif
252 // Change if buffer size changed
253 if (buffer_size != sample_count) {
254 buffer_size = sample_count;
255 filter->setPlayConfigDetails(numInChans, numOutChans, sample_rate, buffer_size);
256 filter->prepareToPlay(sample_rate, buffer_size);
259 // Check for updated parameters
260 float cur_value;
261 for (int i = 0; i < ports_ctrl.size(); i++) {
262 if (ports_ctrl[i] != nullptr) {
263 cur_value = *(float*)ports_ctrl[i];
264 if (ports_ctrl_last[i] != cur_value) {
265 filter->setParameter(i, cur_value);
266 ports_ctrl_last.setUnchecked(i, cur_value);
271 jassert (activePlugins.contains (this));
274 const ScopedLock sl (filter->getCallbackLock());
276 const int numIn = numInChans;
277 const int numOut = numOutChans;
279 if (filter->isSuspended())
281 for (int i = 0; i < numOut; ++i)
282 zeromem (ports_aout[i], sizeof (float) * sample_count);
284 else
286 int i;
287 for (i = 0; i < numOut; ++i)
289 float* chan = tempChannels.getUnchecked(i);
291 if (chan == 0)
293 chan = ports_aout[i];
295 // if some output channels are disabled, some hosts supply the same buffer
296 // for multiple channels - this buggers up our method of copying the
297 // inputs over the outputs, so we need to create unique temp buffers in this case..
298 for (int j = i; --j >= 0;)
300 if (ports_aout[j] == chan)
302 chan = new float [buffer_size * 2];
303 tempChannels.set (i, chan);
304 break;
309 if (i < numIn && chan != ports_ain[i])
310 memcpy (chan, ports_ain[i], sizeof (float) * sample_count);
312 channels[i] = chan;
315 // TODO - MIDI Input
317 for (; i < numIn; ++i)
318 channels[i] = ports_ain[i];
320 AudioSampleBuffer chans (channels, jmax (numIn, numOut), sample_count);
322 filter->processBlock (chans, midiEvents);
326 if (! midiEvents.isEmpty())
328 // TODO - MIDI Output
329 midiEvents.clear();
333 void do_deactivate()
335 if (filter != nullptr)
337 filter->releaseResources();
339 isProcessing = false;
340 channels.free();
342 deleteTempChannels();
346 //==============================================================================
347 static void do_ui_external_run(lv2_external_ui* _this_)
349 printf("do_ui_external_run()\n");
350 //doIdleCallback();
354 static void do_ui_external_show(lv2_external_ui* _this_)
356 printf("do_ui_external_show()\n");
357 //editorComp->setVisible(true);
361 static void do_ui_external_hide(lv2_external_ui* _this_)
363 printf("do_ui_external_hide()\n");
364 //editorComp->setVisible(false);
367 void do_ui_instantiate(const char* UiURI, LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features)
369 ui_write_func = write_function;
370 ui_controller = controller;
372 initialiseJuce_GUI();
374 //checkWhetherMessageThreadIsCorrect();
375 //const MessageManagerLock mmLock;
376 //jassert (! recursionCheck);
378 //startTimer (1000 / 4); // performs misc housekeeping chores
380 deleteEditor (true);
381 createEditor();
383 if (get_external_ui_uri().compare(UiURI) == 0)
385 printf("External UI starting...\n");
387 ui_external_host = nullptr;
389 uint16_t i = 0;
390 while(features[i]) {
391 if (strcmp(features[i]->URI, LV2_EXTERNAL_UI_URI) == 0) {
392 ui_external_host = (lv2_external_ui_host*)features[i]->data;
393 printf("Got external UI host data\n");
394 break;
396 i++;
399 ui_external_widget.run = do_ui_external_run;
400 ui_external_widget.show = do_ui_external_show;
401 ui_external_widget.hide = do_ui_external_hide;
403 *widget = &ui_external_widget;
405 printf("External UI initiated\n");
407 else // JUCE UI
409 printf("JUCE UI starting...\n");
410 *widget = editor;
415 void do_ui_cleanup()
417 // TODO
420 void do_ui_port_event(uint32_t port_index, float value)
422 //int index =
423 //if (port_index < filter->getNumParameters())
424 // filter->setParameter(port_index, value);
427 // TODO - set/get chunk
429 //==============================================================================
430 // JUCE Stuff
431 void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue)
433 //int ctrl_pad = 0;
434 //if (ui_controller && ui_write_func)
435 // ui_write_func(ui_controller, ctrl_pad + index, sizeof(float), 0, &newValue);
438 void audioProcessorChanged (AudioProcessor*)
440 //updateDisplay();
443 void timerCallback()
445 if (shouldDeleteEditor)
447 shouldDeleteEditor = false;
448 deleteEditor (true);
451 if (chunkMemoryTime > 0
452 && chunkMemoryTime < JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000
453 && ! recursionCheck)
455 chunkMemoryTime = 0;
456 chunkMemory.setSize (0);
460 void doIdleCallback()
462 // (wavelab calls this on a separate thread and causes a deadlock)..
463 if (MessageManager::getInstance()->isThisTheMessageThread()
464 && ! recursionCheck)
466 recursionCheck = true;
468 JUCE_AUTORELEASEPOOL
469 juce_callAnyTimersSynchronously();
471 for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
472 ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow();
474 recursionCheck = false;
478 void createEditor()
480 printf("createEditor()\n");
482 if (hasShutdown || filter == nullptr)
483 return;
485 if (editor == nullptr)
487 printf("HERE 001\n");
488 editor = filter->createEditorIfNeeded();
489 printf("HERE 002\n");
491 if (editor != nullptr)
493 printf("HERE 003\n");
494 editor->setOpaque (true);
495 editor->setVisible (true);
496 printf("HERE 005\n");
498 printf("HERE 006\n");
501 shouldDeleteEditor = false;
502 printf("HERE 007\n");
505 void deleteEditor (bool canDeleteLaterIfModal)
507 JUCE_AUTORELEASEPOOL
508 PopupMenu::dismissAllActiveMenus();
510 jassert (! recursionCheck);
511 recursionCheck = true;
513 if (editor != nullptr)
515 Component* const modalComponent = Component::getCurrentlyModalComponent();
516 if (modalComponent != nullptr)
518 modalComponent->exitModalState (0);
520 if (canDeleteLaterIfModal)
522 shouldDeleteEditor = true;
523 recursionCheck = false;
524 return;
528 filter->editorBeingDeleted (editor);
530 // there's some kind of component currently modal, but the host
531 // is trying to delete our plugin. You should try to avoid this happening..
532 jassert (Component::getCurrentlyModalComponent() == nullptr);
535 recursionCheck = false;
538 //==============================================================================
539 private:
540 AudioProcessor* filter;
541 JUCE_NAMESPACE::MemoryBlock chunkMemory;
542 JUCE_NAMESPACE::uint32 chunkMemoryTime;
543 AudioProcessorEditor* editor;
544 MidiBuffer midiEvents;
545 int numInChans, numOutChans;
546 bool isProcessing, hasShutdown, firstProcessCallback, shouldDeleteEditor;
547 HeapBlock<float*> channels;
548 Array<float*> tempChannels; // see note in do_run()
550 const LV2_Descriptor* descriptor;
551 const LV2UI_Descriptor* ui_descriptor;
552 LV2UI_Controller ui_controller;
553 LV2UI_Write_Function ui_write_func;
554 lv2_external_ui ui_external_widget;
555 lv2_external_ui_host* ui_external_host;
557 double sample_rate;
558 int buffer_size;
559 uint16_t midi_uri_id;
560 uint32_t port_count;
562 void* port_min;
563 void* port_mout;
564 float* ports_ain[JucePlugin_MaxNumInputChannels];
565 float* ports_aout[JucePlugin_MaxNumOutputChannels];
566 Array<float*> ports_ctrl;
567 Array<float> ports_ctrl_last;
569 //==============================================================================
570 void deleteTempChannels()
572 for (int i = tempChannels.size(); --i >= 0;)
573 delete[] (tempChannels.getUnchecked(i));
575 tempChannels.clear();
577 if (filter != nullptr)
578 tempChannels.insertMultiple (0, 0, filter->getNumInputChannels() + filter->getNumOutputChannels());
581 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLV2Wrapper);
584 //==============================================================================
585 // LV2 descriptor functions
586 LV2_Handle juce_lv2_instantiate(const LV2_Descriptor* descriptor, double sample_rate, const char* bundle_path, const LV2_Feature* const* features)
588 JuceLV2Wrapper* wrapper = new JuceLV2Wrapper(descriptor, sample_rate, features);
589 return wrapper;
592 void juce_lv2_connect_port(LV2_Handle instance, uint32_t port, void* data_location)
594 if (!instance) return;
595 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
596 wrapper->do_connect_port(port, data_location);
599 void juce_lv2_activate(LV2_Handle instance)
601 if (!instance) return;
602 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
603 wrapper->do_activate();
606 void juce_lv2_run(LV2_Handle instance, uint32_t sample_count)
608 if (!instance) return;
609 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
610 wrapper->do_run(sample_count);
613 void juce_lv2_deactivate(LV2_Handle instance)
615 if (!instance) return;
616 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
617 wrapper->do_deactivate();
620 void juce_lv2_cleanup(LV2_Handle instance)
622 if (!instance) return;
623 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
624 //free((void*)wrapper->descriptor->URI);
625 //delete wrapper->descriptor;
626 delete wrapper;
629 const void* juce_lv2_extension_data(const char* uri)
631 printf("juce_lv2_extension_data()\n");
632 return nullptr;
635 LV2UI_Handle juce_lv2ui_instantiate(const LV2UI_Descriptor* descriptor, const char* plugin_uri, const char* bundle_path, LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features)
637 printf("ui_instantiate()\n");
638 uint16_t i = 0;
639 while (features[i])
641 if (strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0 && features[i]->data) {
642 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)features[i]->data;
643 wrapper->do_ui_instantiate(descriptor->URI, write_function, controller, widget, features);
644 return wrapper;
646 i++;
648 printf("Host does not support instance data - cannot use UI\n");
649 return nullptr;
652 void juce_lv2ui_cleanup(LV2UI_Handle instance)
654 printf("ui_cleanup()\n");
655 if (!instance) return;
656 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
657 wrapper->do_ui_cleanup();
660 void juce_lv2ui_port_event(LV2UI_Handle instance, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void* buffer)
662 printf("ui_port_event()\n");
663 if (!instance) return;
664 if (buffer_size != sizeof(float) || format != 0) return;
665 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
666 float value = *(float*)buffer;
667 wrapper->do_ui_port_event(port_index, value);
670 //==============================================================================
671 // Create new LV2 objects
672 LV2_Descriptor* getNewLv2Plugin()
674 LV2_Descriptor* const Lv2Plugin = new LV2_Descriptor;
675 Lv2Plugin->URI = strdup((const char*)get_uri().toUTF8());
676 Lv2Plugin->instantiate = juce_lv2_instantiate;
677 Lv2Plugin->connect_port = juce_lv2_connect_port;
678 Lv2Plugin->activate = juce_lv2_activate;
679 Lv2Plugin->run = juce_lv2_run;
680 Lv2Plugin->deactivate = juce_lv2_deactivate;
681 Lv2Plugin->cleanup = juce_lv2_cleanup;
682 Lv2Plugin->extension_data = juce_lv2_extension_data;
683 return Lv2Plugin;
686 LV2UI_Descriptor* getNewLv2UI(bool external)
688 LV2UI_Descriptor* Lv2UI = new LV2UI_Descriptor;
689 Lv2UI->URI = strdup((const char*) (external ? get_external_ui_uri() : get_juce_ui_uri()).toUTF8());
690 Lv2UI->instantiate = juce_lv2ui_instantiate;
691 Lv2UI->cleanup = juce_lv2ui_cleanup;
692 Lv2UI->port_event = juce_lv2ui_port_event;
693 Lv2UI->extension_data = juce_lv2_extension_data;
694 return Lv2UI;
697 //==============================================================================
698 // Mac startup code..
699 #if JUCE_MAC
701 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
703 initialiseMac();
704 return (index == 0) ? getNewLv2Plugin() : nullptr;
707 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
709 initialiseMac();
710 // 0 -> External UI; 1 -> JUCE UI
711 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
714 //==============================================================================
715 // Linux startup code..
716 #elif JUCE_LINUX
718 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
720 return (index == 0) ? getNewLv2Plugin() : nullptr;
723 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
725 // 0 -> External UI; 1 -> JUCE UI
726 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
729 // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash!
730 __attribute__((constructor)) void myPluginInit() {}
731 __attribute__((destructor)) void myPluginFini() {}
733 //==============================================================================
734 // Win32 startup code..
735 #else
737 extern "C" __declspec (dllexport) const LV2_Descriptor* lv2_descriptor(uint32_t index)
739 return (index == 0) ? getNewLv2Plugin() : nullptr;
742 extern "C" __declspec (dllexport) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
744 // 0 -> External UI; 1 -> JUCE UI
745 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
748 #endif
750 #endif