Started with UI stuff again, slowly
[juce-lv2.git] / juce / source / src / audio / plugin_client / LV2 / juce_LV2_Wrapper.cpp
blob17bf00fd40be34e35506c4014effd656ff9161b1
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 = (LV2_Event_Buffer*)data_location;
144 return;
146 index += 1;
147 #endif
149 #if JucePlugin_ProducesMidiOutput
150 if (port == index) {
151 port_mout = (LV2_Event_Buffer*)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 // LV2 MIDI Input
296 LV2_Event_Iterator iter;
297 lv2_event_begin(&iter, port_min);
299 lv2_event_buffer_reset(port_min, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(port_min + 1));
301 for (uint32_t i=0; i < iter.buf->event_count; ++i) {
302 uint8_t* data;
303 LV2_Event* ev = lv2_event_get(&iter, &data);
304 midiEvents.addEvent(data, ev->size, ev->frames);
305 lv2_event_increment(&iter);
308 for (; i < numIn; ++i)
309 channels[i] = ports_ain[i];
311 AudioSampleBuffer chans (channels, jmax (numIn, numOut), sample_count);
313 filter->processBlock (chans, midiEvents);
317 if (! midiEvents.isEmpty())
319 #if JucePlugin_ProducesMidiOutput
320 const int numEvents = midiEvents.getNumEvents();
322 LV2_Event_Iterator iter;
323 lv2_event_buffer_reset(port_mout, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(port_mout + 1));
324 lv2_event_begin(&iter, port_mout);
326 const JUCE_NAMESPACE::uint8* midiEventData;
327 int midiEventSize, midiEventPosition;
328 MidiBuffer::Iterator i (midiEvents);
330 while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
332 jassert (midiEventPosition >= 0 && midiEventPosition < sample_count);
334 lv2_event_write(&iter, midiEventPosition, 0, midi_uri_id, midiEventSize, midiEventData);
336 #endif
337 midiEvents.clear();
341 void do_deactivate()
343 if (filter != nullptr)
345 filter->releaseResources();
347 isProcessing = false;
348 channels.free();
350 deleteTempChannels();
354 void do_cleanup()
358 // TODO - set/get chunk
360 //==============================================================================
361 // JUCE Stuff, TODO
363 //==============================================================================
364 private:
365 AudioProcessor* filter;
366 JUCE_NAMESPACE::MemoryBlock chunkMemory;
367 JUCE_NAMESPACE::uint32 chunkMemoryTime;
368 MidiBuffer midiEvents;
369 int numInChans, numOutChans;
370 bool isProcessing, hasShutdown, firstProcessCallback;
371 HeapBlock<float*> channels;
372 Array<float*> tempChannels; // see note in do_run()
374 const LV2_Descriptor* descriptor;
376 double sample_rate;
377 int buffer_size;
378 uint16_t midi_uri_id;
379 uint32_t port_count;
381 LV2_Event_Buffer* port_min;
382 LV2_Event_Buffer* port_mout;
383 float* ports_ain[JucePlugin_MaxNumInputChannels];
384 float* ports_aout[JucePlugin_MaxNumOutputChannels];
385 Array<float*> ports_ctrl;
386 Array<float> ports_ctrl_last;
388 //==============================================================================
389 void deleteTempChannels()
391 for (int i = tempChannels.size(); --i >= 0;)
392 delete[] (tempChannels.getUnchecked(i));
394 tempChannels.clear();
396 if (filter != nullptr)
397 tempChannels.insertMultiple (0, 0, filter->getNumInputChannels() + filter->getNumOutputChannels());
400 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLV2Wrapper);
403 //==============================================================================
404 // LV2 descriptor functions
405 LV2_Handle juce_lv2_instantiate(const LV2_Descriptor* descriptor, double sample_rate, const char* bundle_path, const LV2_Feature* const* features)
407 JuceLV2Wrapper* wrapper = new JuceLV2Wrapper(descriptor, sample_rate, features);
408 return wrapper;
411 void juce_lv2_connect_port(LV2_Handle instance, uint32_t port, void* data_location)
413 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
414 wrapper->do_connect_port(port, data_location);
417 void juce_lv2_activate(LV2_Handle instance)
419 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
420 wrapper->do_activate();
423 void juce_lv2_run(LV2_Handle instance, uint32_t sample_count)
425 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
426 wrapper->do_run(sample_count);
429 void juce_lv2_deactivate(LV2_Handle instance)
431 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
432 wrapper->do_deactivate();
435 void juce_lv2_cleanup(LV2_Handle instance)
437 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)instance;
438 wrapper->do_cleanup();
440 //if (wrapper->descriptor)
442 // free((void*)wrapper->descriptor->URI);
443 // delete wrapper->descriptor;
446 delete wrapper;
449 const void* juce_lv2_extension_data(const char* uri)
451 printf("juce_lv2_extension_data()\n");
452 return nullptr;
455 //==============================================================================
456 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)
458 printf("ui_instantiate()\n");
459 uint16_t i = 0;
460 while (features[i])
462 if (strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0 && features[i]->data) {
463 JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)features[i]->data;
464 // TODO
466 i++;
468 printf("Host does not support instance data - cannot use UI\n");
469 return nullptr;
472 void juce_lv2ui_cleanup(LV2UI_Handle instance)
474 printf("ui_cleanup()\n");
475 // TODO
478 void juce_lv2ui_port_event(LV2UI_Handle instance, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void* buffer)
480 printf("ui_port_event()\n");
482 if (buffer_size == sizeof(float) && format == 0)
484 float value = *(float*)buffer;
485 // TODO
489 //==============================================================================
490 // Create new LV2 objects
491 LV2_Descriptor* getNewLv2Plugin()
493 LV2_Descriptor* const Lv2Plugin = new LV2_Descriptor;
494 Lv2Plugin->URI = strdup((const char*)get_uri().toUTF8());
495 Lv2Plugin->instantiate = juce_lv2_instantiate;
496 Lv2Plugin->connect_port = juce_lv2_connect_port;
497 Lv2Plugin->activate = juce_lv2_activate;
498 Lv2Plugin->run = juce_lv2_run;
499 Lv2Plugin->deactivate = juce_lv2_deactivate;
500 Lv2Plugin->cleanup = juce_lv2_cleanup;
501 Lv2Plugin->extension_data = juce_lv2_extension_data;
502 return Lv2Plugin;
505 LV2UI_Descriptor* getNewLv2UI(bool external)
507 LV2UI_Descriptor* Lv2UI = new LV2UI_Descriptor;
508 Lv2UI->URI = strdup((const char*) (external ? get_external_ui_uri() : get_juce_ui_uri()).toUTF8());
509 Lv2UI->instantiate = juce_lv2ui_instantiate;
510 Lv2UI->cleanup = juce_lv2ui_cleanup;
511 Lv2UI->port_event = juce_lv2ui_port_event;
512 Lv2UI->extension_data = juce_lv2_extension_data;
513 return Lv2UI;
516 //==============================================================================
517 // Mac startup code..
518 #if JUCE_MAC
520 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
522 initialiseMac();
523 return (index == 0) ? getNewLv2Plugin() : nullptr;
526 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
528 initialiseJuce_GUI();
529 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
532 //==============================================================================
533 // Linux startup code..
534 #elif JUCE_LINUX
536 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor* lv2_descriptor(uint32_t index)
538 return (index == 0) ? getNewLv2Plugin() : nullptr;
541 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
543 initialiseJuce_GUI();
544 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
547 // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash!
548 __attribute__((constructor)) void myPluginInit() {}
549 __attribute__((destructor)) void myPluginFini() {}
551 //==============================================================================
552 // Win32 startup code..
553 #else
555 extern "C" __declspec (dllexport) const LV2_Descriptor* lv2_descriptor(uint32_t index)
557 return (index == 0) ? getNewLv2Plugin() : nullptr;
560 extern "C" __declspec (dllexport) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
562 initialiseJuce_GUI();
563 return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr;
566 #endif
568 #endif