Cleanup, need to get non-UI stuff working first
[juce-lv2.git] / juce / source / src / audio / plugin_client / LV2 / juce_LV2_Wrapper.cpp
blob43d3e4c6b9d9b821d37c330654c7c39ff12d7ae0
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
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 hasShutdown (false),
53 firstProcessCallback (true),
54 descriptor (descriptor_),
55 sample_rate (sample_rate_),
56 buffer_size (512),
57 midi_uri_id (0),
58 port_count (0)
60 printf("JuceLV2Wrapper()\n");
61 filter = createPluginFilter();
62 filter->setPlayConfigDetails(numInChans, numOutChans, 0, 0);
64 // Port count
65 #if JucePlugin_WantsMidiInput
66 port_count += 1;
67 #endif
68 #if JucePlugin_ProducesMidiOutput
69 port_count += 1;
70 #endif
71 port_count += numInChans;
72 port_count += numOutChans;
73 port_count += filter->getNumParameters();
75 // Set Port data
76 port_min = nullptr;
77 port_mout = nullptr;
78 ports_ctrl.insertMultiple(0, nullptr, filter->getNumParameters());
79 ports_ctrl_last.insertMultiple(0, 0.0f, filter->getNumParameters());
81 for (int i=0; i < numInChans; i++) {
82 ports_ain[i] = nullptr;
85 for (int i=0; i < numOutChans; i++) {
86 ports_aout[i] = nullptr;
89 for (int i=0; i < filter->getNumParameters(); i++) {
90 ports_ctrl_last.set(i, filter->getParameter(i));
93 // Get MIDI URI Id
94 uint16_t j = 0;
95 while(features[j]) {
96 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;
101 j++;
104 activePlugins.add (this);
107 ~JuceLV2Wrapper()
109 JUCE_AUTORELEASEPOOL
112 hasShutdown = true;
114 delete filter;
115 filter = 0;
117 channels.free();
118 deleteTempChannels();
120 ports_ctrl.clear();
121 ports_ctrl_last.clear();
123 jassert (activePlugins.contains (this));
124 activePlugins.removeValue (this);
127 if (activePlugins.size() == 0)
129 shutdownJuce_GUI();
133 //==============================================================================
134 // LV2 Descriptor Calls
135 void do_connect_port(uint32_t port, void* data_location)
137 if (port < port_count) {
138 int i;
139 uint32_t index = 0;
141 #if JucePlugin_WantsMidiInput
142 if (port == index) {
143 port_min = data_location;
144 return;
146 index += 1;
147 #endif
149 #if JucePlugin_ProducesMidiOutput
150 if (port == index) {
151 port_mout = data_location;
152 return;
154 index += 1;
155 #endif
157 for (i=0; i < numInChans; i++) {
158 if (port == index) {
159 ports_ain[i] = (float*)data_location;
160 return;
162 index += 1;
165 for (i=0; i < numOutChans; i++) {
166 if (port == index) {
167 ports_aout[i] = (float*)data_location;
168 return;
170 index += 1;
173 for (i=0; i < filter->getNumParameters(); i++) {
174 if (port == index) {
175 ports_ctrl.set(i, (float*)data_location);
176 return;
178 index += 1;
183 void do_activate()
185 if (filter != nullptr) {
186 isProcessing = true;
187 channels.calloc (numInChans + numOutChans);
189 jassert (sample_rate > 0);
190 if (sample_rate <= 0.0)
191 sample_rate = 44100.0;
193 jassert (buffer_size > 0);
195 firstProcessCallback = true;
197 filter->setNonRealtime (false);
198 filter->setPlayConfigDetails (numInChans, numOutChans, sample_rate, buffer_size);
200 deleteTempChannels();
202 filter->prepareToPlay (sample_rate, buffer_size);
204 midiEvents.ensureSize (2048);
205 midiEvents.clear();
209 void do_run(uint32_t sample_count)
211 if (firstProcessCallback)
213 firstProcessCallback = false;
215 // if this fails, the host hasn't called resume() before processing
216 jassert (isProcessing);
218 // (tragically, some hosts actually need this, although it's stupid to have
219 // to do it here..)
220 if (! isProcessing)
221 do_activate();
223 filter->setNonRealtime (false);
225 #if JUCE_WINDOWS
226 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
227 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST)
228 filter->setNonRealtime (true);
229 #endif
232 // Check if buffer size changed
233 if (buffer_size != sample_count) {
234 buffer_size = sample_count;
235 filter->setPlayConfigDetails(numInChans, numOutChans, sample_rate, buffer_size);
236 filter->prepareToPlay(sample_rate, buffer_size);
239 // Check for updated parameters
240 float cur_value;
241 for (int i = 0; i < ports_ctrl.size(); i++) {
242 if (ports_ctrl[i] != nullptr) {
243 cur_value = *(float*)ports_ctrl[i];
244 if (ports_ctrl_last[i] != cur_value) {
245 filter->setParameter(i, cur_value);
246 ports_ctrl_last.setUnchecked(i, cur_value);
251 jassert (activePlugins.contains (this));
254 const ScopedLock sl (filter->getCallbackLock());
256 const int numIn = numInChans;
257 const int numOut = numOutChans;
259 if (filter->isSuspended())
261 for (int i = 0; i < numOut; ++i)
262 zeromem (ports_aout[i], sizeof (float) * sample_count);
264 else
266 int i;
267 for (i = 0; i < numOut; ++i)
269 float* chan = tempChannels.getUnchecked(i);
271 if (chan == 0)
273 chan = ports_aout[i];
275 // if some output channels are disabled, some hosts supply the same buffer
276 // for multiple channels - this buggers up our method of copying the
277 // inputs over the outputs, so we need to create unique temp buffers in this case..
278 for (int j = i; --j >= 0;)
280 if (ports_aout[j] == chan)
282 chan = new float [buffer_size * 2];
283 tempChannels.set (i, chan);
284 break;
289 if (i < numIn && chan != ports_ain[i])
290 memcpy (chan, ports_ain[i], sizeof (float) * sample_count);
292 channels[i] = chan;
295 // TODO - MIDI Input
297 for (; i < numIn; ++i)
298 channels[i] = ports_ain[i];
300 AudioSampleBuffer chans (channels, jmax (numIn, numOut), sample_count);
302 filter->processBlock (chans, midiEvents);
306 if (! midiEvents.isEmpty())
308 // TODO - MIDI Output
309 midiEvents.clear();
313 void do_deactivate()
315 if (filter != nullptr)
317 filter->releaseResources();
319 isProcessing = false;
320 channels.free();
322 deleteTempChannels();
326 void do_cleanup()
330 // TODO - set/get chunk
332 //==============================================================================
333 // JUCE Stuff, TODO
335 //==============================================================================
336 private:
337 AudioProcessor* filter;
338 JUCE_NAMESPACE::MemoryBlock chunkMemory;
339 JUCE_NAMESPACE::uint32 chunkMemoryTime;
340 MidiBuffer midiEvents;
341 int numInChans, numOutChans;
342 bool isProcessing, hasShutdown, firstProcessCallback;
343 HeapBlock<float*> channels;
344 Array<float*> tempChannels; // see note in do_run()
346 const LV2_Descriptor* descriptor;
348 double sample_rate;
349 int buffer_size;
350 uint16_t midi_uri_id;
351 uint32_t port_count;
353 void* port_min;
354 void* port_mout;
355 float* ports_ain[JucePlugin_MaxNumInputChannels];
356 float* ports_aout[JucePlugin_MaxNumOutputChannels];
357 Array<float*> ports_ctrl;
358 Array<float> ports_ctrl_last;
360 //==============================================================================
361 void deleteTempChannels()
363 for (int i = tempChannels.size(); --i >= 0;)
364 delete[] (tempChannels.getUnchecked(i));
366 tempChannels.clear();
368 if (filter != nullptr)
369 tempChannels.insertMultiple (0, 0, filter->getNumInputChannels() + filter->getNumOutputChannels());
372 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLV2Wrapper);
375 //==============================================================================
376 // LV2 descriptor functions
377 LV2_Handle juce_lv2_instantiate(const LV2_Descriptor* descriptor, double sample_rate, const char* bundle_path, const LV2_Feature* const* features)
379 JuceLV2Wrapper* wrapper = new JuceLV2Wrapper(descriptor, sample_rate, features);
380 return wrapper;
383 void juce_lv2_connect_port(LV2_Handle instance, uint32_t port, void* data_location)
385 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
386 wrapper->do_connect_port(port, data_location);
389 void juce_lv2_activate(LV2_Handle instance)
391 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
392 wrapper->do_activate();
395 void juce_lv2_run(LV2_Handle instance, uint32_t sample_count)
397 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
398 wrapper->do_run(sample_count);
401 void juce_lv2_deactivate(LV2_Handle instance)
403 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
404 wrapper->do_deactivate();
407 void juce_lv2_cleanup(LV2_Handle instance)
409 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
410 wrapper->do_cleanup();
412 //if (wrapper->descriptor)
414 // free((void*)wrapper->descriptor->URI);
415 // delete wrapper->descriptor;
418 delete wrapper;
421 const void* juce_lv2_extension_data(const char* uri)
423 printf("juce_lv2_extension_data()\n");
424 return nullptr;
427 //==============================================================================
428 #if 0
429 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)
431 printf("ui_instantiate()\n");
432 uint16_t i = 0;
433 while (features[i])
435 if (strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0 && features[i]->data) {
436 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)features[i]->data;
437 wrapper->do_ui_instantiate(descriptor->URI, write_function, controller, widget, features);
438 return wrapper;
440 i++;
442 printf("Host does not support instance data - cannot use UI\n");
443 return nullptr;
446 void juce_lv2ui_cleanup(LV2UI_Handle instance)
448 printf("ui_cleanup()\n");
449 if (!instance) return;
450 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
451 wrapper->do_ui_cleanup();
454 void juce_lv2ui_port_event(LV2UI_Handle instance, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void* buffer)
456 printf("ui_port_event()\n");
457 if (!instance) return;
458 if (buffer_size != sizeof(float) || format != 0) return;
459 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
460 float value = *(float*)buffer;
461 wrapper->do_ui_port_event(port_index, value);
463 #endif
465 //==============================================================================
466 // Create new LV2 objects
467 LV2_Descriptor* getNewLv2Plugin()
469 LV2_Descriptor* const Lv2Plugin = new LV2_Descriptor;
470 Lv2Plugin->URI = strdup((const char*)get_uri().toUTF8());
471 Lv2Plugin->instantiate = juce_lv2_instantiate;
472 Lv2Plugin->connect_port = juce_lv2_connect_port;
473 Lv2Plugin->activate = juce_lv2_activate;
474 Lv2Plugin->run = juce_lv2_run;
475 Lv2Plugin->deactivate = juce_lv2_deactivate;
476 Lv2Plugin->cleanup = juce_lv2_cleanup;
477 Lv2Plugin->extension_data = juce_lv2_extension_data;
478 return Lv2Plugin;
481 #if 0
482 LV2UI_Descriptor* getNewLv2UI(bool external)
484 LV2UI_Descriptor* Lv2UI = new LV2UI_Descriptor;
485 Lv2UI->URI = strdup((const char*) (external ? get_external_ui_uri() : get_juce_ui_uri()).toUTF8());
486 Lv2UI->instantiate = juce_lv2ui_instantiate;
487 Lv2UI->cleanup = juce_lv2ui_cleanup;
488 Lv2UI->port_event = juce_lv2ui_port_event;
489 Lv2UI->extension_data = juce_lv2_extension_data;
490 return Lv2UI;
492 #endif
494 //==============================================================================
495 // Mac startup code..
496 #if JUCE_MAC
498 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
500 initialiseMac();
501 return (index == 0) ? getNewLv2Plugin() : nullptr;
504 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
506 initialiseMac();
507 // 0 -> External UI; 1 -> JUCE UI
508 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
511 //==============================================================================
512 // Linux startup code..
513 #elif JUCE_LINUX
515 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
517 return (index == 0) ? getNewLv2Plugin() : nullptr;
520 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
522 // 0 -> External UI; 1 -> JUCE UI
523 return nullptr; //(index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
526 // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash!
527 __attribute__((constructor)) void myPluginInit() {}
528 __attribute__((destructor)) void myPluginFini() {}
530 //==============================================================================
531 // Win32 startup code..
532 #else
534 extern "C" __declspec (dllexport) const LV2_Descriptor* lv2_descriptor(uint32_t index)
536 return (index == 0) ? getNewLv2Plugin() : nullptr;
539 extern "C" __declspec (dllexport) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
541 // 0 -> External UI; 1 -> JUCE UI
542 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
545 #endif
547 #endif