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