Fix UI RDF info
[juce-lv2.git] / juce / source / src / audio / plugin_client / LV2 / juce_LV2_Wrapper.cpp
blobe3ce2bfeae16afd3ede42ed6ff5b1e0e0dcb6fcb
1 /*
2 * JUCE LV2 wrapper
3 */
5 #include "../juce_IncludeCharacteristics.h"
7 #if JucePlugin_Build_LV2
9 #include "juce.h"
11 // LV2 includes
12 #include "lv2.h"
13 #include "lv2_ui.h"
15 /* Same as juce_lv2_gen.cpp */
16 String get_uri()
18 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":" JucePlugin_VersionString).replace(" ", "_");
21 String get_juce_ui_uri()
23 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":JUCE-Native-UI").replace(" ", "_");
26 String get_external_ui_uri()
28 return String("urn:" JucePlugin_Manufacturer ":" JucePlugin_Name ":JUCE-External-UI").replace(" ", "_");
31 enum JuceLv2PortType {
32 JUCE_LV2_PORT_TYPE_NONE,
33 JUCE_LV2_PORT_TYPE_MIDI,
34 JUCE_LV2_PORT_TYPE_AUDIO,
35 JUCE_LV2_PORT_TYPE_CONTROL
38 struct JuceLv2Port {
39 JuceLv2PortType type;
40 uint32_t index;
41 void* data;
44 int get_parameters_padding()
46 int padding = 0;
47 #if JucePlugin_WantsMidiInput
48 padding += 1;
49 #endif
50 #if JucePlugin_ProducesMidiOutput
51 padding += 1;
52 #endif
53 padding += JucePlugin_MaxNumInputChannels;
54 padding += JucePlugin_MaxNumOutputChannels;
55 return padding;
58 //==============================================================================
59 /** Somewhere in the codebase of your plugin, you need to implement this function
60 and make it create an instance of the filter subclass that you're building.
62 extern AudioProcessor* JUCE_CALLTYPE createPluginFilter();
64 static Array<void*> activePlugins;
66 /* Create a new JUCE LV2 Plugin */
67 class JuceLV2Wrapper : private Timer
69 public:
70 JuceLV2Wrapper(const LV2_Descriptor* _descriptor, double _sample_rate, const LV2_Feature* const* _features) :
71 numInChans (JucePlugin_MaxNumInputChannels),
72 numOutChans (JucePlugin_MaxNumOutputChannels),
73 isProcessing (false),
74 hasShutdown (false),
75 firstProcessCallback (true),
76 shouldDeleteEditor (false)
78 printf("JuceLV2Wrapper()\n");
79 filter = createPluginFilter();
81 filter->setPlayConfigDetails(numInChans, numOutChans, 0, 0);
83 descriptor = _descriptor;
84 //features = _features;
86 sample_rate = _sample_rate;
87 buffer_size = 512;
88 port_count = 0;
90 #if JucePlugin_WantsMidiInput
91 port_min = nullptr;
92 port_count += 1;
93 #endif
94 #if JucePlugin_ProducesMidiOutput
95 port_mout = nullptr;
96 port_count += 1;
97 #endif
99 port_count += numInChans;
100 port_count += numOutChans;
101 port_count += filter->getNumParameters();
103 ports_ctrl.insertMultiple(0, nullptr, filter->getNumParameters());
104 ports_ctrl_last.insertMultiple(0, 0.0f, filter->getNumParameters());
106 for (int i=0; i < numInChans; i++) {
107 ports_ain[i] = nullptr;
110 for (int i=0; i < numOutChans; i++) {
111 ports_aout[i] = nullptr;
114 for (int i=0; i < filter->getNumParameters(); i++) {
115 ports_ctrl_last.set(i, filter->getParameter(i));
118 midi_uri_id = 0; // TODO URI-Map Extension
120 activePlugins.add (this);
123 ~JuceLV2Wrapper()
125 printf("~JuceLV2Wrapper()\n");
126 channels.free();
127 ports_ctrl.clear();
128 ports_ctrl_last.clear();
129 deleteAndZero(filter);
132 //==============================================================================
133 // LV2 Descriptor Calls
134 void do_connect_port(uint32_t port, void* data_location)
136 if (port < port_count) {
137 int i;
138 uint32_t index = 0;
140 #if JucePlugin_WantsMidiInput
141 if (port == index) {
142 port_min = data_location;
143 return;
145 index += 1;
146 #endif
148 #if JucePlugin_ProducesMidiOutput
149 if (port == index) {
150 port_mout = data_location;
151 return;
153 index += 1;
154 #endif
156 for (i=0; i < numInChans; i++) {
157 if (port == index) {
158 ports_ain[i] = (float*)data_location;
159 return;
161 index += 1;
164 for (i=0; i < numOutChans; i++) {
165 if (port == index) {
166 ports_aout[i] = (float*)data_location;
167 return;
169 index += 1;
172 for (i=0; i < filter->getNumParameters(); i++) {
173 if (port == index) {
174 ports_ctrl.set(i, (float*)data_location);
175 return;
177 index += 1;
182 void do_activate()
184 if (filter != nullptr) {
185 isProcessing = true;
186 channels.calloc (numInChans + numOutChans);
188 jassert (sample_rate > 0);
189 if (sample_rate <= 0.0)
190 sample_rate = 44100.0;
192 jassert (buffer_size > 0);
194 firstProcessCallback = true;
196 filter->setNonRealtime (false);
197 filter->setPlayConfigDetails (numInChans, numOutChans, sample_rate, buffer_size);
199 deleteTempChannels();
201 filter->prepareToPlay (sample_rate, buffer_size);
203 midiEvents.ensureSize (2048);
204 midiEvents.clear();
208 void do_run(uint32_t sample_count)
210 if (firstProcessCallback)
212 firstProcessCallback = false;
214 // if this fails, the host hasn't called resume() before processing
215 jassert (isProcessing);
217 // (tragically, some hosts actually need this, although it's stupid to have
218 // to do it here..)
219 if (! isProcessing)
220 do_activate();
222 filter->setNonRealtime (false);
224 #if JUCE_WINDOWS
225 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
226 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST)
227 filter->setNonRealtime (true);
228 #endif
231 // Change buffer size if needed
232 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;
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 for (; i < numIn; ++i)
296 channels[i] = ports_ain[i];
298 AudioSampleBuffer chans (channels, jmax (numIn, numOut), sample_count);
300 filter->processBlock (chans, midiEvents);
304 if (! midiEvents.isEmpty())
306 midiEvents.clear();
310 void do_deactivate()
312 if (filter != nullptr)
314 filter->releaseResources();
316 isProcessing = false;
317 channels.free();
319 deleteTempChannels();
323 //==============================================================================
324 // JUCE Stuff
325 void timerCallback()
327 if (shouldDeleteEditor)
329 shouldDeleteEditor = false;
330 //deleteEditor (true);
334 //==============================================================================
335 private:
336 MidiBuffer midiEvents;
337 int numInChans, numOutChans;
338 bool isProcessing, hasShutdown, firstProcessCallback, shouldDeleteEditor;
339 HeapBlock<float*> channels;
340 Array<float*> tempChannels; // see note in processReplacing()
342 AudioProcessor* filter;
343 const LV2_Descriptor* descriptor;
345 double sample_rate;
346 int buffer_size;
347 uint16_t midi_uri_id;
348 uint32_t port_count;
350 void* port_min;
351 void* port_mout;
352 float* ports_ain[JucePlugin_MaxNumInputChannels];
353 float* ports_aout[JucePlugin_MaxNumOutputChannels];
354 Array<float*> ports_ctrl;
355 Array<float> ports_ctrl_last;
357 //==============================================================================
358 void deleteTempChannels()
360 for (int i = tempChannels.size(); --i >= 0;)
361 delete[] (tempChannels.getUnchecked(i));
363 tempChannels.clear();
365 if (filter != nullptr)
366 tempChannels.insertMultiple (0, 0, filter->getNumInputChannels() + filter->getNumOutputChannels());
369 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLV2Wrapper);
372 //==============================================================================
373 // LV2 descriptor functions
374 LV2_Handle juce_lv2_instantiate(const LV2_Descriptor* descriptor, double sample_rate, const char* bundle_path, const LV2_Feature* const* features)
376 JuceLV2Wrapper* wrapper = new JuceLV2Wrapper(descriptor, sample_rate, features);
377 return wrapper;
380 void juce_lv2_connect_port(LV2_Handle instance, uint32_t port, void* data_location)
382 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
383 wrapper->do_connect_port(port, data_location);
386 void juce_lv2_activate(LV2_Handle instance)
388 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
389 wrapper->do_activate();
392 void juce_lv2_run(LV2_Handle instance, uint32_t sample_count)
394 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
395 wrapper->do_run(sample_count);
398 void juce_lv2_deactivate(LV2_Handle instance)
400 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
401 wrapper->do_deactivate();
404 void juce_lv2_cleanup(LV2_Handle instance)
406 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
407 //free((void*)wrapper->descriptor->URI);
408 //delete wrapper->descriptor;
409 delete wrapper;
412 const void* juce_lv2_extension_data(const char* uri)
414 printf("juce_lv2_extension_data()\n");
415 return nullptr;
418 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)
420 uint16_t i = 0;
421 while (features[i++])
423 printf("%i\n", i);
424 printf("feature #%i -> %s\n", i, features[i]->URI);
426 printf("ui_instantiate()");
427 return nullptr;
430 void juce_lv2ui_cleanup(LV2UI_Handle instance)
432 printf("ui_cleanup()");
435 void juce_lv2ui_port_event(LV2UI_Handle ui, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void* buffer)
437 printf("ui_port_event()");
440 //==============================================================================
441 // Create new LV2 objects
442 LV2_Descriptor* getNewLv2Plugin()
444 LV2_Descriptor* const Lv2Plugin = new LV2_Descriptor;
445 Lv2Plugin->URI = strdup((const char*)get_uri().toUTF8());
446 Lv2Plugin->instantiate = juce_lv2_instantiate;
447 Lv2Plugin->connect_port = juce_lv2_connect_port;
448 Lv2Plugin->activate = juce_lv2_activate;
449 Lv2Plugin->run = juce_lv2_run;
450 Lv2Plugin->deactivate = juce_lv2_deactivate;
451 Lv2Plugin->cleanup = juce_lv2_cleanup;
452 Lv2Plugin->extension_data = juce_lv2_extension_data;
453 return Lv2Plugin;
456 LV2UI_Descriptor* getNewLv2UI(bool external)
458 LV2UI_Descriptor* Lv2UI = new LV2UI_Descriptor;
459 Lv2UI->URI = strdup((const char*) (external ? get_external_ui_uri() : get_juce_ui_uri()).toUTF8());
460 Lv2UI->instantiate = juce_lv2ui_instantiate;
461 Lv2UI->cleanup = juce_lv2ui_cleanup;
462 Lv2UI->port_event = juce_lv2ui_port_event;
463 Lv2UI->extension_data = juce_lv2_extension_data;
464 return Lv2UI;
467 //==============================================================================
468 // Mac startup code..
469 #if JUCE_MAC
471 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
473 initialiseMac();
474 return (index == 0) ? getNewLv2Plugin() : nullptr;
477 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
479 initialiseMac();
480 // 0 -> External UI; 1 -> JUCE UI
481 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
484 //==============================================================================
485 // Linux startup code..
486 #elif JUCE_LINUX
488 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
490 return (index == 0) ? getNewLv2Plugin() : nullptr;
493 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
495 // 0 -> External UI; 1 -> JUCE UI
496 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
499 // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash!
500 __attribute__((constructor)) void myPluginInit() {}
501 __attribute__((destructor)) void myPluginFini() {}
503 //==============================================================================
504 // Win32 startup code..
505 #else
507 extern "C" __declspec (dllexport) const LV2_Descriptor* lv2_descriptor(uint32_t index)
509 return (index == 0) ? getNewLv2Plugin() : nullptr;
512 extern "C" __declspec (dllexport) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
514 // 0 -> External UI; 1 -> JUCE UI
515 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
518 #endif
520 #endif