5 #include "../juce_IncludeCharacteristics.h"
7 #if JucePlugin_Build_LV2
17 #include "lv2/event.h"
18 #include "lv2/event_helpers.h"
19 #include "lv2/instance_access.h"
20 #include "lv2/uri_map.h"
22 #include "lv2/lv2_external_ui.h"
24 // These are dummy values!
32 kPlugCategSpacializer
,
35 kPlugCategRestoration
,
36 kPlugCategOfflineProcess
,
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();
48 Symbol
+= String(port_index
);
52 for (int i
=0; i
< Symbol
.length(); i
++)
54 if (std::isalpha(Symbol
[i
]) || std::isdigit(Symbol
[i
]) || Symbol
[i
] == '_') {
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
;
71 if (!string
.contains(".")) {
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()
101 switch (JucePlugin_VSTCategory
) {
102 case kPlugCategSynth
:
103 ptype
+= "lv2:InstrumentPlugin";
105 case kPlugCategAnalysis
:
106 ptype
+= "lv2:AnalyserPlugin";
108 case kPlugCategMastering
:
109 ptype
+= "lv2:DynamicsPlugin";
111 case kPlugCategSpacializer
:
112 ptype
+= "lv2:SpatialPlugin";
114 case kPlugCategRoomFx
:
115 ptype
+= "lv2:ModulatorPlugin";
117 case kPlugCategRestoration
:
118 ptype
+= "lv2:UtilityPlugin";
120 case kPlugCategGenerator
:
121 ptype
+= "lv2:GeneratorPlugin";
125 if (ptype
.isNotEmpty()) {
129 ptype
+= "lv2:Plugin";
133 String
get_manifest_ttl(String URI
, String Binary
)
136 manifest
+= "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n";
137 manifest
+= "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\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";
146 String
get_plugin_ttl(String URI
, String Binary
)
148 uint32_t i
, port_index
= 0;
149 AudioProcessor
* filter
= createPluginFilter();
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";
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";
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";
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";
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";
200 for (i
=0; i
<JucePlugin_MaxNumInputChannels
; i
++) {
202 plugin
+= " lv2:port [\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";
220 for (i
=0; i
<JucePlugin_MaxNumOutputChannels
; i
++) {
222 plugin
+= " lv2:port [\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";
240 for (i
=0; i
< filter
->getNumParameters(); i
++) {
242 plugin
+= " lv2:port [\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";
264 plugin
+= " doap:name \"" + String(JucePlugin_Name
) + "\" ;\n";
265 plugin
+= " doap:creator \"" + String(JucePlugin_Manufacturer
) + "\" .\n";
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
;
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
;
287 std::cout
<< " done!" << std::endl
;
290 //==============================================================================
292 //extern void juce_callAnyTimersSynchronously();
295 extern void initialiseMac();
299 //==============================================================================
302 class SharedMessageThread
: public Thread
305 SharedMessageThread()
306 : Thread ("Lv2MessageThread"),
311 while (! initialised
)
315 ~SharedMessageThread()
317 signalThreadShouldExit();
318 JUCEApplication::quit();
319 waitForThreadToExit (5000);
320 clearSingletonInstance();
325 initialiseJuce_GUI();
328 MessageManager::getInstance()->setCurrentThreadAsMessageThread();
330 while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250))
334 juce_DeclareSingleton (SharedMessageThread
, false);
340 juce_ImplementSingleton (SharedMessageThread
)
344 static Array
<void*> activePlugins
;
346 //==============================================================================
347 // Create a new JUCE LV2 Plugin
348 class JuceLV2Wrapper
: private Timer
351 JuceLV2Wrapper(const LV2_Descriptor
* descriptor_
, double sample_rate_
, const LV2_Feature
* const* features
) :
353 numInChans (JucePlugin_MaxNumInputChannels
),
354 numOutChans (JucePlugin_MaxNumOutputChannels
),
355 isProcessing (false),
356 firstProcessCallback (true),
357 descriptor (descriptor_
),
358 sample_rate (sample_rate_
),
363 printf("JuceLV2Wrapper()\n");
365 JUCE_AUTORELEASEPOOL
;
366 initialiseJuce_GUI();
369 MessageManagerLock mmLock
;
372 filter
= createPluginFilter();
373 jassert(filter
!= nullptr);
375 filter
->setPlayConfigDetails(numInChans
, numOutChans
, 0, 0);
378 #if JucePlugin_WantsMidiInput
381 #if JucePlugin_ProducesMidiOutput
384 port_count
+= numInChans
;
385 port_count
+= numOutChans
;
386 port_count
+= filter
->getNumParameters();
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
));
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");
417 activePlugins
.add (this);
428 MessageManagerLock mmLock
;
436 deleteTempChannels();
439 ports_ctrl_last
.clear();
441 jassert (activePlugins
.contains (this));
442 activePlugins
.removeValue (this);
445 if (activePlugins
.size() == 0)
448 SharedMessageThread::deleteInstance();
454 //==============================================================================
455 // LV2 Descriptor Calls
456 void do_connect_port(uint32_t port
, void* data_location
)
458 if (port
< port_count
)
463 #if JucePlugin_WantsMidiInput
465 port_min
= (LV2_Event_Buffer
*)data_location
;
471 #if JucePlugin_ProducesMidiOutput
473 port_mout
= (LV2_Event_Buffer
*)data_location
;
479 for (i
=0; i
< numInChans
; i
++) {
481 ports_ain
[i
] = (float*)data_location
;
487 for (i
=0; i
< numOutChans
; i
++) {
489 ports_aout
[i
] = (float*)data_location
;
495 for (i
=0; i
< filter
->getNumParameters(); i
++) {
497 ports_ctrl
.set(i
, (float*)data_location
);
507 if (filter
!= nullptr)
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);
534 if (filter
!= nullptr)
536 filter
->releaseResources();
538 isProcessing
= false;
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
559 filter
->setNonRealtime (false);
562 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
563 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST
)
564 filter
->setNonRealtime (true);
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
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
);
606 for (i
= 0; i
< numOut
; ++i
)
608 float* chan
= tempChannels
.getUnchecked(i
);
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
);
628 if (i
< numIn
&& chan
!= ports_ain
[i
])
629 memcpy (chan
, ports_ain
[i
], sizeof (float) * sample_count
);
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
) {
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
);
690 free((void*)descriptor
->URI
);
696 //==============================================================================
699 AudioProcessor
* getFilter() { return filter
; }
701 //==============================================================================
702 int32_t getChunk (void** data
)
704 if (filter
== nullptr)
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)
724 chunkMemory
.setSize (0);
727 if (byteSize
> 0 && data
!= nullptr)
729 filter
->setCurrentProgramStateInformation (data
, byteSize
);
735 if (chunkMemoryTime
> 0 && chunkMemoryTime
< JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000)
738 chunkMemory
.setSize (0);
742 //==============================================================================
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
;
757 uint16_t midi_uri_id
;
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
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),
802 external_ui_host(external_ui_host_
),
803 controller(controller_
),
804 do_ui_flags(JUCE_EXTERNAL_UI_NONE
)
807 editor
->setOpaque (true);
809 String title
= external_ui_host
->plugin_human_id
!= nullptr ? String(external_ui_host
->plugin_human_id
) : wrapper
->getFilter()->getName();
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);
829 activeExternalUIs
.removeValue(this);
832 AudioProcessorEditor
* getEditorComp() const
834 return dynamic_cast <AudioProcessorEditor
*> (getContentComponent());
837 void closeButtonPressed()
840 external_ui_host
->ui_closed(controller
);
845 if (do_ui_flags
& JUCE_EXTERNAL_UI_SHOW
)
848 if (do_ui_flags
& JUCE_EXTERNAL_UI_HIDE
)
851 if (do_ui_flags
& JUCE_EXTERNAL_UI_RUN
)
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
);
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
);
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
);
900 LV2UI_Widget
getLv2Widget()
905 void setUiFlag(JUCE_EXTERNAL_UI_Flags flags
)
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
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
) :
931 ui_descriptor(ui_descriptor_
),
932 write_function(write_function_
),
933 controller(controller_
),
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)
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
;
962 if (external_ui_host
)
964 externalUI
= new JuceLv2ExternalUI(wrapper
, editor
, external_ui_host
, controller
);
965 *widget
= externalUI
->getLv2Widget();
970 std::cerr
<< "Failed to init external UI" << std::cout
;
976 editor
->setOpaque (true);
977 editor
->setVisible (true);
984 std::cerr
<< "Failed to init UI" << std::cout
;
987 // Padding for control ports
989 #if JucePlugin_WantsMidiInput
992 #if JucePlugin_ProducesMidiOutput
995 ctrl_pad
+= JucePlugin_MaxNumInputChannels
;
996 ctrl_pad
+= JucePlugin_MaxNumOutputChannels
;
1001 JUCE_AUTORELEASEPOOL
1002 PopupMenu::dismissAllActiveMenus();
1004 filter
->removeListener(this);
1005 filter
->editorBeingDeleted (editor
);
1007 if (externalUI
!= nullptr)
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
);
1021 // We should only do this after UI gets working
1025 free((void*)ui_descriptor
->URI
);
1026 delete ui_descriptor
;
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
*) {}
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
;
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
);
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();
1094 const void* juce_lv2_extension_data(const char* uri
)
1096 printf("TODO :: juce_lv2_extension_data()\n");
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
);
1115 std::cerr
<< "Host does not support instance data - cannot use UI" << std::cout
;
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();
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
;
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
;
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
;
1187 LV2UI_Descriptor
* getNewLv2UI(bool external
)
1189 return external
? getNewLv2UI_External() : getNewLv2UI_JUCE();
1192 //==============================================================================
1193 // Mac startup code..
1196 extern "C" __attribute__ ((visibility("default"))) void juce_lv2_ttl_generator()
1201 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor
* lv2_descriptor(uint32_t index
)
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..
1216 extern "C" __attribute__ ((visibility("default"))) void juce_lv2_ttl_generator()
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..
1240 extern "C" __declspec (dllexport
) void juce_lv2_ttl_generator()
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;