Misc
[juce-lv2.git] / juce / source / src / audio / plugin_client / LV2 / juce_LV2_Wrapper.cpp
blob1e3c6eb2364e72a33a3d2d75f65677bd1108440e
1 /*
2 * JUCE LV2 wrapper
3 */
5 #include "../juce_IncludeCharacteristics.h"
7 #define JucePlugin_Build_LV2 1
9 #if JucePlugin_Build_LV2
11 // LV2 includes..
12 #include "lv2/lv2.h"
13 #include "lv2/event.h"
14 #include "lv2/event_helpers.h"
15 #include "lv2/instance_access.h"
16 #include "lv2/uri_map.h"
17 #include "lv2/ui.h"
18 #include "lv2/lv2_external_ui.h"
20 #include "../juce_PluginHeaders.h"
21 #include "../juce_PluginHostType.h"
23 //==============================================================================
24 // Same as juce_lv2_gen.cpp
25 String get_uri()
27 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":" JucePlugin_VersionString).replace(" ", "_");
30 String get_juce_ui_uri()
32 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":JUCE-Native-UI").replace(" ", "_");
35 String get_external_ui_uri()
37 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":JUCE-External-UI").replace(" ", "_");
40 static Array<void*> activePlugins;
42 extern AudioProcessor* JUCE_CALLTYPE createPluginFilter();
44 //==============================================================================
45 // Create a new JUCE LV2 Plugin
46 class JuceLV2Wrapper : private Timer
48 public:
49 JuceLV2Wrapper(const LV2_Descriptor* descriptor_, double sample_rate_, const LV2_Feature* const* features) :
50 chunkMemoryTime (0),
51 numInChans (JucePlugin_MaxNumInputChannels),
52 numOutChans (JucePlugin_MaxNumOutputChannels),
53 isProcessing (false),
54 firstProcessCallback (true),
55 descriptor (descriptor_),
56 sample_rate (sample_rate_),
57 buffer_size (512),
58 midi_uri_id (0),
59 port_count (0)
61 printf("JuceLV2Wrapper()\n");
62 initialiseJuce_GUI();
64 filter = createPluginFilter();
65 filter->setPlayConfigDetails(numInChans, numOutChans, 0, 0);
67 // Port count
68 #if JucePlugin_WantsMidiInput
69 port_count += 1;
70 #endif
71 #if JucePlugin_ProducesMidiOutput
72 port_count += 1;
73 #endif
74 port_count += numInChans;
75 port_count += numOutChans;
76 port_count += filter->getNumParameters();
78 // Set Port data
79 port_min = nullptr;
80 port_mout = nullptr;
81 ports_ctrl.insertMultiple(0, nullptr, filter->getNumParameters());
82 ports_ctrl_last.insertMultiple(0, 0.0f, filter->getNumParameters());
84 for (int i=0; i < numInChans; i++) {
85 ports_ain[i] = nullptr;
88 for (int i=0; i < numOutChans; i++) {
89 ports_aout[i] = nullptr;
92 for (int i=0; i < filter->getNumParameters(); i++) {
93 ports_ctrl_last.set(i, filter->getParameter(i));
96 // Get MIDI URI Id
97 for (uint16_t j = 0; features[j]; j++)
99 if (strcmp(features[j]->URI, LV2_URI_MAP_URI) == 0)
101 LV2_URI_Map_Feature* uri_feature = (LV2_URI_Map_Feature*)features[j]->data;
102 midi_uri_id = uri_feature->uri_to_id(uri_feature->callback_data, LV2_EVENT_URI, "http://lv2plug.in/ns/ext/midi#MidiEvent");
103 break;
107 activePlugins.add (this);
109 startTimer(1000);
112 ~JuceLV2Wrapper()
114 JUCE_AUTORELEASEPOOL
117 stopTimer();
119 delete filter;
120 filter = 0;
122 channels.free();
123 deleteTempChannels();
125 ports_ctrl.clear();
126 ports_ctrl_last.clear();
128 jassert (activePlugins.contains (this));
129 activePlugins.removeValue (this);
131 if (activePlugins.size() == 0)
133 shutdownJuce_GUI();
138 //==============================================================================
139 // LV2 Descriptor Calls
140 void do_connect_port(uint32_t port, void* data_location)
142 if (port < port_count)
144 int i;
145 uint32_t index = 0;
147 #if JucePlugin_WantsMidiInput
148 if (port == index) {
149 port_min = (LV2_Event_Buffer*)data_location;
150 return;
152 index += 1;
153 #endif
155 #if JucePlugin_ProducesMidiOutput
156 if (port == index) {
157 port_mout = (LV2_Event_Buffer*)data_location;
158 return;
160 index += 1;
161 #endif
163 for (i=0; i < numInChans; i++) {
164 if (port == index) {
165 ports_ain[i] = (float*)data_location;
166 return;
168 index += 1;
171 for (i=0; i < numOutChans; i++) {
172 if (port == index) {
173 ports_aout[i] = (float*)data_location;
174 return;
176 index += 1;
179 for (i=0; i < filter->getNumParameters(); i++) {
180 if (port == index) {
181 ports_ctrl.set(i, (float*)data_location);
182 return;
184 index += 1;
189 void do_activate()
191 if (filter != nullptr)
193 isProcessing = true;
194 channels.calloc (numInChans + numOutChans);
196 jassert (sample_rate > 0);
197 if (sample_rate <= 0.0)
198 sample_rate = 44100.0;
200 jassert (buffer_size > 0);
202 firstProcessCallback = true;
204 filter->setNonRealtime (false);
205 filter->setPlayConfigDetails (numInChans, numOutChans, sample_rate, buffer_size);
207 deleteTempChannels();
209 filter->prepareToPlay (sample_rate, buffer_size);
211 midiEvents.ensureSize (2048);
212 midiEvents.clear();
216 void do_run(uint32_t sample_count)
218 if (firstProcessCallback)
220 firstProcessCallback = false;
222 // if this fails, the host hasn't called resume() before processing
223 jassert (isProcessing);
225 // (tragically, some hosts actually need this, although it's stupid to have
226 // to do it here..)
227 if (! isProcessing)
228 do_activate();
230 filter->setNonRealtime (false);
232 #if JUCE_WINDOWS
233 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
234 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST)
235 filter->setNonRealtime (true);
236 #endif
239 // Check if buffer size changed
240 if (buffer_size != sample_count)
242 buffer_size = sample_count;
243 filter->setPlayConfigDetails(numInChans, numOutChans, sample_rate, buffer_size);
244 filter->prepareToPlay(sample_rate, buffer_size);
247 // Check for updated parameters
248 float cur_value;
249 for (int i = 0; i < ports_ctrl.size(); i++)
251 if (ports_ctrl[i] != nullptr)
253 cur_value = *(float*)ports_ctrl[i];
254 if (ports_ctrl_last[i] != cur_value) {
255 filter->setParameter(i, cur_value);
256 ports_ctrl_last.setUnchecked(i, cur_value);
261 jassert (activePlugins.contains (this));
264 const ScopedLock sl (filter->getCallbackLock());
266 const int numIn = numInChans;
267 const int numOut = numOutChans;
269 if (filter->isSuspended())
271 for (int i = 0; i < numOut; ++i)
272 zeromem (ports_aout[i], sizeof (float) * sample_count);
274 else
276 int i;
277 for (i = 0; i < numOut; ++i)
279 float* chan = tempChannels.getUnchecked(i);
281 if (chan == 0)
283 chan = ports_aout[i];
285 // if some output channels are disabled, some hosts supply the same buffer
286 // for multiple channels - this buggers up our method of copying the
287 // inputs over the outputs, so we need to create unique temp buffers in this case..
288 for (int j = i; --j >= 0;)
290 if (ports_aout[j] == chan)
292 chan = new float [buffer_size * 2];
293 tempChannels.set (i, chan);
294 break;
299 if (i < numIn && chan != ports_ain[i])
300 memcpy (chan, ports_ain[i], sizeof (float) * sample_count);
302 channels[i] = chan;
305 // LV2 MIDI Input
306 LV2_Event_Iterator iter;
307 lv2_event_begin(&iter, port_min);
309 lv2_event_buffer_reset(port_min, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(port_min + 1));
311 for (uint32_t i=0; i < iter.buf->event_count; ++i) {
312 uint8_t* data;
313 LV2_Event* ev = lv2_event_get(&iter, &data);
314 midiEvents.addEvent(data, ev->size, ev->frames);
315 lv2_event_increment(&iter);
318 for (; i < numIn; ++i)
319 channels[i] = ports_ain[i];
321 AudioSampleBuffer chans (channels, jmax (numIn, numOut), sample_count);
323 filter->processBlock (chans, midiEvents);
327 if (! midiEvents.isEmpty())
329 #if JucePlugin_ProducesMidiOutput
330 const int numEvents = midiEvents.getNumEvents();
332 LV2_Event_Iterator iter;
333 lv2_event_buffer_reset(port_mout, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(port_mout + 1));
334 lv2_event_begin(&iter, port_mout);
336 const JUCE_NAMESPACE::uint8* midiEventData;
337 int midiEventSize, midiEventPosition;
338 MidiBuffer::Iterator i (midiEvents);
340 while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
342 jassert (midiEventPosition >= 0 && midiEventPosition < sample_count);
344 lv2_event_write(&iter, midiEventPosition, 0, midi_uri_id, midiEventSize, midiEventData);
346 #endif
347 midiEvents.clear();
351 void do_deactivate()
353 if (filter != nullptr)
355 filter->releaseResources();
357 isProcessing = false;
358 channels.free();
360 deleteTempChannels();
364 void do_cleanup()
366 stopTimer();
368 if (descriptor)
370 free((void*)descriptor->URI);
371 delete descriptor;
375 //==============================================================================
376 // JUCE Stuff
378 // TODO - set/get chunk
380 AudioProcessor* getFilter() { return filter; }
382 void timerCallback()
384 if (chunkMemoryTime > 0 && chunkMemoryTime < JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000)
386 chunkMemoryTime = 0;
387 chunkMemory.setSize (0);
391 //==============================================================================
392 private:
393 AudioProcessor* filter;
394 JUCE_NAMESPACE::MemoryBlock chunkMemory;
395 JUCE_NAMESPACE::uint32 chunkMemoryTime;
396 MidiBuffer midiEvents;
397 int numInChans, numOutChans;
398 bool isProcessing, firstProcessCallback;
399 HeapBlock<float*> channels;
400 Array<float*> tempChannels; // see note in do_run()
402 const LV2_Descriptor* descriptor;
404 double sample_rate;
405 int buffer_size;
406 uint16_t midi_uri_id;
407 uint32_t port_count;
409 LV2_Event_Buffer* port_min;
410 LV2_Event_Buffer* port_mout;
411 float* ports_ain[JucePlugin_MaxNumInputChannels];
412 float* ports_aout[JucePlugin_MaxNumOutputChannels];
413 Array<float*> ports_ctrl;
414 Array<float> ports_ctrl_last;
416 //==============================================================================
417 void deleteTempChannels()
419 for (int i = tempChannels.size(); --i >= 0;)
420 delete[] (tempChannels.getUnchecked(i));
422 tempChannels.clear();
424 if (filter != nullptr)
425 tempChannels.insertMultiple (0, 0, filter->getNumInputChannels() + filter->getNumOutputChannels());
428 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLV2Wrapper);
431 //==============================================================================
432 // Create a new JUCE Editor
433 class JuceLv2Editor : public AudioProcessorListener
435 public:
436 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) :
437 wrapper(wrapper_),
438 ui_descriptor(ui_descriptor_),
439 write_function(write_function_),
440 controller(controller_),
441 widget(widget_),
442 editor(nullptr)
444 filter = wrapper->getFilter();
446 if (filter != nullptr)
448 printf("TEST - Before\n");
449 //editor = filter->createEditorIfNeeded();
450 printf("TEST - After\n");
453 // Padding for control ports
454 ctrl_pad = 0;
455 #if JucePlugin_WantsMidiInput
456 ctrl_pad += 1;
457 #endif
458 #if JucePlugin_ProducesMidiOutput
459 ctrl_pad += 1;
460 #endif
461 ctrl_pad += JucePlugin_MaxNumInputChannels;
462 ctrl_pad += JucePlugin_MaxNumOutputChannels;
465 ~JuceLv2Editor()
467 JUCE_AUTORELEASEPOOL
468 PopupMenu::dismissAllActiveMenus();
470 if (editor != nullptr)
471 deleteAndZero (editor);
474 void do_port_event(uint32_t port_index, float value)
476 filter->setParameter(port_index-ctrl_pad, value);
479 void do_cleanup()
481 // We should only do this after UI gets working
482 #if 0
483 if (ui_descriptor)
485 free((void*)ui_descriptor->URI);
486 delete ui_descriptor;
488 #endif
491 void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue)
493 if (controller && write_function)
494 write_function(controller, index+ctrl_pad, sizeof(float), 0, &newValue);
497 void audioProcessorChanged (AudioProcessor*) {}
499 private:
500 JuceLV2Wrapper* wrapper;
501 const LV2UI_Descriptor* ui_descriptor;
502 LV2UI_Write_Function write_function;
503 LV2UI_Controller controller;
504 LV2UI_Widget* widget;
506 AudioProcessor* filter;
507 AudioProcessorEditor* editor;
509 int ctrl_pad;
511 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2Editor);
514 //==============================================================================
515 // LV2 descriptor functions
516 LV2_Handle juce_lv2_instantiate(const LV2_Descriptor* descriptor, double sample_rate, const char* bundle_path, const LV2_Feature* const* features)
518 const MessageManagerLock mmLock;
519 JuceLV2Wrapper* wrapper = new JuceLV2Wrapper(descriptor, sample_rate, features);
520 return wrapper;
523 void juce_lv2_connect_port(LV2_Handle instance, uint32_t port, void* data_location)
525 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
526 wrapper->do_connect_port(port, data_location);
529 void juce_lv2_activate(LV2_Handle instance)
531 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
532 wrapper->do_activate();
535 void juce_lv2_run(LV2_Handle instance, uint32_t sample_count)
537 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
538 wrapper->do_run(sample_count);
541 void juce_lv2_deactivate(LV2_Handle instance)
543 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
544 wrapper->do_deactivate();
547 void juce_lv2_cleanup(LV2_Handle instance)
549 const MessageManagerLock mmLock;
550 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
551 wrapper->do_cleanup();
552 delete wrapper;
555 const void* juce_lv2_extension_data(const char* uri)
557 printf("TODO :: juce_lv2_extension_data()\n");
558 return nullptr;
561 //==============================================================================
562 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)
564 for (uint16_t i = 0; features[i]; i++)
566 if (strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0 && features[i]->data)
568 const MessageManagerLock mmLock;
569 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)features[i]->data;
570 JuceLv2Editor* editor = new JuceLv2Editor(wrapper, descriptor, write_function, controller, widget, features);
571 return editor;
574 printf("Host does not support instance data - cannot use UI\n");
575 return nullptr;
578 void juce_lv2ui_port_event(LV2UI_Handle instance, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void* buffer)
580 const MessageManagerLock mmLock;
581 JuceLv2Editor* editor = (JuceLv2Editor*)instance;
583 if (buffer_size == sizeof(float) && format == 0)
585 float value = *(float*)buffer;
586 editor->do_port_event(port_index, value);
590 void juce_lv2ui_cleanup(LV2UI_Handle instance)
592 const MessageManagerLock mmLock;
593 JuceLv2Editor* editor = (JuceLv2Editor*)instance;
594 editor->do_cleanup();
595 delete editor;
598 //==============================================================================
599 // Create new LV2 objects
600 LV2_Descriptor* getNewLv2Plugin()
602 LV2_Descriptor* const Lv2Plugin = new LV2_Descriptor;
603 Lv2Plugin->URI = strdup((const char*)get_uri().toUTF8());
604 Lv2Plugin->instantiate = juce_lv2_instantiate;
605 Lv2Plugin->connect_port = juce_lv2_connect_port;
606 Lv2Plugin->activate = juce_lv2_activate;
607 Lv2Plugin->run = juce_lv2_run;
608 Lv2Plugin->deactivate = juce_lv2_deactivate;
609 Lv2Plugin->cleanup = juce_lv2_cleanup;
610 Lv2Plugin->extension_data = juce_lv2_extension_data;
611 return Lv2Plugin;
614 LV2UI_Descriptor* getNewLv2UI(bool external)
616 LV2UI_Descriptor* Lv2UI = new LV2UI_Descriptor;
617 Lv2UI->URI = strdup((const char*) (external ? get_external_ui_uri() : get_juce_ui_uri()).toUTF8());
618 Lv2UI->instantiate = juce_lv2ui_instantiate;
619 Lv2UI->cleanup = juce_lv2ui_cleanup;
620 Lv2UI->port_event = juce_lv2ui_port_event;
621 Lv2UI->extension_data = juce_lv2_extension_data;
622 return Lv2UI;
625 //==============================================================================
626 // Mac startup code..
627 #if JUCE_MAC
629 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
631 initialiseMac();
632 return (index == 0) ? getNewLv2Plugin() : nullptr;
635 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
637 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
640 //==============================================================================
641 // Linux startup code..
642 #elif JUCE_LINUX
644 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
646 return (index == 0) ? getNewLv2Plugin() : nullptr;
649 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
651 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
654 // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash!
655 __attribute__((constructor)) void myPluginInit() {}
656 __attribute__((destructor)) void myPluginFini() {}
658 //==============================================================================
659 // Win32 startup code..
660 #else
662 extern "C" __declspec (dllexport) const LV2_Descriptor* lv2_descriptor(uint32_t index)
664 return (index == 0) ? getNewLv2Plugin() : nullptr;
667 extern "C" __declspec (dllexport) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
669 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
672 #endif
674 #endif