5 #include "../juce_IncludeCharacteristics.h"
7 #if JucePlugin_Build_LV2
17 #include "lv2/event.h"
18 #include "lv2/event_helpers.h"
19 #include "lv2/instance_access.h"
20 #include "lv2/uri_map.h"
22 #include "lv2/lv2_external_ui.h"
24 // These are dummy values!
32 kPlugCategSpacializer
,
35 kPlugCategRestoration
,
36 kPlugCategOfflineProcess
,
40 extern AudioProcessor
* JUCE_CALLTYPE
createPluginFilter();
42 String
name_to_symbol(String Name
, uint32_t port_index
)
44 String Symbol
= Name
.trimStart().trimEnd().replace(" ", "_").toLowerCase();
48 Symbol
+= String(port_index
);
52 for (int i
=0; i
< Symbol
.length(); i
++)
54 if (std::isalpha(Symbol
[i
]) || std::isdigit(Symbol
[i
]) || Symbol
[i
] == '_') {
65 String
float_to_string(float value
)
67 if (value
< 0.0f
|| value
> 1.0f
) {
68 std::cerr
<< "WARNING - Parameter uses out-of-bounds value -> " << value
<< std::endl
;
71 if (!string
.contains(".")) {
79 return String("urn:" JucePlugin_Manufacturer
":" JucePlugin_Name
":" JucePlugin_VersionString
).replace(" ", "_");
82 String
get_juce_ui_uri()
84 return String("urn:" JucePlugin_Manufacturer
":" JucePlugin_Name
":JUCE-Native-UI").replace(" ", "_");
87 String
get_external_ui_uri()
89 return String("urn:" JucePlugin_Manufacturer
":" JucePlugin_Name
":JUCE-External-UI").replace(" ", "_");
92 String
get_binary_name()
94 return String(JucePlugin_Name
).replace(" ", "_");
97 String
get_plugin_type()
101 switch (JucePlugin_VSTCategory
) {
102 case kPlugCategSynth
:
103 ptype
+= "lv2:InstrumentPlugin";
105 case kPlugCategAnalysis
:
106 ptype
+= "lv2:AnalyserPlugin";
108 case kPlugCategMastering
:
109 ptype
+= "lv2:DynamicsPlugin";
111 case kPlugCategSpacializer
:
112 ptype
+= "lv2:SpatialPlugin";
114 case kPlugCategRoomFx
:
115 ptype
+= "lv2:ModulatorPlugin";
117 case kPlugCategRestoration
:
118 ptype
+= "lv2:UtilityPlugin";
120 case kPlugCategGenerator
:
121 ptype
+= "lv2:GeneratorPlugin";
125 if (ptype
.isNotEmpty()) {
129 ptype
+= "lv2:Plugin";
133 String
get_manifest_ttl(String URI
, String Binary
)
136 manifest
+= "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n";
137 manifest
+= "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
139 manifest
+= "<" + URI
+ ">\n";
140 manifest
+= " a lv2:Plugin ;\n";
141 manifest
+= " lv2:binary <" + Binary
+ ".so> ;\n";
142 manifest
+= " rdfs:seeAlso <" + Binary
+".ttl> .\n";
146 String
get_plugin_ttl(String URI
, String Binary
)
148 uint32_t i
, port_index
= 0;
149 AudioProcessor
* filter
= createPluginFilter();
152 plugin
+= "@prefix doap: <http://usefulinc.com/ns/doap#> .\n";
153 //plugin += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n";
154 //plugin += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
155 plugin
+= "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n";
156 plugin
+= "@prefix lv2ev: <http://lv2plug.in/ns/ext/event#> .\n";
157 plugin
+= "@prefix lv2ui: <http://lv2plug.in/ns/extensions/ui#> .\n";
160 if (filter
->hasEditor()) {
162 plugin
+= "<" + get_juce_ui_uri() + ">\n";
163 plugin
+= " a lv2ui:JUCEUI ;\n";
164 plugin
+= " lv2ui:binary <" + Binary
+ ".so> .\n";
166 plugin
+= "<" + get_external_ui_uri() + ">\n";
167 plugin
+= " a lv2ui:external ;\n";
168 plugin
+= " lv2ui:binary <" + Binary
+ ".so> .\n";
172 plugin
+= "<" + URI
+ ">\n";
173 plugin
+= " a " + get_plugin_type() + " ;\n";
175 if (filter
->hasEditor()) {
177 plugin
+= " lv2ui:ui <" + get_juce_ui_uri() + ">;\n";
179 plugin
+= " lv2ui:ui <" + get_external_ui_uri() + ">;\n";
184 #if JucePlugin_WantsMidiInput
185 plugin
+= " lv2:port [\n";
186 plugin
+= " a lv2:InputPort, lv2ev:EventPort;\n";
187 plugin
+= " lv2ev:supportsEvent <http://lv2plug.in/ns/ext/midi#MidiEvent> ;\n";
188 plugin
+= " lv2:index " + String(port_index
++) + ";\n";
189 plugin
+= " lv2:symbol \"midi_in\";\n";
190 plugin
+= " lv2:name \"MIDI Input\";\n";
191 plugin
+= " ] ;\n\n";
194 #if JucePlugin_ProducesMidiOutput
195 plugin
+= " lv2:port [\n";
196 plugin
+= " a lv2:OutputPort, lv2ev:EventPort;\n";
197 plugin
+= " lv2ev:supportsEvent <http://lv2plug.in/ns/ext/midi#MidiEvent> ;\n";
198 plugin
+= " lv2:index " + String(port_index
++) + ";\n";
199 plugin
+= " lv2:symbol \"midi_out\";\n";
200 plugin
+= " lv2:name \"MIDI Output\";\n";
201 plugin
+= " ] ;\n\n";
204 for (i
=0; i
<JucePlugin_MaxNumInputChannels
; i
++) {
206 plugin
+= " lv2:port [\n";
211 plugin
+= " a lv2:InputPort, lv2:AudioPort;\n";
212 //plugin += " lv2:datatype lv2:float;\n";
213 plugin
+= " lv2:index " + String(port_index
++) + ";\n";
214 plugin
+= " lv2:symbol \"audio_in_" + String(i
+1) + "\";\n";
215 plugin
+= " lv2:name \"Audio Input " + String(i
+1) + "\";\n";
217 if (i
== JucePlugin_MaxNumInputChannels
-1) {
218 plugin
+= " ] ;\n\n";
224 for (i
=0; i
<JucePlugin_MaxNumOutputChannels
; i
++) {
226 plugin
+= " lv2:port [\n";
231 plugin
+= " a lv2:OutputPort, lv2:AudioPort;\n";
232 //plugin += " lv2:datatype lv2:float;\n";
233 plugin
+= " lv2:index " + String(port_index
++) + ";\n";
234 plugin
+= " lv2:symbol \"audio_out_" + String(i
+1) + "\";\n";
235 plugin
+= " lv2:name \"Audio Output " + String(i
+1) + "\";\n";
237 if (i
== JucePlugin_MaxNumOutputChannels
-1) {
238 plugin
+= " ] ;\n\n";
244 for (i
=0; i
< filter
->getNumParameters(); i
++) {
246 plugin
+= " lv2:port [\n";
251 plugin
+= " a lv2:InputPort;\n";
252 plugin
+= " a lv2:ControlPort;\n";
253 //plugin += " lv2:datatype lv2:float;\n";
254 plugin
+= " lv2:index " + String(port_index
++) + ";\n";
255 plugin
+= " lv2:symbol \"" + name_to_symbol(filter
->getParameterName(i
), i
) + "\";\n";
256 plugin
+= " lv2:name \"" + filter
->getParameterName(i
) + "\";\n";
257 plugin
+= " lv2:default " + float_to_string(filter
->getParameter(i
)) + ";\n";
258 plugin
+= " lv2:minimum 0.0;\n";
259 plugin
+= " lv2:maximum 1.0;\n";
261 if (i
== filter
->getNumParameters()-1) {
262 plugin
+= " ] ;\n\n";
270 plugin
+= " doap:name \"" + String(JucePlugin_Name
) + "\" ;\n";
271 plugin
+= " doap:creator \"" + String(JucePlugin_Manufacturer
) + "\" .\n";
279 String URI
= get_uri();
280 String Binary
= get_binary_name();
281 String BinaryTTL
= Binary
+ ".ttl";
283 std::cout
<< "Writing manifest.ttl...";
284 std::fstream
manifest("manifest.ttl", std::ios::out
);
285 manifest
<< get_manifest_ttl(URI
, Binary
) << std::endl
;
287 std::cout
<< " done!" << std::endl
;
289 std::cout
<< "Writing " << BinaryTTL
<< "...";
290 std::fstream
plugin(BinaryTTL
.toUTF8(), std::ios::out
);
291 plugin
<< get_plugin_ttl(URI
, Binary
) << std::endl
;
293 std::cout
<< " done!" << std::endl
;
296 //==============================================================================
298 extern void juce_callAnyTimersSynchronously();
301 extern void initialiseMac();
305 //==============================================================================
308 class SharedMessageThread
: public Thread
311 SharedMessageThread()
312 : Thread ("Lv2MessageThread"),
317 while (! initialised
)
321 ~SharedMessageThread()
323 signalThreadShouldExit();
324 JUCEApplication::quit();
325 waitForThreadToExit (5000);
326 clearSingletonInstance();
331 initialiseJuce_GUI();
334 MessageManager::getInstance()->setCurrentThreadAsMessageThread();
336 while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250))
340 juce_DeclareSingleton (SharedMessageThread
, false);
346 juce_ImplementSingleton (SharedMessageThread
)
350 static Array
<void*> activePlugins
;
352 //==============================================================================
353 // Create a new JUCE LV2 Plugin
354 class JuceLV2Wrapper
:
356 public AudioProcessorListener
359 JuceLV2Wrapper(const LV2_Descriptor
* descriptor_
, double sample_rate_
, const LV2_Feature
* const* features
) :
361 numInChans (JucePlugin_MaxNumInputChannels
),
362 numOutChans (JucePlugin_MaxNumOutputChannels
),
363 isProcessing (false),
364 firstProcessCallback (true),
365 descriptor (descriptor_
),
366 sample_rate (sample_rate_
),
371 printf("JuceLV2Wrapper()\n");
373 filter
= createPluginFilter();
374 jassert(filter
!= nullptr);
376 filter
->setPlayConfigDetails(numInChans
, numOutChans
, 0, 0);
379 #if JucePlugin_WantsMidiInput
382 #if JucePlugin_ProducesMidiOutput
385 port_count
+= numInChans
;
386 port_count
+= numOutChans
;
387 port_count
+= filter
->getNumParameters();
392 ports_ctrl
.insertMultiple(0, nullptr, filter
->getNumParameters());
393 ports_ctrl_last
.insertMultiple(0, 0.0f
, filter
->getNumParameters());
395 for (int i
=0; i
< numInChans
; i
++) {
396 ports_ain
[i
] = nullptr;
399 for (int i
=0; i
< numOutChans
; i
++) {
400 ports_aout
[i
] = nullptr;
403 for (int i
=0; i
< filter
->getNumParameters(); i
++) {
404 ports_ctrl_last
.set(i
, filter
->getParameter(i
));
408 for (uint16_t j
= 0; features
[j
]; j
++)
410 if (strcmp(features
[j
]->URI
, LV2_URI_MAP_URI
) == 0)
412 LV2_URI_Map_Feature
* uri_feature
= (LV2_URI_Map_Feature
*)features
[j
]->data
;
413 midi_uri_id
= uri_feature
->uri_to_id(uri_feature
->callback_data
, LV2_EVENT_URI
, "http://lv2plug.in/ns/ext/midi#MidiEvent");
418 activePlugins
.add (this);
431 deleteTempChannels();
434 ports_ctrl_last
.clear();
436 jassert (activePlugins
.contains (this));
437 activePlugins
.removeValue (this);
440 //==============================================================================
441 // LV2 Descriptor Calls
442 void do_connect_port(uint32_t port
, void* data_location
)
444 if (port
< port_count
)
449 #if JucePlugin_WantsMidiInput
451 port_min
= (LV2_Event_Buffer
*)data_location
;
457 #if JucePlugin_ProducesMidiOutput
459 port_mout
= (LV2_Event_Buffer
*)data_location
;
465 for (i
=0; i
< numInChans
; i
++) {
467 ports_ain
[i
] = (float*)data_location
;
473 for (i
=0; i
< numOutChans
; i
++) {
475 ports_aout
[i
] = (float*)data_location
;
481 for (i
=0; i
< filter
->getNumParameters(); i
++) {
483 ports_ctrl
.set(i
, (float*)data_location
);
493 if (filter
!= nullptr)
496 channels
.calloc (numInChans
+ numOutChans
);
498 jassert (sample_rate
> 0);
499 if (sample_rate
<= 0.0)
500 sample_rate
= 44100.0;
502 jassert (buffer_size
> 0);
504 firstProcessCallback
= true;
506 filter
->setNonRealtime (false);
507 filter
->setPlayConfigDetails (numInChans
, numOutChans
, sample_rate
, buffer_size
);
509 deleteTempChannels();
511 filter
->prepareToPlay (sample_rate
, buffer_size
);
513 midiEvents
.ensureSize (2048);
520 if (filter
!= nullptr)
522 filter
->releaseResources();
524 isProcessing
= false;
527 deleteTempChannels();
531 void do_run(uint32_t sample_count
)
533 if (firstProcessCallback
)
535 firstProcessCallback
= false;
537 // if this fails, the host hasn't called resume() before processing
538 jassert (isProcessing
);
540 // (tragically, some hosts actually need this, although it's stupid to have
545 filter
->setNonRealtime (false);
548 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
549 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST
)
550 filter
->setNonRealtime (true);
554 // Check if buffer size changed
555 if (buffer_size
!= sample_count
)
557 buffer_size
= sample_count
;
558 filter
->setPlayConfigDetails(numInChans
, numOutChans
, sample_rate
, buffer_size
);
559 filter
->prepareToPlay(sample_rate
, buffer_size
);
562 // Check for updated parameters
564 for (int i
= 0; i
< ports_ctrl
.size(); i
++)
566 if (ports_ctrl
[i
] != nullptr)
568 cur_value
= *(float*)ports_ctrl
[i
];
569 if (ports_ctrl_last
[i
] != cur_value
) {
570 filter
->setParameter(i
, cur_value
);
571 ports_ctrl_last
.setUnchecked(i
, cur_value
);
576 jassert (activePlugins
.contains (this));
579 const ScopedLock
sl (filter
->getCallbackLock());
581 const int numIn
= numInChans
;
582 const int numOut
= numOutChans
;
584 if (filter
->isSuspended())
586 for (int i
= 0; i
< numOut
; ++i
)
587 zeromem (ports_aout
[i
], sizeof (float) * sample_count
);
592 for (i
= 0; i
< numOut
; ++i
)
594 float* chan
= tempChannels
.getUnchecked(i
);
598 chan
= ports_aout
[i
];
600 // if some output channels are disabled, some hosts supply the same buffer
601 // for multiple channels - this buggers up our method of copying the
602 // inputs over the outputs, so we need to create unique temp buffers in this case..
603 for (int j
= i
; --j
>= 0;)
605 if (ports_aout
[j
] == chan
)
607 chan
= new float [buffer_size
* 2];
608 tempChannels
.set (i
, chan
);
614 if (i
< numIn
&& chan
!= ports_ain
[i
])
615 memcpy (chan
, ports_ain
[i
], sizeof (float) * sample_count
);
621 if (port_min
!= nullptr)
623 LV2_Event_Iterator iter
;
624 lv2_event_begin(&iter
, port_min
);
626 lv2_event_buffer_reset(port_min
, LV2_EVENT_AUDIO_STAMP
, (uint8_t*)(port_min
+ 1));
628 for (uint32_t i
=0; i
< iter
.buf
->event_count
; ++i
) {
630 LV2_Event
* ev
= lv2_event_get(&iter
, &data
);
631 midiEvents
.addEvent(data
, ev
->size
, ev
->frames
);
632 lv2_event_increment(&iter
);
636 for (; i
< numIn
; ++i
)
637 channels
[i
] = ports_ain
[i
];
639 AudioSampleBuffer
chans (channels
, jmax (numIn
, numOut
), sample_count
);
641 filter
->processBlock (chans
, midiEvents
);
645 if (! midiEvents
.isEmpty() && port_mout
!= nullptr)
647 #if JucePlugin_ProducesMidiOutput
648 const int numEvents
= midiEvents
.getNumEvents();
650 LV2_Event_Iterator iter
;
651 lv2_event_buffer_reset(port_mout
, LV2_EVENT_AUDIO_STAMP
, (uint8_t*)(port_mout
+ 1));
652 lv2_event_begin(&iter
, port_mout
);
654 const JUCE_NAMESPACE::uint8
* midiEventData
;
655 int midiEventSize
, midiEventPosition
;
656 MidiBuffer::Iterator
i (midiEvents
);
658 while (i
.getNextEvent (midiEventData
, midiEventSize
, midiEventPosition
))
660 jassert (midiEventPosition
>= 0 && midiEventPosition
< sample_count
);
662 lv2_event_write(&iter
, midiEventPosition
, 0, midi_uri_id
, midiEventSize
, midiEventData
);
675 // free((void*)descriptor->URI);
676 // delete descriptor;
680 //==============================================================================
683 AudioProcessor
* getFilter() { return filter
; }
685 void audioProcessorParameterChanged (AudioProcessor
*, int index
, float newValue
)
690 void audioProcessorChanged (AudioProcessor
*)
695 //==============================================================================
696 int32_t getChunk (void** data
)
698 if (filter
== nullptr)
701 chunkMemory
.setSize (0);
702 filter
->getCurrentProgramStateInformation (chunkMemory
);
704 *data
= (void*) chunkMemory
.getData();
706 // because the chunk is only needed temporarily by the host (or at least you'd
707 // hope so) we'll give it a while and then free it in the timer callback.
708 chunkMemoryTime
= JUCE_NAMESPACE::Time::getApproximateMillisecondCounter();
710 return (int32_t) chunkMemory
.getSize();
713 void setChunk (void* data
, int32_t byteSize
)
715 if (filter
== nullptr)
718 chunkMemory
.setSize (0);
721 if (byteSize
> 0 && data
!= nullptr)
723 filter
->setCurrentProgramStateInformation (data
, byteSize
);
729 if (chunkMemoryTime
> 0 && chunkMemoryTime
< JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000)
732 chunkMemory
.setSize (0);
736 void doIdleCallback()
738 if (MessageManager::getInstance()->isThisTheMessageThread())
741 juce_callAnyTimersSynchronously();
743 for (int i
= ComponentPeer::getNumPeers(); --i
>= 0;)
744 ComponentPeer::getPeer (i
)->performAnyPendingRepaintsNow();
748 //==============================================================================
750 AudioProcessor
* filter
;
751 JUCE_NAMESPACE::MemoryBlock chunkMemory
;
752 JUCE_NAMESPACE::uint32 chunkMemoryTime
;
753 MidiBuffer midiEvents
;
754 int numInChans
, numOutChans
;
755 bool isProcessing
, firstProcessCallback
;
756 HeapBlock
<float*> channels
;
757 Array
<float*> tempChannels
; // see note in do_run()
759 const LV2_Descriptor
* descriptor
;
763 uint16_t midi_uri_id
;
766 LV2_Event_Buffer
* port_min
;
767 LV2_Event_Buffer
* port_mout
;
768 float* ports_ain
[JucePlugin_MaxNumInputChannels
];
769 float* ports_aout
[JucePlugin_MaxNumOutputChannels
];
770 Array
<float*> ports_ctrl
;
771 Array
<float> ports_ctrl_last
;
773 //==============================================================================
774 void deleteTempChannels()
776 for (int i
= tempChannels
.size(); --i
>= 0;)
777 delete[] (tempChannels
.getUnchecked(i
));
779 tempChannels
.clear();
781 if (filter
!= nullptr)
782 tempChannels
.insertMultiple (0, 0, filter
->getNumInputChannels() + filter
->getNumOutputChannels());
785 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLV2Wrapper
);
788 //==============================================================================
789 // LV2 descriptor functions
790 LV2_Handle
juce_lv2_instantiate(const LV2_Descriptor
* descriptor
, double sample_rate
, const char* bundle_path
, const LV2_Feature
* const* features
)
792 JUCE_AUTORELEASEPOOL
;
793 initialiseJuce_GUI();
796 MessageManagerLock mmLock
;
799 JuceLV2Wrapper
* wrapper
= new JuceLV2Wrapper(descriptor
, sample_rate
, features
);
803 void juce_lv2_connect_port(LV2_Handle instance
, uint32_t port
, void* data_location
)
805 JuceLV2Wrapper
* wrapper
= (JuceLV2Wrapper
*)instance
;
806 wrapper
->do_connect_port(port
, data_location
);
809 void juce_lv2_activate(LV2_Handle instance
)
811 JuceLV2Wrapper
* wrapper
= (JuceLV2Wrapper
*)instance
;
812 wrapper
->do_activate();
815 void juce_lv2_run(LV2_Handle instance
, uint32_t sample_count
)
817 JuceLV2Wrapper
* wrapper
= (JuceLV2Wrapper
*)instance
;
818 wrapper
->do_run(sample_count
);
821 void juce_lv2_deactivate(LV2_Handle instance
)
823 JuceLV2Wrapper
* wrapper
= (JuceLV2Wrapper
*)instance
;
824 wrapper
->do_deactivate();
827 void juce_lv2_cleanup(LV2_Handle instance
)
829 JUCE_AUTORELEASEPOOL
;
832 MessageManagerLock mmLock
;
835 JuceLV2Wrapper
* wrapper
= (JuceLV2Wrapper
*)instance
;
836 wrapper
->do_cleanup();
839 if (activePlugins
.size() == 0)
842 SharedMessageThread::deleteInstance();
848 const void* juce_lv2_extension_data(const char* uri
)
850 printf("TODO :: juce_lv2_extension_data()\n");
854 //==============================================================================
855 // Create new LV2 objects
856 LV2_Descriptor
* getNewLv2Plugin()
858 LV2_Descriptor
* const Lv2Plugin
= new LV2_Descriptor
;
859 Lv2Plugin
->URI
= strdup((const char*)get_uri().toUTF8());
860 Lv2Plugin
->instantiate
= juce_lv2_instantiate
;
861 Lv2Plugin
->connect_port
= juce_lv2_connect_port
;
862 Lv2Plugin
->activate
= juce_lv2_activate
;
863 Lv2Plugin
->run
= juce_lv2_run
;
864 Lv2Plugin
->deactivate
= juce_lv2_deactivate
;
865 Lv2Plugin
->cleanup
= juce_lv2_cleanup
;
866 Lv2Plugin
->extension_data
= juce_lv2_extension_data
;
870 //==============================================================================
871 // Mac startup code..
874 extern "C" __attribute__ ((visibility("default"))) void juce_lv2_ttl_generator()
879 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor
* lv2_descriptor(uint32_t index
)
882 return (index
== 0) ? getNewLv2Plugin() : nullptr;
885 //==============================================================================
886 // Linux startup code..
889 extern "C" __attribute__ ((visibility("default"))) void juce_lv2_ttl_generator()
894 extern "C" __attribute__ ((visibility("default"))) const LV2_Descriptor
* lv2_descriptor(uint32_t index
)
896 SharedMessageThread::getInstance();
897 return (index
== 0) ? getNewLv2Plugin() : nullptr;
900 // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash!
901 __attribute__((constructor
)) void myPluginInit() {}
902 __attribute__((destructor
)) void myPluginFini() {}
904 //==============================================================================
905 // Win32 startup code..
908 extern "C" __declspec (dllexport
) void juce_lv2_ttl_generator()
913 extern "C" __declspec (dllexport
) const LV2_Descriptor
* lv2_descriptor(uint32_t index
)
915 return (index
== 0) ? getNewLv2Plugin() : nullptr;