Use both internal and external UIs
[juce-lv2.git] / juce / source / src / audio / plugin_client / LV2 / juce_LV2_Wrapper.cpp
blob1d3b688a488dd60c9e4409fcc8e6100c01837d44
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 plugin += "<" + get_juce_ui_uri() + ">\n";
162 plugin += " a lv2ui:JUCEUI ;\n";
163 plugin += " lv2ui:binary <" + Binary + ".so> .\n";
164 plugin += "<" + get_external_ui_uri() + ">\n";
165 plugin += " a lv2ui:external ;\n";
166 plugin += " lv2ui:binary <" + Binary + ".so> .\n";
167 plugin += "\n";
170 plugin += "<" + URI + ">\n";
171 plugin += " a " + get_plugin_type() + " ;\n";
173 if (filter->hasEditor()) {
174 plugin += " lv2ui:ui <" + get_juce_ui_uri() + ">,\n";
175 plugin += " <" + get_external_ui_uri() + ">;\n";
178 plugin += "\n";
180 #if JucePlugin_WantsMidiInput
181 plugin += " lv2:port [\n";
182 plugin += " a lv2:InputPort, lv2ev:EventPort;\n";
183 plugin += " lv2ev:supportsEvent <http://lv2plug.in/ns/ext/midi#MidiEvent>;\n";
184 plugin += " lv2:index " + String(port_index++) + ";\n";
185 plugin += " lv2:symbol \"midi_in\";\n";
186 plugin += " lv2:name \"MIDI Input\";\n";
187 plugin += " ] ;\n\n";
188 #endif
190 #if JucePlugin_ProducesMidiOutput
191 plugin += " lv2:port [\n";
192 plugin += " a lv2:OutputPort, lv2ev:EventPort;\n";
193 plugin += " lv2ev:supportsEvent <http://lv2plug.in/ns/ext/midi#MidiEvent>;\n";
194 plugin += " lv2:index " + String(port_index++) + ";\n";
195 plugin += " lv2:symbol \"midi_out\";\n";
196 plugin += " lv2:name \"MIDI Output\";\n";
197 plugin += " ] ;\n\n";
198 #endif
200 for (i=0; i<JucePlugin_MaxNumInputChannels; i++) {
201 if (i == 0) {
202 plugin += " lv2:port [\n";
203 } else {
204 plugin += " [\n";
207 plugin += " a lv2:InputPort, lv2:AudioPort;\n";
208 //plugin += " lv2:datatype lv2:float;\n";
209 plugin += " lv2:index " + String(port_index++) + ";\n";
210 plugin += " lv2:symbol \"audio_in_" + String(i+1) + "\";\n";
211 plugin += " lv2:name \"Audio Input " + String(i+1) + "\";\n";
213 if (i == JucePlugin_MaxNumInputChannels-1) {
214 plugin += " ] ;\n\n";
215 } else {
216 plugin += " ],\n";
220 for (i=0; i<JucePlugin_MaxNumOutputChannels; i++) {
221 if (i == 0) {
222 plugin += " lv2:port [\n";
223 } else {
224 plugin += " [\n";
227 plugin += " a lv2:OutputPort, lv2:AudioPort;\n";
228 //plugin += " lv2:datatype lv2:float;\n";
229 plugin += " lv2:index " + String(port_index++) + ";\n";
230 plugin += " lv2:symbol \"audio_out_" + String(i+1) + "\";\n";
231 plugin += " lv2:name \"Audio Output " + String(i+1) + "\";\n";
233 if (i == JucePlugin_MaxNumOutputChannels-1) {
234 plugin += " ] ;\n\n";
235 } else {
236 plugin += " ],\n";
240 for (i=0; i < filter->getNumParameters(); i++) {
241 if (i == 0) {
242 plugin += " lv2:port [\n";
243 } else {
244 plugin += " [\n";
247 plugin += " a lv2:InputPort;\n";
248 plugin += " a lv2:ControlPort;\n";
249 //plugin += " lv2:datatype lv2:float;\n";
250 plugin += " lv2:index " + String(port_index++) + ";\n";
251 plugin += " lv2:symbol \"" + name_to_symbol(filter->getParameterName(i), i) + "\";\n";
252 plugin += " lv2:name \"" + filter->getParameterName(i) + "\";\n";
253 plugin += " lv2:default " + float_to_string(filter->getParameter(i)) + ";\n";
254 plugin += " lv2:minimum 0.0;\n";
255 plugin += " lv2:maximum 1.0;\n";
257 if (i == filter->getNumParameters()-1) {
258 plugin += " ] ;\n\n";
259 } else {
260 plugin += " ],\n";
264 plugin += " doap:name \"" + String(JucePlugin_Name) + "\" ;\n";
265 plugin += " doap:creator \"" + String(JucePlugin_Manufacturer) + "\" .\n";
267 delete filter;
268 return plugin;
271 void generate_ttl()
273 String URI = get_uri();
274 String Binary = get_binary_name();
275 String BinaryTTL = Binary + ".ttl";
277 std::cout << "Writing manifest.ttl...";
278 std::fstream manifest("manifest.ttl", std::ios::out);
279 manifest << get_manifest_ttl(URI, Binary) << std::endl;
280 manifest.close();
281 std::cout << " done!" << std::endl;
283 std::cout << "Writing " << BinaryTTL << "...";
284 std::fstream plugin(BinaryTTL.toUTF8(), std::ios::out);
285 plugin << get_plugin_ttl(URI, Binary) << std::endl;
286 plugin.close();
287 std::cout << " done!" << std::endl;
290 //==============================================================================
291 BEGIN_JUCE_NAMESPACE
292 //extern void juce_callAnyTimersSynchronously();
294 #if JUCE_MAC
295 extern void initialiseMac();
296 #endif
297 END_JUCE_NAMESPACE
299 //==============================================================================
300 #if JUCE_LINUX
302 class SharedMessageThread : public Thread
304 public:
305 SharedMessageThread()
306 : Thread ("Lv2MessageThread"),
307 initialised (false)
309 startThread (7);
311 while (! initialised)
312 sleep (1);
315 ~SharedMessageThread()
317 signalThreadShouldExit();
318 JUCEApplication::quit();
319 waitForThreadToExit (5000);
320 clearSingletonInstance();
323 void run()
325 initialiseJuce_GUI();
326 initialised = true;
328 MessageManager::getInstance()->setCurrentThreadAsMessageThread();
330 while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250))
334 juce_DeclareSingleton (SharedMessageThread, false);
336 private:
337 bool initialised;
340 juce_ImplementSingleton (SharedMessageThread)
342 #endif
344 static Array<void*> activePlugins;
346 //==============================================================================
347 // Create a new JUCE LV2 Plugin
348 class JuceLV2Wrapper : private Timer
350 public:
351 JuceLV2Wrapper(const LV2_Descriptor* descriptor_, double sample_rate_, const LV2_Feature* const* features) :
352 chunkMemoryTime (0),
353 numInChans (JucePlugin_MaxNumInputChannels),
354 numOutChans (JucePlugin_MaxNumOutputChannels),
355 isProcessing (false),
356 firstProcessCallback (true),
357 descriptor (descriptor_),
358 sample_rate (sample_rate_),
359 buffer_size (512),
360 midi_uri_id (0),
361 port_count (0)
363 printf("JuceLV2Wrapper()\n");
365 JUCE_AUTORELEASEPOOL;
366 initialiseJuce_GUI();
368 #if JUCE_LINUX
369 MessageManagerLock mmLock;
370 #endif
372 filter = createPluginFilter();
373 jassert(filter != nullptr);
375 filter->setPlayConfigDetails(numInChans, numOutChans, 0, 0);
377 // Port count
378 #if JucePlugin_WantsMidiInput
379 port_count += 1;
380 #endif
381 #if JucePlugin_ProducesMidiOutput
382 port_count += 1;
383 #endif
384 port_count += numInChans;
385 port_count += numOutChans;
386 port_count += filter->getNumParameters();
388 // Set Port data
389 port_min = nullptr;
390 port_mout = nullptr;
391 ports_ctrl.insertMultiple(0, nullptr, filter->getNumParameters());
392 ports_ctrl_last.insertMultiple(0, 0.0f, filter->getNumParameters());
394 for (int i=0; i < numInChans; i++) {
395 ports_ain[i] = nullptr;
398 for (int i=0; i < numOutChans; i++) {
399 ports_aout[i] = nullptr;
402 for (int i=0; i < filter->getNumParameters(); i++) {
403 ports_ctrl_last.set(i, filter->getParameter(i));
406 // Get MIDI URI Id
407 for (uint16_t j = 0; features[j]; j++)
409 if (strcmp(features[j]->URI, LV2_URI_MAP_URI) == 0)
411 LV2_URI_Map_Feature* uri_feature = (LV2_URI_Map_Feature*)features[j]->data;
412 midi_uri_id = uri_feature->uri_to_id(uri_feature->callback_data, LV2_EVENT_URI, "http://lv2plug.in/ns/ext/midi#MidiEvent");
413 break;
417 activePlugins.add (this);
419 startTimer(1000);
422 ~JuceLV2Wrapper()
424 JUCE_AUTORELEASEPOOL
427 #if JUCE_LINUX
428 MessageManagerLock mmLock;
429 #endif
430 stopTimer();
432 delete filter;
433 filter = nullptr;
435 channels.free();
436 deleteTempChannels();
438 ports_ctrl.clear();
439 ports_ctrl_last.clear();
441 jassert (activePlugins.contains (this));
442 activePlugins.removeValue (this);
445 if (activePlugins.size() == 0)
447 #if JUCE_LINUX
448 SharedMessageThread::deleteInstance();
449 #endif
450 shutdownJuce_GUI();
454 //==============================================================================
455 // LV2 Descriptor Calls
456 void do_connect_port(uint32_t port, void* data_location)
458 if (port < port_count)
460 int i;
461 uint32_t index = 0;
463 #if JucePlugin_WantsMidiInput
464 if (port == index) {
465 port_min = (LV2_Event_Buffer*)data_location;
466 return;
468 index += 1;
469 #endif
471 #if JucePlugin_ProducesMidiOutput
472 if (port == index) {
473 port_mout = (LV2_Event_Buffer*)data_location;
474 return;
476 index += 1;
477 #endif
479 for (i=0; i < numInChans; i++) {
480 if (port == index) {
481 ports_ain[i] = (float*)data_location;
482 return;
484 index += 1;
487 for (i=0; i < numOutChans; i++) {
488 if (port == index) {
489 ports_aout[i] = (float*)data_location;
490 return;
492 index += 1;
495 for (i=0; i < filter->getNumParameters(); i++) {
496 if (port == index) {
497 ports_ctrl.set(i, (float*)data_location);
498 return;
500 index += 1;
505 void do_activate()
507 if (filter != nullptr)
509 isProcessing = true;
510 channels.calloc (numInChans + numOutChans);
512 jassert (sample_rate > 0);
513 if (sample_rate <= 0.0)
514 sample_rate = 44100.0;
516 jassert (buffer_size > 0);
518 firstProcessCallback = true;
520 filter->setNonRealtime (false);
521 filter->setPlayConfigDetails (numInChans, numOutChans, sample_rate, buffer_size);
523 deleteTempChannels();
525 filter->prepareToPlay (sample_rate, buffer_size);
527 midiEvents.ensureSize (2048);
528 midiEvents.clear();
532 void do_deactivate()
534 if (filter != nullptr)
536 filter->releaseResources();
538 isProcessing = false;
539 channels.free();
541 deleteTempChannels();
545 void do_run(uint32_t sample_count)
547 if (firstProcessCallback)
549 firstProcessCallback = false;
551 // if this fails, the host hasn't called resume() before processing
552 jassert (isProcessing);
554 // (tragically, some hosts actually need this, although it's stupid to have
555 // to do it here..)
556 if (! isProcessing)
557 do_activate();
559 filter->setNonRealtime (false);
561 #if JUCE_WINDOWS
562 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
563 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST)
564 filter->setNonRealtime (true);
565 #endif
568 // Check if buffer size changed
569 if (buffer_size != sample_count)
571 buffer_size = sample_count;
572 filter->setPlayConfigDetails(numInChans, numOutChans, sample_rate, buffer_size);
573 filter->prepareToPlay(sample_rate, buffer_size);
576 // Check for updated parameters
577 float cur_value;
578 for (int i = 0; i < ports_ctrl.size(); i++)
580 if (ports_ctrl[i] != nullptr)
582 cur_value = *(float*)ports_ctrl[i];
583 if (ports_ctrl_last[i] != cur_value) {
584 filter->setParameter(i, cur_value);
585 ports_ctrl_last.setUnchecked(i, cur_value);
590 jassert (activePlugins.contains (this));
593 const ScopedLock sl (filter->getCallbackLock());
595 const int numIn = numInChans;
596 const int numOut = numOutChans;
598 if (filter->isSuspended())
600 for (int i = 0; i < numOut; ++i)
601 zeromem (ports_aout[i], sizeof (float) * sample_count);
603 else
605 int i;
606 for (i = 0; i < numOut; ++i)
608 float* chan = tempChannels.getUnchecked(i);
610 if (chan == 0)
612 chan = ports_aout[i];
614 // if some output channels are disabled, some hosts supply the same buffer
615 // for multiple channels - this buggers up our method of copying the
616 // inputs over the outputs, so we need to create unique temp buffers in this case..
617 for (int j = i; --j >= 0;)
619 if (ports_aout[j] == chan)
621 chan = new float [buffer_size * 2];
622 tempChannels.set (i, chan);
623 break;
628 if (i < numIn && chan != ports_ain[i])
629 memcpy (chan, ports_ain[i], sizeof (float) * sample_count);
631 channels[i] = chan;
634 // LV2 MIDI Input
635 if (port_min != nullptr)
637 LV2_Event_Iterator iter;
638 lv2_event_begin(&iter, port_min);
640 lv2_event_buffer_reset(port_min, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(port_min + 1));
642 for (uint32_t i=0; i < iter.buf->event_count; ++i) {
643 uint8_t* data;
644 LV2_Event* ev = lv2_event_get(&iter, &data);
645 midiEvents.addEvent(data, ev->size, ev->frames);
646 lv2_event_increment(&iter);
650 for (; i < numIn; ++i)
651 channels[i] = ports_ain[i];
653 AudioSampleBuffer chans (channels, jmax (numIn, numOut), sample_count);
655 filter->processBlock (chans, midiEvents);
659 if (! midiEvents.isEmpty() && port_mout != nullptr)
661 #if JucePlugin_ProducesMidiOutput
662 const int numEvents = midiEvents.getNumEvents();
664 LV2_Event_Iterator iter;
665 lv2_event_buffer_reset(port_mout, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(port_mout + 1));
666 lv2_event_begin(&iter, port_mout);
668 const JUCE_NAMESPACE::uint8* midiEventData;
669 int midiEventSize, midiEventPosition;
670 MidiBuffer::Iterator i (midiEvents);
672 while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
674 jassert (midiEventPosition >= 0 && midiEventPosition < sample_count);
676 lv2_event_write(&iter, midiEventPosition, 0, midi_uri_id, midiEventSize, midiEventData);
678 #endif
679 midiEvents.clear();
683 void do_cleanup()
685 stopTimer();
687 #if 0
688 if (descriptor)
690 free((void*)descriptor->URI);
691 delete descriptor;
693 #endif
696 //==============================================================================
697 // JUCE Stuff
699 AudioProcessor* getFilter() { return filter; }
701 //==============================================================================
702 int32_t getChunk (void** data)
704 if (filter == nullptr)
705 return 0;
707 chunkMemory.setSize (0);
708 filter->getCurrentProgramStateInformation (chunkMemory);
710 *data = (void*) chunkMemory.getData();
712 // because the chunk is only needed temporarily by the host (or at least you'd
713 // hope so) we'll give it a while and then free it in the timer callback.
714 chunkMemoryTime = JUCE_NAMESPACE::Time::getApproximateMillisecondCounter();
716 return (int32_t) chunkMemory.getSize();
719 void setChunk (void* data, int32_t byteSize)
721 if (filter == nullptr)
722 return;
724 chunkMemory.setSize (0);
725 chunkMemoryTime = 0;
727 if (byteSize > 0 && data != nullptr)
729 filter->setCurrentProgramStateInformation (data, byteSize);
733 void timerCallback()
735 if (chunkMemoryTime > 0 && chunkMemoryTime < JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000)
737 chunkMemoryTime = 0;
738 chunkMemory.setSize (0);
742 //==============================================================================
743 private:
744 AudioProcessor* filter;
745 JUCE_NAMESPACE::MemoryBlock chunkMemory;
746 JUCE_NAMESPACE::uint32 chunkMemoryTime;
747 MidiBuffer midiEvents;
748 int numInChans, numOutChans;
749 bool isProcessing, firstProcessCallback;
750 HeapBlock<float*> channels;
751 Array<float*> tempChannels; // see note in do_run()
753 const LV2_Descriptor* descriptor;
755 double sample_rate;
756 int buffer_size;
757 uint16_t midi_uri_id;
758 uint32_t port_count;
760 LV2_Event_Buffer* port_min;
761 LV2_Event_Buffer* port_mout;
762 float* ports_ain[JucePlugin_MaxNumInputChannels];
763 float* ports_aout[JucePlugin_MaxNumOutputChannels];
764 Array<float*> ports_ctrl;
765 Array<float> ports_ctrl_last;
767 //==============================================================================
768 void deleteTempChannels()
770 for (int i = tempChannels.size(); --i >= 0;)
771 delete[] (tempChannels.getUnchecked(i));
773 tempChannels.clear();
775 if (filter != nullptr)
776 tempChannels.insertMultiple (0, 0, filter->getNumInputChannels() + filter->getNumOutputChannels());
779 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLV2Wrapper);
782 class JuceLv2ExternalUI;
783 static Array<JuceLv2ExternalUI*> activeExternalUIs;
785 enum JUCE_EXTERNAL_UI_Flags {
786 JUCE_EXTERNAL_UI_NONE = 0,
787 JUCE_EXTERNAL_UI_RUN = 0x1,
788 JUCE_EXTERNAL_UI_SHOW = 0x2,
789 JUCE_EXTERNAL_UI_HIDE = 0x4
792 //==============================================================================
793 // Create a new JUCE External UI
794 class JuceLv2ExternalUI : private Timer,
795 public DocumentWindow
797 public:
798 JuceLv2ExternalUI (JuceLV2Wrapper* const wrapper_, AudioProcessorEditor* const editor_, lv2_external_ui_host* external_ui_host_, LV2UI_Controller controller_) :
799 DocumentWindow("", Colours::white, DocumentWindow::minimiseButton | DocumentWindow::closeButton, true),
800 wrapper(wrapper_),
801 editor(editor_),
802 external_ui_host(external_ui_host_),
803 controller(controller_),
804 do_ui_flags(JUCE_EXTERNAL_UI_NONE)
806 setOpaque (true);
807 editor->setOpaque (true);
809 String title = external_ui_host->plugin_human_id != nullptr ? String(external_ui_host->plugin_human_id) : wrapper->getFilter()->getName();
810 setName(title);
812 setTitleBarHeight (0);
813 setDropShadowEnabled(false);
814 setUsingNativeTitleBar(true);
816 setContentNonOwned(editor, true);
818 external_ui.run = do_external_ui_run;
819 external_ui.show = do_external_ui_show;
820 external_ui.hide = do_external_ui_hide;
822 activeExternalUIs.add(this);
824 startTimer(200);
827 ~JuceLv2ExternalUI()
829 activeExternalUIs.removeValue(this);
832 AudioProcessorEditor* getEditorComp() const
834 return dynamic_cast <AudioProcessorEditor*> (getContentComponent());
837 void closeButtonPressed()
839 delete this;
840 external_ui_host->ui_closed(controller);
843 void timerCallback()
845 if (do_ui_flags & JUCE_EXTERNAL_UI_SHOW)
846 setVisible(true);
848 if (do_ui_flags & JUCE_EXTERNAL_UI_HIDE)
849 setVisible(false);
851 if (do_ui_flags & JUCE_EXTERNAL_UI_RUN)
853 editor->repaint();
854 repaint();
857 do_ui_flags = JUCE_EXTERNAL_UI_NONE;
860 //==============================================================================
861 static void do_external_ui_run(lv2_external_ui* _this_)
863 for (int i = 0; i < activeExternalUIs.size(); i++)
865 JuceLv2ExternalUI* Lv2UI = (JuceLv2ExternalUI*)activeExternalUIs.getUnchecked(i);
866 if (Lv2UI->getLv2Widget() == _this_)
868 Lv2UI->setUiFlag(JUCE_EXTERNAL_UI_RUN);
869 break;
874 static void do_external_ui_show(lv2_external_ui* _this_)
876 for (int i = 0; i < activeExternalUIs.size(); i++)
878 JuceLv2ExternalUI* Lv2UI = (JuceLv2ExternalUI*)activeExternalUIs.getUnchecked(i);
879 if (Lv2UI->getLv2Widget() == _this_)
881 Lv2UI->setUiFlag(JUCE_EXTERNAL_UI_SHOW);
882 break;
887 static void do_external_ui_hide(lv2_external_ui* _this_)
889 for (int i = 0; i < activeExternalUIs.size(); i++)
891 JuceLv2ExternalUI* Lv2UI = (JuceLv2ExternalUI*)activeExternalUIs.getUnchecked(i);
892 if (Lv2UI->getLv2Widget() == _this_)
894 Lv2UI->setUiFlag(JUCE_EXTERNAL_UI_HIDE);
895 break;
900 LV2UI_Widget getLv2Widget()
902 return &external_ui;
905 void setUiFlag(JUCE_EXTERNAL_UI_Flags flags)
907 do_ui_flags = flags;
910 private:
911 JuceLV2Wrapper* wrapper;
912 AudioProcessorEditor* editor;
914 lv2_external_ui external_ui;
915 lv2_external_ui_host* external_ui_host;
916 LV2UI_Controller controller;
918 JUCE_EXTERNAL_UI_Flags do_ui_flags;
920 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ExternalUI);
923 //==============================================================================
924 // Create a new JUCE Editor
925 class JuceLv2Editor : public AudioProcessorListener
927 public:
928 JuceLv2Editor (JuceLV2Wrapper* const wrapper_, const LV2UI_Descriptor* ui_descriptor_, LV2UI_Write_Function write_function_, LV2UI_Controller controller_, LV2UI_Widget* widget, const LV2_Feature* const* features, bool isExternalUI) :
929 wrapper(wrapper_),
930 externalUI(nullptr),
931 ui_descriptor(ui_descriptor_),
932 write_function(write_function_),
933 controller(controller_),
934 editor(nullptr)
936 filter = wrapper->getFilter();
937 filter->addListener(this);
939 if (filter != nullptr && filter->hasEditor())
941 printf("TEST - Before\n");
942 editor = filter->createEditorIfNeeded();
943 printf("TEST - After\n");
946 if (editor != nullptr)
948 if (isExternalUI)
950 // External UI
951 lv2_external_ui_host* external_ui_host = nullptr;
953 for (uint16_t j = 0; features[j]; j++)
955 if (strcmp(features[j]->URI, LV2_EXTERNAL_UI_URI) == 0 && features[j]->data != nullptr)
957 external_ui_host = (lv2_external_ui_host*)features[j]->data;
958 break;
962 if (external_ui_host)
964 externalUI = new JuceLv2ExternalUI(wrapper, editor, external_ui_host, controller);
965 *widget = externalUI->getLv2Widget();
967 else
969 widget = nullptr;
970 std::cerr << "Failed to init external UI" << std::cout;
973 else
975 // JUCE UI
976 editor->setOpaque (true);
977 editor->setVisible (true);
978 *widget = editor;
981 else
983 widget = nullptr;
984 std::cerr << "Failed to init UI" << std::cout;
987 // Padding for control ports
988 ctrl_pad = 0;
989 #if JucePlugin_WantsMidiInput
990 ctrl_pad += 1;
991 #endif
992 #if JucePlugin_ProducesMidiOutput
993 ctrl_pad += 1;
994 #endif
995 ctrl_pad += JucePlugin_MaxNumInputChannels;
996 ctrl_pad += JucePlugin_MaxNumOutputChannels;
999 ~JuceLv2Editor()
1001 JUCE_AUTORELEASEPOOL
1002 PopupMenu::dismissAllActiveMenus();
1004 filter->removeListener(this);
1005 filter->editorBeingDeleted (editor);
1007 if (externalUI != nullptr)
1008 delete externalUI;
1010 //if (editor != nullptr)
1011 // deleteAndZero (editor);
1014 void do_port_event(uint32_t port_index, float value)
1016 filter->setParameter(port_index-ctrl_pad, value);
1019 void do_cleanup()
1021 // We should only do this after UI gets working
1022 #if 0
1023 if (ui_descriptor)
1025 free((void*)ui_descriptor->URI);
1026 delete ui_descriptor;
1028 #endif
1031 //==============================================================================
1032 void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue)
1034 if (controller && write_function)
1035 write_function(controller, index+ctrl_pad, sizeof(float), 0, &newValue);
1038 void audioProcessorChanged (AudioProcessor*) {}
1040 private:
1041 JuceLV2Wrapper* wrapper;
1042 JuceLv2ExternalUI* externalUI;
1043 AudioProcessor* filter;
1044 AudioProcessorEditor* editor;
1046 const LV2UI_Descriptor* ui_descriptor;
1047 LV2UI_Write_Function write_function;
1048 LV2UI_Controller controller;
1050 int32_t ctrl_pad;
1052 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2Editor);
1055 //==============================================================================
1056 // LV2 descriptor functions
1057 LV2_Handle juce_lv2_instantiate(const LV2_Descriptor* descriptor, double sample_rate, const char* bundle_path, const LV2_Feature* const* features)
1059 JuceLV2Wrapper* wrapper = new JuceLV2Wrapper(descriptor, sample_rate, features);
1060 return wrapper;
1063 void juce_lv2_connect_port(LV2_Handle instance, uint32_t port, void* data_location)
1065 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
1066 wrapper->do_connect_port(port, data_location);
1069 void juce_lv2_activate(LV2_Handle instance)
1071 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
1072 wrapper->do_activate();
1075 void juce_lv2_run(LV2_Handle instance, uint32_t sample_count)
1077 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
1078 wrapper->do_run(sample_count);
1081 void juce_lv2_deactivate(LV2_Handle instance)
1083 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
1084 wrapper->do_deactivate();
1087 void juce_lv2_cleanup(LV2_Handle instance)
1089 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
1090 wrapper->do_cleanup();
1091 delete wrapper;
1094 const void* juce_lv2_extension_data(const char* uri)
1096 printf("TODO :: juce_lv2_extension_data()\n");
1097 return nullptr;
1100 //==============================================================================
1101 LV2UI_Handle juce_lv2ui_instantiate(const LV2UI_Descriptor* descriptor, LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features, bool isExternalUI)
1103 const MessageManagerLock mmLock;
1105 for (uint16_t i = 0; features[i]; i++)
1107 if (strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0 && features[i]->data != nullptr)
1109 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)features[i]->data;
1110 JuceLv2Editor* editor = new JuceLv2Editor(wrapper, descriptor, write_function, controller, widget, features, isExternalUI);
1111 return editor;
1115 std::cerr << "Host does not support instance data - cannot use UI" << std::cout;
1116 return nullptr;
1119 LV2UI_Handle juce_lv2ui_instantiate_JUCE(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)
1121 return juce_lv2ui_instantiate(descriptor, write_function, controller, widget, features, false);
1124 LV2UI_Handle juce_lv2ui_instantiate_external(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)
1126 return juce_lv2ui_instantiate(descriptor, write_function, controller, widget, features, true);
1129 void juce_lv2ui_port_event(LV2UI_Handle instance, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void* buffer)
1131 const MessageManagerLock mmLock;
1132 JuceLv2Editor* editor = (JuceLv2Editor*)instance;
1134 if (buffer_size == sizeof(float) && format == 0)
1136 float value = *(float*)buffer;
1137 editor->do_port_event(port_index, value);
1141 void juce_lv2ui_cleanup(LV2UI_Handle instance)
1143 const MessageManagerLock mmLock;
1144 JuceLv2Editor* editor = (JuceLv2Editor*)instance;
1145 editor->do_cleanup();
1146 delete editor;
1149 //==============================================================================
1150 // Create new LV2 objects
1151 LV2_Descriptor* getNewLv2Plugin()
1153 LV2_Descriptor* const Lv2Plugin = new LV2_Descriptor;
1154 Lv2Plugin->URI = strdup((const char*) get_uri().toUTF8());
1155 Lv2Plugin->instantiate = juce_lv2_instantiate;
1156 Lv2Plugin->connect_port = juce_lv2_connect_port;
1157 Lv2Plugin->activate = juce_lv2_activate;
1158 Lv2Plugin->run = juce_lv2_run;
1159 Lv2Plugin->deactivate = juce_lv2_deactivate;
1160 Lv2Plugin->cleanup = juce_lv2_cleanup;
1161 Lv2Plugin->extension_data = juce_lv2_extension_data;
1162 return Lv2Plugin;
1165 LV2UI_Descriptor* getNewLv2UI_JUCE()
1167 LV2UI_Descriptor* Lv2UI = new LV2UI_Descriptor;
1168 Lv2UI->URI = strdup((const char*) get_juce_ui_uri().toUTF8());
1169 Lv2UI->instantiate = juce_lv2ui_instantiate_JUCE;
1170 Lv2UI->cleanup = juce_lv2ui_cleanup;
1171 Lv2UI->port_event = juce_lv2ui_port_event;
1172 Lv2UI->extension_data = juce_lv2_extension_data;
1173 return Lv2UI;
1176 LV2UI_Descriptor* getNewLv2UI_External()
1178 LV2UI_Descriptor* Lv2UI = new LV2UI_Descriptor;
1179 Lv2UI->URI = strdup((const char*) get_external_ui_uri().toUTF8());
1180 Lv2UI->instantiate = juce_lv2ui_instantiate_external;
1181 Lv2UI->cleanup = juce_lv2ui_cleanup;
1182 Lv2UI->port_event = juce_lv2ui_port_event;
1183 Lv2UI->extension_data = juce_lv2_extension_data;
1184 return Lv2UI;
1187 LV2UI_Descriptor* getNewLv2UI(bool external)
1189 return external ? getNewLv2UI_External() : getNewLv2UI_JUCE();
1192 //==============================================================================
1193 // Mac startup code..
1194 #if JUCE_MAC
1196 extern "C" __attribute__ ((visibility("default"))) void juce_lv2_ttl_generator()
1198 generate_ttl();
1201 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
1203 initialiseMac();
1204 return (index == 0) ? getNewLv2Plugin() : nullptr;
1207 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
1209 return (index <= 1) ? getNewLv2UI((index == 1)) : nullptr;
1212 //==============================================================================
1213 // Linux startup code..
1214 #elif JUCE_LINUX
1216 extern "C" __attribute__ ((visibility("default"))) void juce_lv2_ttl_generator()
1218 generate_ttl();
1221 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
1223 SharedMessageThread::getInstance();
1224 return (index == 0) ? getNewLv2Plugin() : nullptr;
1227 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
1229 return (index <= 1) ? getNewLv2UI((index == 1)) : nullptr;
1232 // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash!
1233 __attribute__((constructor)) void myPluginInit() {}
1234 __attribute__((destructor)) void myPluginFini() {}
1236 //==============================================================================
1237 // Win32 startup code..
1238 #else
1240 extern "C" __declspec (dllexport) void juce_lv2_ttl_generator()
1242 generate_ttl();
1245 extern "C" __declspec (dllexport) const LV2_Descriptor* lv2_descriptor(uint32_t index)
1247 return (index == 0) ? getNewLv2Plugin() : nullptr;
1250 extern "C" __declspec (dllexport) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
1252 return (index <= 1) ? getNewLv2UI((index == 1)) : nullptr;
1255 #endif
1257 #endif