Massive UI work
[juce-lv2.git] / juce / source / src / audio / plugin_client / LV2 / juce_LV2_Wrapper.cpp
blobf765c033b755ea6aef6640d77f3ef6bb14a7a068
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 : private Timer
354 public:
355 JuceLV2Wrapper(const LV2_Descriptor* descriptor_, double sample_rate_, const LV2_Feature* const* features) :
356 chunkMemoryTime (0),
357 numInChans (JucePlugin_MaxNumInputChannels),
358 numOutChans (JucePlugin_MaxNumOutputChannels),
359 isProcessing (false),
360 firstProcessCallback (true),
361 descriptor (descriptor_),
362 sample_rate (sample_rate_),
363 buffer_size (512),
364 midi_uri_id (0),
365 port_count (0)
367 printf("JuceLV2Wrapper()\n");
369 JUCE_AUTORELEASEPOOL;
370 initialiseJuce_GUI();
372 #if JUCE_LINUX
373 MessageManagerLock mmLock;
374 #endif
376 filter = createPluginFilter();
377 jassert(filter != nullptr);
379 filter->setPlayConfigDetails(numInChans, numOutChans, 0, 0);
381 // Port count
382 #if JucePlugin_WantsMidiInput
383 port_count += 1;
384 #endif
385 #if JucePlugin_ProducesMidiOutput
386 port_count += 1;
387 #endif
388 port_count += numInChans;
389 port_count += numOutChans;
390 port_count += filter->getNumParameters();
392 // Set Port data
393 port_min = nullptr;
394 port_mout = nullptr;
395 ports_ctrl.insertMultiple(0, nullptr, filter->getNumParameters());
396 ports_ctrl_last.insertMultiple(0, 0.0f, filter->getNumParameters());
398 for (int i=0; i < numInChans; i++) {
399 ports_ain[i] = nullptr;
402 for (int i=0; i < numOutChans; i++) {
403 ports_aout[i] = nullptr;
406 for (int i=0; i < filter->getNumParameters(); i++) {
407 ports_ctrl_last.set(i, filter->getParameter(i));
410 // Get MIDI URI Id
411 for (uint16_t j = 0; features[j]; j++)
413 if (strcmp(features[j]->URI, LV2_URI_MAP_URI) == 0)
415 LV2_URI_Map_Feature* uri_feature = (LV2_URI_Map_Feature*)features[j]->data;
416 midi_uri_id = uri_feature->uri_to_id(uri_feature->callback_data, LV2_EVENT_URI, "http://lv2plug.in/ns/ext/midi#MidiEvent");
417 break;
421 activePlugins.add (this);
423 startTimer(1000);
426 ~JuceLV2Wrapper()
428 JUCE_AUTORELEASEPOOL
431 #if JUCE_LINUX
432 MessageManagerLock mmLock;
433 #endif
434 stopTimer();
436 delete filter;
437 filter = nullptr;
439 channels.free();
440 deleteTempChannels();
442 ports_ctrl.clear();
443 ports_ctrl_last.clear();
445 jassert (activePlugins.contains (this));
446 activePlugins.removeValue (this);
449 if (activePlugins.size() == 0)
451 #if JUCE_LINUX
452 SharedMessageThread::deleteInstance();
453 #endif
454 shutdownJuce_GUI();
458 //==============================================================================
459 // LV2 Descriptor Calls
460 void do_connect_port(uint32_t port, void* data_location)
462 if (port < port_count)
464 int i;
465 uint32_t index = 0;
467 #if JucePlugin_WantsMidiInput
468 if (port == index) {
469 port_min = (LV2_Event_Buffer*)data_location;
470 return;
472 index += 1;
473 #endif
475 #if JucePlugin_ProducesMidiOutput
476 if (port == index) {
477 port_mout = (LV2_Event_Buffer*)data_location;
478 return;
480 index += 1;
481 #endif
483 for (i=0; i < numInChans; i++) {
484 if (port == index) {
485 ports_ain[i] = (float*)data_location;
486 return;
488 index += 1;
491 for (i=0; i < numOutChans; i++) {
492 if (port == index) {
493 ports_aout[i] = (float*)data_location;
494 return;
496 index += 1;
499 for (i=0; i < filter->getNumParameters(); i++) {
500 if (port == index) {
501 ports_ctrl.set(i, (float*)data_location);
502 return;
504 index += 1;
509 void do_activate()
511 if (filter != nullptr)
513 isProcessing = true;
514 channels.calloc (numInChans + numOutChans);
516 jassert (sample_rate > 0);
517 if (sample_rate <= 0.0)
518 sample_rate = 44100.0;
520 jassert (buffer_size > 0);
522 firstProcessCallback = true;
524 filter->setNonRealtime (false);
525 filter->setPlayConfigDetails (numInChans, numOutChans, sample_rate, buffer_size);
527 deleteTempChannels();
529 filter->prepareToPlay (sample_rate, buffer_size);
531 midiEvents.ensureSize (2048);
532 midiEvents.clear();
536 void do_deactivate()
538 if (filter != nullptr)
540 filter->releaseResources();
542 isProcessing = false;
543 channels.free();
545 deleteTempChannels();
549 void do_run(uint32_t sample_count)
551 if (firstProcessCallback)
553 firstProcessCallback = false;
555 // if this fails, the host hasn't called resume() before processing
556 jassert (isProcessing);
558 // (tragically, some hosts actually need this, although it's stupid to have
559 // to do it here..)
560 if (! isProcessing)
561 do_activate();
563 filter->setNonRealtime (false);
565 #if JUCE_WINDOWS
566 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
567 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST)
568 filter->setNonRealtime (true);
569 #endif
572 // Check if buffer size changed
573 if (buffer_size != sample_count)
575 buffer_size = sample_count;
576 filter->setPlayConfigDetails(numInChans, numOutChans, sample_rate, buffer_size);
577 filter->prepareToPlay(sample_rate, buffer_size);
580 // Check for updated parameters
581 float cur_value;
582 for (int i = 0; i < ports_ctrl.size(); i++)
584 if (ports_ctrl[i] != nullptr)
586 cur_value = *(float*)ports_ctrl[i];
587 if (ports_ctrl_last[i] != cur_value) {
588 filter->setParameter(i, cur_value);
589 ports_ctrl_last.setUnchecked(i, cur_value);
594 jassert (activePlugins.contains (this));
597 const ScopedLock sl (filter->getCallbackLock());
599 const int numIn = numInChans;
600 const int numOut = numOutChans;
602 if (filter->isSuspended())
604 for (int i = 0; i < numOut; ++i)
605 zeromem (ports_aout[i], sizeof (float) * sample_count);
607 else
609 int i;
610 for (i = 0; i < numOut; ++i)
612 float* chan = tempChannels.getUnchecked(i);
614 if (chan == 0)
616 chan = ports_aout[i];
618 // if some output channels are disabled, some hosts supply the same buffer
619 // for multiple channels - this buggers up our method of copying the
620 // inputs over the outputs, so we need to create unique temp buffers in this case..
621 for (int j = i; --j >= 0;)
623 if (ports_aout[j] == chan)
625 chan = new float [buffer_size * 2];
626 tempChannels.set (i, chan);
627 break;
632 if (i < numIn && chan != ports_ain[i])
633 memcpy (chan, ports_ain[i], sizeof (float) * sample_count);
635 channels[i] = chan;
638 // LV2 MIDI Input
639 if (port_min != nullptr)
641 LV2_Event_Iterator iter;
642 lv2_event_begin(&iter, port_min);
644 lv2_event_buffer_reset(port_min, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(port_min + 1));
646 for (uint32_t i=0; i < iter.buf->event_count; ++i) {
647 uint8_t* data;
648 LV2_Event* ev = lv2_event_get(&iter, &data);
649 midiEvents.addEvent(data, ev->size, ev->frames);
650 lv2_event_increment(&iter);
654 for (; i < numIn; ++i)
655 channels[i] = ports_ain[i];
657 AudioSampleBuffer chans (channels, jmax (numIn, numOut), sample_count);
659 filter->processBlock (chans, midiEvents);
663 if (! midiEvents.isEmpty() && port_mout != nullptr)
665 #if JucePlugin_ProducesMidiOutput
666 const int numEvents = midiEvents.getNumEvents();
668 LV2_Event_Iterator iter;
669 lv2_event_buffer_reset(port_mout, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(port_mout + 1));
670 lv2_event_begin(&iter, port_mout);
672 const JUCE_NAMESPACE::uint8* midiEventData;
673 int midiEventSize, midiEventPosition;
674 MidiBuffer::Iterator i (midiEvents);
676 while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
678 jassert (midiEventPosition >= 0 && midiEventPosition < sample_count);
680 lv2_event_write(&iter, midiEventPosition, 0, midi_uri_id, midiEventSize, midiEventData);
682 #endif
683 midiEvents.clear();
687 void do_cleanup()
689 stopTimer();
691 #if 0
692 if (descriptor)
694 free((void*)descriptor->URI);
695 delete descriptor;
697 #endif
700 //==============================================================================
701 // JUCE Stuff
703 AudioProcessor* getFilter() { return filter; }
705 //==============================================================================
706 int32_t getChunk (void** data)
708 if (filter == nullptr)
709 return 0;
711 chunkMemory.setSize (0);
712 filter->getCurrentProgramStateInformation (chunkMemory);
714 *data = (void*) chunkMemory.getData();
716 // because the chunk is only needed temporarily by the host (or at least you'd
717 // hope so) we'll give it a while and then free it in the timer callback.
718 chunkMemoryTime = JUCE_NAMESPACE::Time::getApproximateMillisecondCounter();
720 return (int32_t) chunkMemory.getSize();
723 void setChunk (void* data, int32_t byteSize)
725 if (filter == nullptr)
726 return;
728 chunkMemory.setSize (0);
729 chunkMemoryTime = 0;
731 if (byteSize > 0 && data != nullptr)
733 filter->setCurrentProgramStateInformation (data, byteSize);
737 void timerCallback()
739 if (chunkMemoryTime > 0 && chunkMemoryTime < JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000)
741 chunkMemoryTime = 0;
742 chunkMemory.setSize (0);
746 //==============================================================================
747 private:
748 AudioProcessor* filter;
749 JUCE_NAMESPACE::MemoryBlock chunkMemory;
750 JUCE_NAMESPACE::uint32 chunkMemoryTime;
751 MidiBuffer midiEvents;
752 int numInChans, numOutChans;
753 bool isProcessing, firstProcessCallback;
754 HeapBlock<float*> channels;
755 Array<float*> tempChannels; // see note in do_run()
757 const LV2_Descriptor* descriptor;
759 double sample_rate;
760 int buffer_size;
761 uint16_t midi_uri_id;
762 uint32_t port_count;
764 LV2_Event_Buffer* port_min;
765 LV2_Event_Buffer* port_mout;
766 float* ports_ain[JucePlugin_MaxNumInputChannels];
767 float* ports_aout[JucePlugin_MaxNumOutputChannels];
768 Array<float*> ports_ctrl;
769 Array<float> ports_ctrl_last;
771 //==============================================================================
772 void deleteTempChannels()
774 for (int i = tempChannels.size(); --i >= 0;)
775 delete[] (tempChannels.getUnchecked(i));
777 tempChannels.clear();
779 if (filter != nullptr)
780 tempChannels.insertMultiple (0, 0, filter->getNumInputChannels() + filter->getNumOutputChannels());
783 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLV2Wrapper);
786 class JuceLv2ExternalUI;
787 static Array<JuceLv2ExternalUI*> activeExternalUIs;
789 enum JUCE_EXTERNAL_UI_Flags {
790 JUCE_EXTERNAL_UI_NONE = 0,
791 JUCE_EXTERNAL_UI_RUN = 0x1,
792 JUCE_EXTERNAL_UI_SHOW = 0x2,
793 JUCE_EXTERNAL_UI_HIDE = 0x4
796 //==============================================================================
797 // Create a new JUCE External UI
798 class JuceLv2ExternalUI : private Timer,
799 public DocumentWindow
801 public:
802 JuceLv2ExternalUI (JuceLV2Wrapper* const wrapper_, AudioProcessorEditor* const editor_, lv2_external_ui_host* external_ui_host_, LV2UI_Controller controller_) :
803 DocumentWindow("", Colours::white, DocumentWindow::minimiseButton | DocumentWindow::closeButton, true),
804 wrapper(wrapper_),
805 editor(editor_),
806 external_ui_host(external_ui_host_),
807 controller(controller_),
808 do_ui_flags(JUCE_EXTERNAL_UI_NONE)
810 setOpaque (true);
811 editor->setOpaque (true);
813 String title = external_ui_host->plugin_human_id != nullptr ? String(external_ui_host->plugin_human_id) : wrapper->getFilter()->getName();
814 setName(title);
816 setTitleBarHeight (0);
817 setDropShadowEnabled(false);
818 setUsingNativeTitleBar(true);
820 setContentNonOwned(editor, true);
822 external_ui.run = do_external_ui_run;
823 external_ui.show = do_external_ui_show;
824 external_ui.hide = do_external_ui_hide;
826 activeExternalUIs.add(this);
828 startTimer(200);
831 ~JuceLv2ExternalUI()
833 activeExternalUIs.removeValue(this);
836 AudioProcessorEditor* getEditorComp() const
838 return dynamic_cast <AudioProcessorEditor*> (getContentComponent());
841 void closeButtonPressed()
843 delete this;
844 external_ui_host->ui_closed(controller);
847 void timerCallback()
849 if (do_ui_flags & JUCE_EXTERNAL_UI_SHOW)
850 setVisible(true);
852 if (do_ui_flags & JUCE_EXTERNAL_UI_HIDE)
853 setVisible(false);
855 if (do_ui_flags & JUCE_EXTERNAL_UI_RUN)
857 editor->repaint();
858 repaint();
861 do_ui_flags = JUCE_EXTERNAL_UI_NONE;
864 //==============================================================================
865 static void do_external_ui_run(lv2_external_ui* _this_)
867 for (int i = 0; i < activeExternalUIs.size(); i++)
869 JuceLv2ExternalUI* Lv2UI = (JuceLv2ExternalUI*)activeExternalUIs.getUnchecked(i);
870 if (Lv2UI->getLv2Widget() == _this_)
872 Lv2UI->setUiFlag(JUCE_EXTERNAL_UI_RUN);
873 break;
878 static void do_external_ui_show(lv2_external_ui* _this_)
880 for (int i = 0; i < activeExternalUIs.size(); i++)
882 JuceLv2ExternalUI* Lv2UI = (JuceLv2ExternalUI*)activeExternalUIs.getUnchecked(i);
883 if (Lv2UI->getLv2Widget() == _this_)
885 Lv2UI->setUiFlag(JUCE_EXTERNAL_UI_SHOW);
886 break;
891 static void do_external_ui_hide(lv2_external_ui* _this_)
893 for (int i = 0; i < activeExternalUIs.size(); i++)
895 JuceLv2ExternalUI* Lv2UI = (JuceLv2ExternalUI*)activeExternalUIs.getUnchecked(i);
896 if (Lv2UI->getLv2Widget() == _this_)
898 Lv2UI->setUiFlag(JUCE_EXTERNAL_UI_HIDE);
899 break;
904 LV2UI_Widget getLv2Widget()
906 return &external_ui;
909 void setUiFlag(JUCE_EXTERNAL_UI_Flags flags)
911 do_ui_flags = flags;
914 private:
915 JuceLV2Wrapper* wrapper;
916 AudioProcessorEditor* editor;
918 lv2_external_ui external_ui;
919 lv2_external_ui_host* external_ui_host;
920 LV2UI_Controller controller;
922 JUCE_EXTERNAL_UI_Flags do_ui_flags;
924 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ExternalUI);
927 //==============================================================================
928 // Create a new JUCE Editor
929 class JuceLv2Editor : public AudioProcessorListener
931 public:
932 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) :
933 wrapper(wrapper_),
934 externalUI(nullptr),
935 ui_descriptor(ui_descriptor_),
936 write_function(write_function_),
937 controller(controller_),
938 editor(nullptr)
940 filter = wrapper->getFilter();
941 filter->addListener(this);
943 if (filter != nullptr && filter->hasEditor())
945 printf("TEST - Before\n");
946 editor = filter->createEditorIfNeeded();
947 printf("TEST - After\n");
950 if (editor != nullptr)
952 if (isExternalUI)
954 // External UI
955 lv2_external_ui_host* external_ui_host = nullptr;
957 for (uint16_t j = 0; features[j]; j++)
959 if (strcmp(features[j]->URI, LV2_EXTERNAL_UI_URI) == 0 && features[j]->data != nullptr)
961 external_ui_host = (lv2_external_ui_host*)features[j]->data;
962 break;
966 if (external_ui_host)
968 externalUI = new JuceLv2ExternalUI(wrapper, editor, external_ui_host, controller);
969 *widget = externalUI->getLv2Widget();
971 else
973 widget = nullptr;
974 std::cerr << "Failed to init external UI" << std::cout;
977 else
979 // JUCE UI
980 editor->setOpaque (true);
981 editor->setVisible (true);
982 *widget = editor;
985 else
987 widget = nullptr;
988 std::cerr << "Failed to init UI" << std::cout;
991 // Padding for control ports
992 ctrl_pad = 0;
993 #if JucePlugin_WantsMidiInput
994 ctrl_pad += 1;
995 #endif
996 #if JucePlugin_ProducesMidiOutput
997 ctrl_pad += 1;
998 #endif
999 ctrl_pad += JucePlugin_MaxNumInputChannels;
1000 ctrl_pad += JucePlugin_MaxNumOutputChannels;
1003 ~JuceLv2Editor()
1005 JUCE_AUTORELEASEPOOL
1006 PopupMenu::dismissAllActiveMenus();
1008 filter->removeListener(this);
1009 filter->editorBeingDeleted (editor);
1011 if (externalUI != nullptr)
1012 delete externalUI;
1014 //if (editor != nullptr)
1015 // deleteAndZero (editor);
1018 void do_port_event(uint32_t port_index, float value)
1020 filter->setParameter(port_index-ctrl_pad, value);
1023 void do_cleanup()
1025 // We should only do this after UI gets working
1026 #if 0
1027 if (ui_descriptor)
1029 free((void*)ui_descriptor->URI);
1030 delete ui_descriptor;
1032 #endif
1035 //==============================================================================
1036 void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue)
1038 if (controller && write_function)
1039 write_function(controller, index+ctrl_pad, sizeof(float), 0, &newValue);
1042 void audioProcessorChanged (AudioProcessor*) {}
1044 private:
1045 JuceLV2Wrapper* wrapper;
1046 JuceLv2ExternalUI* externalUI;
1047 AudioProcessor* filter;
1048 AudioProcessorEditor* editor;
1050 const LV2UI_Descriptor* ui_descriptor;
1051 LV2UI_Write_Function write_function;
1052 LV2UI_Controller controller;
1054 int32_t ctrl_pad;
1056 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2Editor);
1059 //==============================================================================
1060 // LV2 descriptor functions
1061 LV2_Handle juce_lv2_instantiate(const LV2_Descriptor* descriptor, double sample_rate, const char* bundle_path, const LV2_Feature* const* features)
1063 JuceLV2Wrapper* wrapper = new JuceLV2Wrapper(descriptor, sample_rate, features);
1064 return wrapper;
1067 void juce_lv2_connect_port(LV2_Handle instance, uint32_t port, void* data_location)
1069 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
1070 wrapper->do_connect_port(port, data_location);
1073 void juce_lv2_activate(LV2_Handle instance)
1075 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
1076 wrapper->do_activate();
1079 void juce_lv2_run(LV2_Handle instance, uint32_t sample_count)
1081 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
1082 wrapper->do_run(sample_count);
1085 void juce_lv2_deactivate(LV2_Handle instance)
1087 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
1088 wrapper->do_deactivate();
1091 void juce_lv2_cleanup(LV2_Handle instance)
1093 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
1094 wrapper->do_cleanup();
1095 delete wrapper;
1098 const void* juce_lv2_extension_data(const char* uri)
1100 printf("TODO :: juce_lv2_extension_data()\n");
1101 return nullptr;
1104 //==============================================================================
1105 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)
1107 const MessageManagerLock mmLock;
1109 for (uint16_t i = 0; features[i]; i++)
1111 if (strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0 && features[i]->data != nullptr)
1113 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)features[i]->data;
1114 JuceLv2Editor* editor = new JuceLv2Editor(wrapper, descriptor, write_function, controller, widget, features, isExternalUI);
1115 return editor;
1119 std::cerr << "Host does not support instance data - cannot use UI" << std::cout;
1120 return nullptr;
1123 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)
1125 return juce_lv2ui_instantiate(descriptor, write_function, controller, widget, features, false);
1128 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)
1130 return juce_lv2ui_instantiate(descriptor, write_function, controller, widget, features, true);
1133 void juce_lv2ui_port_event(LV2UI_Handle instance, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void* buffer)
1135 const MessageManagerLock mmLock;
1136 JuceLv2Editor* editor = (JuceLv2Editor*)instance;
1138 if (buffer_size == sizeof(float) && format == 0)
1140 float value = *(float*)buffer;
1141 editor->do_port_event(port_index, value);
1145 void juce_lv2ui_cleanup(LV2UI_Handle instance)
1147 const MessageManagerLock mmLock;
1148 JuceLv2Editor* editor = (JuceLv2Editor*)instance;
1149 editor->do_cleanup();
1150 delete editor;
1153 //==============================================================================
1154 // Create new LV2 objects
1155 LV2_Descriptor* getNewLv2Plugin()
1157 LV2_Descriptor* const Lv2Plugin = new LV2_Descriptor;
1158 Lv2Plugin->URI = strdup((const char*) get_uri().toUTF8());
1159 Lv2Plugin->instantiate = juce_lv2_instantiate;
1160 Lv2Plugin->connect_port = juce_lv2_connect_port;
1161 Lv2Plugin->activate = juce_lv2_activate;
1162 Lv2Plugin->run = juce_lv2_run;
1163 Lv2Plugin->deactivate = juce_lv2_deactivate;
1164 Lv2Plugin->cleanup = juce_lv2_cleanup;
1165 Lv2Plugin->extension_data = juce_lv2_extension_data;
1166 return Lv2Plugin;
1169 LV2UI_Descriptor* getNewLv2UI_JUCE()
1171 LV2UI_Descriptor* Lv2UI = new LV2UI_Descriptor;
1172 Lv2UI->URI = strdup((const char*) get_juce_ui_uri().toUTF8());
1173 Lv2UI->instantiate = juce_lv2ui_instantiate_JUCE;
1174 Lv2UI->cleanup = juce_lv2ui_cleanup;
1175 Lv2UI->port_event = juce_lv2ui_port_event;
1176 Lv2UI->extension_data = juce_lv2_extension_data;
1177 return Lv2UI;
1180 LV2UI_Descriptor* getNewLv2UI_External()
1182 LV2UI_Descriptor* Lv2UI = new LV2UI_Descriptor;
1183 Lv2UI->URI = strdup((const char*) get_external_ui_uri().toUTF8());
1184 Lv2UI->instantiate = juce_lv2ui_instantiate_external;
1185 Lv2UI->cleanup = juce_lv2ui_cleanup;
1186 Lv2UI->port_event = juce_lv2ui_port_event;
1187 Lv2UI->extension_data = juce_lv2_extension_data;
1188 return Lv2UI;
1191 LV2UI_Descriptor* getNewLv2UI(bool external)
1193 return external ? getNewLv2UI_External() : getNewLv2UI_JUCE();
1196 //==============================================================================
1197 // Mac startup code..
1198 #if JUCE_MAC
1200 extern "C" __attribute__ ((visibility("default"))) void juce_lv2_ttl_generator()
1202 generate_ttl();
1205 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
1207 initialiseMac();
1208 return (index == 0) ? getNewLv2Plugin() : nullptr;
1211 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
1213 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
1216 //==============================================================================
1217 // Linux startup code..
1218 #elif JUCE_LINUX
1220 extern "C" __attribute__ ((visibility("default"))) void juce_lv2_ttl_generator()
1222 generate_ttl();
1225 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
1227 SharedMessageThread::getInstance();
1228 return (index == 0) ? getNewLv2Plugin() : nullptr;
1231 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
1233 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
1236 // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash!
1237 __attribute__((constructor)) void myPluginInit() {}
1238 __attribute__((destructor)) void myPluginFini() {}
1240 //==============================================================================
1241 // Win32 startup code..
1242 #else
1244 extern "C" __declspec (dllexport) void juce_lv2_ttl_generator()
1246 generate_ttl();
1249 extern "C" __declspec (dllexport) const LV2_Descriptor* lv2_descriptor(uint32_t index)
1251 return (index == 0) ? getNewLv2Plugin() : nullptr;
1254 extern "C" __declspec (dllexport) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
1256 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
1259 #endif
1261 #endif