5 #include "../juce_IncludeCharacteristics.h"
7 #define JucePlugin_Build_LV2 1
9 #if JucePlugin_Build_LV2
13 #include "lv2/event.h"
14 #include "lv2/event_helpers.h"
15 #include "lv2/instance_access.h"
16 #include "lv2/uri_map.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
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
49 JuceLV2Wrapper(const LV2_Descriptor
* descriptor_
, double sample_rate_
, const LV2_Feature
* const* features
) :
51 numInChans (JucePlugin_MaxNumInputChannels
),
52 numOutChans (JucePlugin_MaxNumOutputChannels
),
54 firstProcessCallback (true),
55 descriptor (descriptor_
),
56 sample_rate (sample_rate_
),
61 printf("JuceLV2Wrapper()\n");
64 filter
= createPluginFilter();
65 filter
->setPlayConfigDetails(numInChans
, numOutChans
, 0, 0);
68 #if JucePlugin_WantsMidiInput
71 #if JucePlugin_ProducesMidiOutput
74 port_count
+= numInChans
;
75 port_count
+= numOutChans
;
76 port_count
+= filter
->getNumParameters();
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
));
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");
107 activePlugins
.add (this);
123 deleteTempChannels();
126 ports_ctrl_last
.clear();
128 jassert (activePlugins
.contains (this));
129 activePlugins
.removeValue (this);
131 if (activePlugins
.size() == 0)
138 //==============================================================================
139 // LV2 Descriptor Calls
140 void do_connect_port(uint32_t port
, void* data_location
)
142 if (port
< port_count
)
147 #if JucePlugin_WantsMidiInput
149 port_min
= (LV2_Event_Buffer
*)data_location
;
155 #if JucePlugin_ProducesMidiOutput
157 port_mout
= (LV2_Event_Buffer
*)data_location
;
163 for (i
=0; i
< numInChans
; i
++) {
165 ports_ain
[i
] = (float*)data_location
;
171 for (i
=0; i
< numOutChans
; i
++) {
173 ports_aout
[i
] = (float*)data_location
;
179 for (i
=0; i
< filter
->getNumParameters(); i
++) {
181 ports_ctrl
.set(i
, (float*)data_location
);
191 if (filter
!= nullptr)
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);
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
230 filter
->setNonRealtime (false);
233 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
234 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST
)
235 filter
->setNonRealtime (true);
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
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
);
277 for (i
= 0; i
< numOut
; ++i
)
279 float* chan
= tempChannels
.getUnchecked(i
);
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
);
299 if (i
< numIn
&& chan
!= ports_ain
[i
])
300 memcpy (chan
, ports_ain
[i
], sizeof (float) * sample_count
);
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
) {
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
);
353 if (filter
!= nullptr)
355 filter
->releaseResources();
357 isProcessing
= false;
360 deleteTempChannels();
370 free((void*)descriptor
->URI
);
375 //==============================================================================
378 // TODO - set/get chunk
380 AudioProcessor
* getFilter() { return filter
; }
384 if (chunkMemoryTime
> 0 && chunkMemoryTime
< JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000)
387 chunkMemory
.setSize (0);
391 //==============================================================================
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
;
406 uint16_t midi_uri_id
;
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
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
) :
438 ui_descriptor(ui_descriptor_
),
439 write_function(write_function_
),
440 controller(controller_
),
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
455 #if JucePlugin_WantsMidiInput
458 #if JucePlugin_ProducesMidiOutput
461 ctrl_pad
+= JucePlugin_MaxNumInputChannels
;
462 ctrl_pad
+= JucePlugin_MaxNumOutputChannels
;
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
);
481 // We should only do this after UI gets working
485 free((void*)ui_descriptor
->URI
);
486 delete ui_descriptor
;
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
*) {}
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
;
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
);
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();
555 const void* juce_lv2_extension_data(const char* uri
)
557 printf("TODO :: juce_lv2_extension_data()\n");
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
);
574 printf("Host does not support instance data - cannot use UI\n");
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();
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
;
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
;
625 //==============================================================================
626 // Mac startup code..
629 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor
* lv2_descriptor(uint32_t index
)
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..
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..
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;