Misc cleanup & fixes
[juce-lv2.git] / juce / source / src / audio / plugin_client / LV2 / juce_LV2_Wrapper.cpp
blob94ecb184b90c7ae96aaec33b6a027fd22107bbe6
1 /*
2 * JUCE LV2 wrapper
3 */
5 #include "../juce_IncludeCharacteristics.h"
7 #if JucePlugin_Build_LV2
9 // LV2 includes..
10 #include "lv2/lv2.h"
11 #include "lv2/event.h"
12 #include "lv2/event_helpers.h"
13 #include "lv2/instance_access.h"
14 #include "lv2/uri_map.h"
15 #include "lv2/ui.h"
16 #include "lv2/lv2_external_ui.h"
18 #include "../juce_PluginHeaders.h"
19 #include "../juce_PluginHostType.h"
21 //==============================================================================
22 // Same as juce_lv2_gen.cpp
23 String get_uri()
25 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":" JucePlugin_VersionString).replace(" ", "_");
28 String get_juce_ui_uri()
30 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":JUCE-Native-UI").replace(" ", "_");
33 String get_external_ui_uri()
35 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":JUCE-External-UI").replace(" ", "_");
38 static Array<void*> activePlugins;
40 extern AudioProcessor* JUCE_CALLTYPE createPluginFilter();
42 //==============================================================================
43 // Create a new JUCE LV2 Plugin
44 class JuceLV2Wrapper : private Timer
46 public:
47 JuceLV2Wrapper(const LV2_Descriptor* descriptor_, double sample_rate_, const LV2_Feature* const* features) :
48 chunkMemoryTime (0),
49 numInChans (JucePlugin_MaxNumInputChannels),
50 numOutChans (JucePlugin_MaxNumOutputChannels),
51 isProcessing (false),
52 firstProcessCallback (true),
53 descriptor (descriptor_),
54 sample_rate (sample_rate_),
55 buffer_size (512),
56 midi_uri_id (0),
57 port_count (0)
59 printf("JuceLV2Wrapper()\n");
60 filter = createPluginFilter();
61 filter->setPlayConfigDetails(numInChans, numOutChans, 0, 0);
63 // Port count
64 #if JucePlugin_WantsMidiInput
65 port_count += 1;
66 #endif
67 #if JucePlugin_ProducesMidiOutput
68 port_count += 1;
69 #endif
70 port_count += numInChans;
71 port_count += numOutChans;
72 port_count += filter->getNumParameters();
74 // Set Port data
75 port_min = nullptr;
76 port_mout = nullptr;
77 ports_ctrl.insertMultiple(0, nullptr, filter->getNumParameters());
78 ports_ctrl_last.insertMultiple(0, 0.0f, filter->getNumParameters());
80 for (int i=0; i < numInChans; i++) {
81 ports_ain[i] = nullptr;
84 for (int i=0; i < numOutChans; i++) {
85 ports_aout[i] = nullptr;
88 for (int i=0; i < filter->getNumParameters(); i++) {
89 ports_ctrl_last.set(i, filter->getParameter(i));
92 // Get MIDI URI Id
93 for (uint16_t j = 0; features[j]; j++)
95 if (strcmp(features[j]->URI, LV2_URI_MAP_URI) == 0)
97 LV2_URI_Map_Feature* uri_feature = (LV2_URI_Map_Feature*)features[j]->data;
98 midi_uri_id = uri_feature->uri_to_id(uri_feature->callback_data, LV2_EVENT_URI, "http://lv2plug.in/ns/ext/midi#MidiEvent");
99 break;
103 activePlugins.add (this);
105 startTimer(1000);
108 ~JuceLV2Wrapper()
110 JUCE_AUTORELEASEPOOL
113 stopTimer();
115 delete filter;
116 filter = 0;
118 channels.free();
119 deleteTempChannels();
121 ports_ctrl.clear();
122 ports_ctrl_last.clear();
124 jassert (activePlugins.contains (this));
125 activePlugins.removeValue (this);
129 //==============================================================================
130 // LV2 Descriptor Calls
131 void do_connect_port(uint32_t port, void* data_location)
133 if (port < port_count)
135 int i;
136 uint32_t index = 0;
138 #if JucePlugin_WantsMidiInput
139 if (port == index) {
140 port_min = (LV2_Event_Buffer*)data_location;
141 return;
143 index += 1;
144 #endif
146 #if JucePlugin_ProducesMidiOutput
147 if (port == index) {
148 port_mout = (LV2_Event_Buffer*)data_location;
149 return;
151 index += 1;
152 #endif
154 for (i=0; i < numInChans; i++) {
155 if (port == index) {
156 ports_ain[i] = (float*)data_location;
157 return;
159 index += 1;
162 for (i=0; i < numOutChans; i++) {
163 if (port == index) {
164 ports_aout[i] = (float*)data_location;
165 return;
167 index += 1;
170 for (i=0; i < filter->getNumParameters(); i++) {
171 if (port == index) {
172 ports_ctrl.set(i, (float*)data_location);
173 return;
175 index += 1;
180 void do_activate()
182 if (filter != nullptr)
184 isProcessing = true;
185 channels.calloc (numInChans + numOutChans);
187 jassert (sample_rate > 0);
188 if (sample_rate <= 0.0)
189 sample_rate = 44100.0;
191 jassert (buffer_size > 0);
193 firstProcessCallback = true;
195 filter->setNonRealtime (false);
196 filter->setPlayConfigDetails (numInChans, numOutChans, sample_rate, buffer_size);
198 deleteTempChannels();
200 filter->prepareToPlay (sample_rate, buffer_size);
202 midiEvents.ensureSize (2048);
203 midiEvents.clear();
207 void do_run(uint32_t sample_count)
209 if (firstProcessCallback)
211 firstProcessCallback = false;
213 // if this fails, the host hasn't called resume() before processing
214 jassert (isProcessing);
216 // (tragically, some hosts actually need this, although it's stupid to have
217 // to do it here..)
218 if (! isProcessing)
219 do_activate();
221 filter->setNonRealtime (false);
223 #if JUCE_WINDOWS
224 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
225 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST)
226 filter->setNonRealtime (true);
227 #endif
230 // Check if buffer size changed
231 if (buffer_size != sample_count)
233 buffer_size = sample_count;
234 filter->setPlayConfigDetails(numInChans, numOutChans, sample_rate, buffer_size);
235 filter->prepareToPlay(sample_rate, buffer_size);
238 // Check for updated parameters
239 float cur_value;
240 for (int i = 0; i < ports_ctrl.size(); i++)
242 if (ports_ctrl[i] != nullptr)
244 cur_value = *(float*)ports_ctrl[i];
245 if (ports_ctrl_last[i] != cur_value) {
246 filter->setParameter(i, cur_value);
247 ports_ctrl_last.setUnchecked(i, cur_value);
252 jassert (activePlugins.contains (this));
255 const ScopedLock sl (filter->getCallbackLock());
257 const int numIn = numInChans;
258 const int numOut = numOutChans;
260 if (filter->isSuspended())
262 for (int i = 0; i < numOut; ++i)
263 zeromem (ports_aout[i], sizeof (float) * sample_count);
265 else
267 int i;
268 for (i = 0; i < numOut; ++i)
270 float* chan = tempChannels.getUnchecked(i);
272 if (chan == 0)
274 chan = ports_aout[i];
276 // if some output channels are disabled, some hosts supply the same buffer
277 // for multiple channels - this buggers up our method of copying the
278 // inputs over the outputs, so we need to create unique temp buffers in this case..
279 for (int j = i; --j >= 0;)
281 if (ports_aout[j] == chan)
283 chan = new float [buffer_size * 2];
284 tempChannels.set (i, chan);
285 break;
290 if (i < numIn && chan != ports_ain[i])
291 memcpy (chan, ports_ain[i], sizeof (float) * sample_count);
293 channels[i] = chan;
296 // LV2 MIDI Input
297 LV2_Event_Iterator iter;
298 lv2_event_begin(&iter, port_min);
300 lv2_event_buffer_reset(port_min, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(port_min + 1));
302 for (uint32_t i=0; i < iter.buf->event_count; ++i) {
303 uint8_t* data;
304 LV2_Event* ev = lv2_event_get(&iter, &data);
305 midiEvents.addEvent(data, ev->size, ev->frames);
306 lv2_event_increment(&iter);
309 for (; i < numIn; ++i)
310 channels[i] = ports_ain[i];
312 AudioSampleBuffer chans (channels, jmax (numIn, numOut), sample_count);
314 filter->processBlock (chans, midiEvents);
318 if (! midiEvents.isEmpty())
320 #if JucePlugin_ProducesMidiOutput
321 const int numEvents = midiEvents.getNumEvents();
323 LV2_Event_Iterator iter;
324 lv2_event_buffer_reset(port_mout, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(port_mout + 1));
325 lv2_event_begin(&iter, port_mout);
327 const JUCE_NAMESPACE::uint8* midiEventData;
328 int midiEventSize, midiEventPosition;
329 MidiBuffer::Iterator i (midiEvents);
331 while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
333 jassert (midiEventPosition >= 0 && midiEventPosition < sample_count);
335 lv2_event_write(&iter, midiEventPosition, 0, midi_uri_id, midiEventSize, midiEventData);
337 #endif
338 midiEvents.clear();
342 void do_deactivate()
344 if (filter != nullptr)
346 filter->releaseResources();
348 isProcessing = false;
349 channels.free();
351 deleteTempChannels();
355 void do_cleanup()
357 stopTimer();
359 if (descriptor)
361 free((void*)descriptor->URI);
362 delete descriptor;
366 //==============================================================================
367 // JUCE Stuff
369 // TODO - set/get chunk
371 AudioProcessor* getFilter() { return filter; }
373 void timerCallback()
375 if (chunkMemoryTime > 0 && chunkMemoryTime < JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000)
377 chunkMemoryTime = 0;
378 chunkMemory.setSize (0);
382 //==============================================================================
383 private:
384 AudioProcessor* filter;
385 JUCE_NAMESPACE::MemoryBlock chunkMemory;
386 JUCE_NAMESPACE::uint32 chunkMemoryTime;
387 MidiBuffer midiEvents;
388 int numInChans, numOutChans;
389 bool isProcessing, firstProcessCallback;
390 HeapBlock<float*> channels;
391 Array<float*> tempChannels; // see note in do_run()
393 const LV2_Descriptor* descriptor;
395 double sample_rate;
396 int buffer_size;
397 uint16_t midi_uri_id;
398 uint32_t port_count;
400 LV2_Event_Buffer* port_min;
401 LV2_Event_Buffer* port_mout;
402 float* ports_ain[JucePlugin_MaxNumInputChannels];
403 float* ports_aout[JucePlugin_MaxNumOutputChannels];
404 Array<float*> ports_ctrl;
405 Array<float> ports_ctrl_last;
407 //==============================================================================
408 void deleteTempChannels()
410 for (int i = tempChannels.size(); --i >= 0;)
411 delete[] (tempChannels.getUnchecked(i));
413 tempChannels.clear();
415 if (filter != nullptr)
416 tempChannels.insertMultiple (0, 0, filter->getNumInputChannels() + filter->getNumOutputChannels());
419 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLV2Wrapper);
422 //==============================================================================
423 // Create a new JUCE Editor
424 class JuceLv2Editor : public AudioProcessorListener
426 public:
427 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) :
428 wrapper(wrapper_),
429 ui_descriptor(ui_descriptor_),
430 write_function(write_function_),
431 controller(controller_),
432 widget(widget_)
434 initialiseJuce_GUI();
436 filter = wrapper->getFilter();
438 //printf("TEST - Before\n");
439 //editor = filter->createEditorIfNeeded();
440 //printf("TEST - After\n");
442 // Padding for control ports
443 ctrl_pad = 0;
444 #if JucePlugin_WantsMidiInput
445 ctrl_pad += 1;
446 #endif
447 #if JucePlugin_ProducesMidiOutput
448 ctrl_pad += 1;
449 #endif
450 ctrl_pad += JucePlugin_MaxNumInputChannels;
451 ctrl_pad += JucePlugin_MaxNumOutputChannels;
454 ~JuceLv2Editor()
456 deleteAndZero (editor);
457 shutdownJuce_GUI();
460 void do_port_event(uint32_t port_index, float value)
462 filter->setParameter(port_index-ctrl_pad, value);
465 void do_cleanup()
467 if (ui_descriptor)
469 free((void*)ui_descriptor->URI);
470 delete ui_descriptor;
474 void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue)
476 if (controller && write_function)
477 write_function(controller, index+ctrl_pad, sizeof(float), 0, &newValue);
480 void audioProcessorChanged (AudioProcessor*) {}
482 private:
483 JuceLV2Wrapper* wrapper;
484 const LV2UI_Descriptor* ui_descriptor;
485 LV2UI_Write_Function write_function;
486 LV2UI_Controller controller;
487 LV2UI_Widget* widget;
489 AudioProcessor* filter;
490 AudioProcessorEditor* editor;
492 int ctrl_pad;
494 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2Editor);
497 //==============================================================================
498 // LV2 descriptor functions
499 LV2_Handle juce_lv2_instantiate(const LV2_Descriptor* descriptor, double sample_rate, const char* bundle_path, const LV2_Feature* const* features)
501 JuceLV2Wrapper* wrapper = new JuceLV2Wrapper(descriptor, sample_rate, features);
502 return wrapper;
505 void juce_lv2_connect_port(LV2_Handle instance, uint32_t port, void* data_location)
507 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
508 wrapper->do_connect_port(port, data_location);
511 void juce_lv2_activate(LV2_Handle instance)
513 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
514 wrapper->do_activate();
517 void juce_lv2_run(LV2_Handle instance, uint32_t sample_count)
519 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
520 wrapper->do_run(sample_count);
523 void juce_lv2_deactivate(LV2_Handle instance)
525 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
526 wrapper->do_deactivate();
529 void juce_lv2_cleanup(LV2_Handle instance)
531 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
532 wrapper->do_cleanup();
533 delete wrapper;
536 const void* juce_lv2_extension_data(const char* uri)
538 printf("TODO :: juce_lv2_extension_data()\n");
539 return nullptr;
542 //==============================================================================
543 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)
545 for (uint16_t i = 0; features[i]; i++)
547 if (strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0 && features[i]->data)
549 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)features[i]->data;
550 JuceLv2Editor* editor = new JuceLv2Editor(wrapper, descriptor, write_function, controller, widget, features);
551 return editor;
554 printf("Host does not support instance data - cannot use UI\n");
555 return nullptr;
558 void juce_lv2ui_port_event(LV2UI_Handle instance, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void* buffer)
560 JuceLv2Editor* editor = (JuceLv2Editor*)instance;
562 if (buffer_size == sizeof(float) && format == 0)
564 float value = *(float*)buffer;
565 editor->do_port_event(port_index, value);
569 void juce_lv2ui_cleanup(LV2UI_Handle instance)
571 JuceLv2Editor* editor = (JuceLv2Editor*)instance;
572 editor->do_cleanup();
573 delete editor;
576 //==============================================================================
577 // Create new LV2 objects
578 LV2_Descriptor* getNewLv2Plugin()
580 LV2_Descriptor* const Lv2Plugin = new LV2_Descriptor;
581 Lv2Plugin->URI = strdup((const char*)get_uri().toUTF8());
582 Lv2Plugin->instantiate = juce_lv2_instantiate;
583 Lv2Plugin->connect_port = juce_lv2_connect_port;
584 Lv2Plugin->activate = juce_lv2_activate;
585 Lv2Plugin->run = juce_lv2_run;
586 Lv2Plugin->deactivate = juce_lv2_deactivate;
587 Lv2Plugin->cleanup = juce_lv2_cleanup;
588 Lv2Plugin->extension_data = juce_lv2_extension_data;
589 return Lv2Plugin;
592 LV2UI_Descriptor* getNewLv2UI(bool external)
594 LV2UI_Descriptor* Lv2UI = new LV2UI_Descriptor;
595 Lv2UI->URI = strdup((const char*) (external ? get_external_ui_uri() : get_juce_ui_uri()).toUTF8());
596 Lv2UI->instantiate = juce_lv2ui_instantiate;
597 Lv2UI->cleanup = juce_lv2ui_cleanup;
598 Lv2UI->port_event = juce_lv2ui_port_event;
599 Lv2UI->extension_data = juce_lv2_extension_data;
600 return Lv2UI;
603 //==============================================================================
604 // Mac startup code..
605 #if JUCE_MAC
607 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
609 initialiseMac();
610 return (index == 0) ? getNewLv2Plugin() : nullptr;
613 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
615 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
618 //==============================================================================
619 // Linux startup code..
620 #elif JUCE_LINUX
622 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
624 return (index == 0) ? getNewLv2Plugin() : nullptr;
627 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
629 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
632 // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash!
633 __attribute__((constructor)) void myPluginInit() {}
634 __attribute__((destructor)) void myPluginFini() {}
636 //==============================================================================
637 // Win32 startup code..
638 #else
640 extern "C" __declspec (dllexport) const LV2_Descriptor* lv2_descriptor(uint32_t index)
642 return (index == 0) ? getNewLv2Plugin() : nullptr;
645 extern "C" __declspec (dllexport) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
647 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
650 #endif
652 #endif