5 #include "../juce_IncludeCharacteristics.h"
7 #if JucePlugin_Build_LV2
11 #include "lv2/event.h"
12 #include "lv2/event_helpers.h"
13 #include "lv2/instance_access.h"
14 #include "lv2/uri_map.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
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
47 JuceLV2Wrapper(const LV2_Descriptor
* descriptor_
, double sample_rate_
, const LV2_Feature
* const* features
) :
49 numInChans (JucePlugin_MaxNumInputChannels
),
50 numOutChans (JucePlugin_MaxNumOutputChannels
),
53 firstProcessCallback (true),
54 descriptor (descriptor_
),
55 sample_rate (sample_rate_
),
60 printf("JuceLV2Wrapper()\n");
61 filter
= createPluginFilter();
62 filter
->setPlayConfigDetails(numInChans
, numOutChans
, 0, 0);
65 #if JucePlugin_WantsMidiInput
68 #if JucePlugin_ProducesMidiOutput
71 port_count
+= numInChans
;
72 port_count
+= numOutChans
;
73 port_count
+= filter
->getNumParameters();
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
));
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");
104 activePlugins
.add (this);
118 deleteTempChannels();
121 ports_ctrl_last
.clear();
123 jassert (activePlugins
.contains (this));
124 activePlugins
.removeValue (this);
127 if (activePlugins
.size() == 0)
133 //==============================================================================
134 // LV2 Descriptor Calls
135 void do_connect_port(uint32_t port
, void* data_location
)
137 if (port
< port_count
) {
141 #if JucePlugin_WantsMidiInput
143 port_min
= data_location
;
149 #if JucePlugin_ProducesMidiOutput
151 port_mout
= data_location
;
157 for (i
=0; i
< numInChans
; i
++) {
159 ports_ain
[i
] = (float*)data_location
;
165 for (i
=0; i
< numOutChans
; i
++) {
167 ports_aout
[i
] = (float*)data_location
;
173 for (i
=0; i
< filter
->getNumParameters(); i
++) {
175 ports_ctrl
.set(i
, (float*)data_location
);
185 if (filter
!= nullptr) {
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);
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
223 filter
->setNonRealtime (false);
226 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
227 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST
)
228 filter
->setNonRealtime (true);
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
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
);
267 for (i
= 0; i
< numOut
; ++i
)
269 float* chan
= tempChannels
.getUnchecked(i
);
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
);
289 if (i
< numIn
&& chan
!= ports_ain
[i
])
290 memcpy (chan
, ports_ain
[i
], sizeof (float) * sample_count
);
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
315 if (filter
!= nullptr)
317 filter
->releaseResources();
319 isProcessing
= false;
322 deleteTempChannels();
330 // TODO - set/get chunk
332 //==============================================================================
335 //==============================================================================
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
;
350 uint16_t midi_uri_id
;
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
);
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;
421 const void* juce_lv2_extension_data(const char* uri
)
423 printf("juce_lv2_extension_data()\n");
427 //==============================================================================
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");
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
);
442 printf("Host does not support instance data - cannot use UI\n");
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
);
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
;
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
;
494 //==============================================================================
495 // Mac startup code..
498 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor
* lv2_descriptor(uint32_t index
)
501 return (index
== 0) ? getNewLv2Plugin() : nullptr;
504 extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor
* lv2ui_descriptor(uint32_t index
)
507 // 0 -> External UI; 1 -> JUCE UI
508 return (index
<= 1) ? getNewLv2UI((index
== 0)) : nullptr;
511 //==============================================================================
512 // Linux startup code..
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..
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;