26#if JUCE_PLUGINHOST_LV2 && (! (JUCE_ANDROID || JUCE_IOS))
28#include "juce_LV2Common.h"
29#include "juce_LV2Resources.h"
31#include <juce_gui_extra/native/juce_NSViewFrameWatcher_mac.h>
40template <
typename Struct,
typename Value>
43 s.*
member = std::move (value);
67 return reinterpret_cast<const LV2_Atom*
> (ptr);
76 constexpr SimpleSpan (T* beginIn, T* endIn) : b (beginIn), e (endIn) {}
78 constexpr auto begin()
const {
return b; }
79 constexpr auto end()
const {
return e; }
81 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4814)
82 constexpr auto& operator[] (
size_t index) {
return b[index]; }
83 JUCE_END_IGNORE_WARNINGS_MSVC
85 constexpr auto size()
const {
return (
size_t) (e - b); }
100 virtual ~PhysicalResizeListener() =
default;
101 virtual void viewRequestedResizeInPhysicalPixels (
int width,
int height) = 0;
106 virtual ~LogicalResizeListener() =
default;
107 virtual void viewRequestedResizeInLogicalPixels (
int width,
int height) = 0;
114 WindowSizeChangeDetector()
115 : hook (SetWindowsHookEx (WH_CALLWNDPROC,
117 (HINSTANCE)
juce::Process::getCurrentModuleInstanceHandle(),
118 GetCurrentThreadId()))
121 ~WindowSizeChangeDetector() noexcept
123 UnhookWindowsHookEx (hook);
126 static void addListener (HWND hwnd, PhysicalResizeListener& listener)
128 getActiveEditors().emplace (hwnd, &listener);
131 static void removeListener (HWND hwnd)
133 getActiveEditors().erase (hwnd);
143 static void processMessage (
int nCode,
const CWPSTRUCT* info)
145 if (nCode < 0 || info ==
nullptr)
148 constexpr UINT events[] { WM_SIZING, WM_SIZE, WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGED };
153 auto& map = getActiveEditors();
154 auto iter = map.find (info->hwnd);
156 if (iter == map.end())
160 GetWindowRect (info->hwnd, &rect);
161 iter->second->viewRequestedResizeInPhysicalPixels (rect.right - rect.left, rect.bottom - rect.top);
164 static LRESULT CALLBACK callWndProc (
int nCode, WPARAM wParam, LPARAM lParam)
166 processMessage (nCode, lv2_shared::wordCast<CWPSTRUCT*> (lParam));
167 return CallNextHookEx ({}, nCode, wParam, lParam);
176 WindowSizeChangeListener (HWND hwndIn, PhysicalResizeListener& l)
179 detector->addListener (hwnd, l);
182 ~WindowSizeChangeListener()
184 detector->removeListener (hwnd);
188 SharedResourcePointer<WindowSizeChangeDetector> detector;
197 void operator() (LilvNode* ptr)
const noexcept { lilv_node_free (ptr); }
202template <
typename Traits>
206 template <
typename... Ts>
207 TypesafeLilvNode (Ts&&... ts)
210 bool equals (
const TypesafeLilvNode& other)
const noexcept
212 return lilv_node_equals (node.get(), other.node.get());
215 const LilvNode*
get() const noexcept {
return node.get(); }
217 auto getTyped() const noexcept -> decltype (
Traits::access (
nullptr))
219 return Traits::access (node.get());
222 static TypesafeLilvNode claim (LilvNode* node)
224 return TypesafeLilvNode { node };
227 static TypesafeLilvNode
copy (
const LilvNode* node)
229 return TypesafeLilvNode { lilv_node_duplicate (node) };
233 explicit TypesafeLilvNode (LilvNode* ptr)
236 jassert (ptr ==
nullptr || Traits::verify (node.get()));
246 static LilvNode* construct (LilvWorld* world,
const char* uri)
noexcept
248 return lilv_new_uri (world, uri);
251 static LilvNode* construct (LilvWorld* world,
const char* host,
const char* path)
noexcept
253 return lilv_new_file_uri (world, host, path);
256 static constexpr auto verify = lilv_node_is_uri;
257 static constexpr auto access = lilv_node_as_uri;
261 static constexpr auto verify = lilv_node_is_string;
262 static constexpr auto access = lilv_node_as_string; };
269 explicit UsefulUris (LilvWorld* worldIn)
272 LilvWorld*
const world =
nullptr;
274 #define X(str) const NodeUri m##str { world, str };
275 X (LV2_ATOM__AtomPort)
276 X (LV2_ATOM__atomTransfer)
277 X (LV2_ATOM__eventTransfer)
278 X (LV2_CORE__AudioPort)
280 X (LV2_CORE__ControlPort)
281 X (LV2_CORE__GeneratorPlugin)
282 X (LV2_CORE__InputPort)
283 X (LV2_CORE__InstrumentPlugin)
284 X (LV2_CORE__OutputPort)
285 X (LV2_CORE__enumeration)
286 X (LV2_CORE__integer)
287 X (LV2_CORE__toggled)
288 X (LV2_RESIZE_PORT__minimumSize)
289 X (LV2_UI__floatProtocol)
290 X (LV2_WORKER__interface)
294template <
typename Ptr,
typename Free>
298 static const Ptr*
get (
const type& t)
noexcept {
return t.get(); }
301template <
typename Ptr>
304 using type =
const Ptr*;
305 static const Ptr*
get (
const type& t)
noexcept {
return t; }
310 using Container =
const LilvPlugins*;
311 using Iter = LilvIter*;
312 static constexpr auto begin = lilv_plugins_begin;
313 static constexpr auto next = lilv_plugins_next;
314 static constexpr auto isEnd = lilv_plugins_is_end;
315 static constexpr auto get = lilv_plugins_get;
322 using Container =
const LilvPluginClasses*;
323 using Iter = LilvIter*;
324 static constexpr auto begin = lilv_plugin_classes_begin;
325 static constexpr auto next = lilv_plugin_classes_next;
326 static constexpr auto isEnd = lilv_plugin_classes_is_end;
327 static constexpr auto get = lilv_plugin_classes_get;
334 using Container =
const LilvNodes*;
335 using Iter = LilvIter*;
336 static constexpr auto begin = lilv_nodes_begin;
337 static constexpr auto next = lilv_nodes_next;
338 static constexpr auto isEnd = lilv_nodes_is_end;
339 static constexpr auto get = lilv_nodes_get;
342using NodesIterator = lv2_shared::Iterator<NodesIteratorTraits>;
346 using Container =
const LilvScalePoints*;
347 using Iter = LilvIter*;
348 static constexpr auto begin = lilv_scale_points_begin;
349 static constexpr auto next = lilv_scale_points_next;
350 static constexpr auto isEnd = lilv_scale_points_is_end;
351 static constexpr auto get = lilv_scale_points_get;
358 using Container =
const LilvUIs*;
359 using Iter = LilvIter*;
360 static constexpr auto begin = lilv_uis_begin;
361 static constexpr auto next = lilv_uis_next;
362 static constexpr auto isEnd = lilv_uis_is_end;
363 static constexpr auto get = lilv_uis_get;
366using UisIterator = lv2_shared::Iterator<UisIteratorTraits>;
368template <
typename PtrTraits>
372 using type =
typename PtrTraits::type;
374 template <
typename Ptr>
375 explicit NodesImpl (Ptr* ptr)
376 : nodes (type { ptr }) {}
378 explicit NodesImpl (type ptr)
381 unsigned size() const noexcept {
return lilv_nodes_size (PtrTraits::get (nodes)); }
383 NodesIterator
begin() const noexcept
385 return nodes ==
nullptr ? NodesIterator{}
386 : NodesIterator { PtrTraits::get (nodes) };
389 NodesIterator
end() const noexcept {
return {}; }
397 void operator() (LilvNodes* ptr)
const noexcept { lilv_nodes_free (ptr); }
406 explicit ScalePoints (
const LilvScalePoints* pt)
409 ScalePointsIterator
begin() const noexcept
411 return points ==
nullptr ? ScalePointsIterator{}
412 : ScalePointsIterator { points };
415 ScalePointsIterator
end() const noexcept {
return {}; }
418 const LilvScalePoints* points =
nullptr;
424 explicit ScalePoint (
const LilvScalePoint* pt)
427 const LilvNode* getLabel() const noexcept {
return lilv_scale_point_get_label (point); }
428 const LilvNode* getValue() const noexcept {
return lilv_scale_point_get_value (point); }
431 const LilvScalePoint* point =
nullptr;
436 float defaultValue,
min,
max;
458 Port (
const LilvPlugin* pluginIn,
const LilvPort* portIn)
459 : plugin (pluginIn), port (portIn) {}
461 Direction getDirection (
const UsefulUris& uris)
const noexcept
463 if (isA (uris.mLV2_CORE__InputPort))
464 return Direction::input;
466 if (isA (uris.mLV2_CORE__OutputPort))
467 return Direction::output;
469 return Direction::unknown;
472 Kind getKind (
const UsefulUris& uris)
const noexcept
474 if (isA (uris.mLV2_CORE__ControlPort))
475 return Kind::control;
477 if (isA (uris.mLV2_CORE__AudioPort))
480 if (isA (uris.mLV2_CORE__CVPort))
483 if (isA (uris.mLV2_ATOM__AtomPort))
486 return Kind::unknown;
489 OwningNode
get (
const LilvNode* predicate)
const noexcept
491 return OwningNode { lilv_port_get (plugin, port, predicate) };
494 NonOwningNodes getClasses() const noexcept
496 return NonOwningNodes { lilv_port_get_classes (plugin, port) };
499 NodeString getName() const noexcept
501 return NodeString::claim (lilv_port_get_name (plugin, port));
504 NodeString getSymbol() const noexcept
506 return NodeString::copy (lilv_port_get_symbol (plugin, port));
509 OwningNodes getProperties() const noexcept
511 return OwningNodes { lilv_port_get_properties (plugin, port) };
514 ScalePoints getScalePoints() const noexcept
516 return ScalePoints { lilv_port_get_scale_points (plugin, port) };
519 bool hasProperty (
const NodeUri& uri)
const noexcept
521 return lilv_port_has_property (plugin, port, uri.get());
524 uint32_t getIndex() const noexcept {
return lilv_port_get_index (plugin, port); }
526 static float getFloatValue (
const LilvNode* node,
float fallback)
528 if (lilv_node_is_float (node) || lilv_node_is_int (node))
529 return lilv_node_as_float (node);
534 bool supportsEvent (
const LilvNode* node)
const noexcept
536 return lilv_port_supports_event (plugin, port, node);
539 PortRange getRange() const noexcept
541 LilvNode* def =
nullptr;
542 LilvNode*
min =
nullptr;
543 LilvNode*
max =
nullptr;
545 lilv_port_get_range (plugin, port, &def, &min, &max);
547 const OwningNode defOwner { def };
548 const OwningNode minOwner {
min };
549 const OwningNode maxOwner {
max };
551 return { getFloatValue (def, 0.0f),
552 getFloatValue (min, 0.0f),
553 getFloatValue (max, 1.0f) };
556 bool isValid() const noexcept {
return port !=
nullptr; }
559 bool isA (
const NodeUri& uri)
const noexcept
561 return lilv_port_is_a (plugin, port, uri.get());
564 const LilvPlugin* plugin =
nullptr;
565 const LilvPort* port =
nullptr;
573 explicit Plugin (
const LilvPlugin* p) : plugin (p) {}
575 bool verify() const noexcept {
return lilv_plugin_verify (plugin); }
576 NodeUri getUri() const noexcept {
return NodeUri::copy (lilv_plugin_get_uri (plugin)); }
577 NodeUri getBundleUri() const noexcept {
return NodeUri::copy (lilv_plugin_get_bundle_uri (plugin)); }
578 NodeUri getLibraryUri() const noexcept {
return NodeUri::copy (lilv_plugin_get_library_uri (plugin)); }
579 NodeString getName() const noexcept {
return NodeString::claim (lilv_plugin_get_name (plugin)); }
580 NodeString getAuthorName() const noexcept {
return NodeString::claim (lilv_plugin_get_author_name (plugin)); }
581 uint32_t getNumPorts() const noexcept {
return lilv_plugin_get_num_ports (plugin); }
582 const LilvPluginClass* getClass() const noexcept {
return lilv_plugin_get_class (plugin); }
583 OwningNodes getValue (
const LilvNode* predicate)
const noexcept {
return OwningNodes { lilv_plugin_get_value (plugin, predicate) }; }
585 Port getPortByIndex (uint32_t index)
const noexcept
587 return Port { plugin, lilv_plugin_get_port_by_index (plugin, index) };
590 Port getPortByDesignation (
const LilvNode* portClass,
const LilvNode* designation)
const noexcept
592 return Port { plugin, lilv_plugin_get_port_by_designation (plugin, portClass, designation) };
595 OwningNodes getRequiredFeatures() const noexcept
597 return OwningNodes { lilv_plugin_get_required_features (plugin) };
600 OwningNodes getOptionalFeatures() const noexcept
602 return OwningNodes { lilv_plugin_get_optional_features (plugin) };
605 bool hasExtensionData (
const NodeUri& uri)
const noexcept
607 return lilv_plugin_has_extension_data (plugin, uri.get());
610 bool hasFeature (
const NodeUri& uri)
const noexcept
612 return lilv_plugin_has_feature (plugin, uri.get());
615 template <
typename... Classes>
616 uint32_t getNumPortsOfClass (
const Classes&... classes)
const noexcept
618 return lilv_plugin_get_num_ports_of_class (plugin, classes.get()..., 0);
621 const LilvPlugin*
get() const noexcept {
return plugin; }
623 bool hasLatency() const noexcept {
return lilv_plugin_has_latency (plugin); }
624 uint32_t getLatencyPortIndex() const noexcept {
return lilv_plugin_get_latency_port_index (plugin); }
627 const LilvPlugin* plugin =
nullptr;
638 SymbolMap() =
default;
642 for (
const auto* str : uris)
646 LV2_URID map (
const char* uri)
648 const auto comparator = [
this] (
size_t index,
const String& str)
650 return strings[index] < str;
653 const auto uriString = String::fromUTF8 (uri);
654 const auto it =
std::lower_bound (indices.cbegin(), indices.cend(), uriString, comparator);
656 if (it != indices.cend() && strings[*it] == uriString)
657 return static_cast<LV2_URID
> (*it + 1);
659 const auto index = strings.size();
660 indices.insert (it, index);
661 strings.push_back (uriString);
662 return static_cast<LV2_URID
> (index + 1);
665 const char* unmap (LV2_URID urid)
const
667 const auto index = urid - 1;
668 return index < strings.size() ? strings[index].toRawUTF8()
672 static LV2_URID mapUri (LV2_URID_Map_Handle handle,
const char* uri)
674 return static_cast<SymbolMap*
> (handle)->map (uri);
677 static const char* unmapUri (LV2_URID_Unmap_Handle handle, LV2_URID urid)
679 return static_cast<SymbolMap*
> (handle)->unmap (urid);
682 LV2_URID_Map getMapFeature() {
return {
this, mapUri }; }
683 LV2_URID_Unmap getUnmapFeature() {
return {
this, unmapUri }; }
694 explicit UsefulUrids (SymbolMap& m) : symap (m) {}
698 #define X(token) const LV2_URID m##token = symap.map (token);
705 X (LV2_ATOM__Sequence)
706 X (LV2_ATOM__atomTransfer)
707 X (LV2_ATOM__beatTime)
708 X (LV2_ATOM__eventTransfer)
709 X (LV2_ATOM__frameTime)
714 X (LV2_MIDI__MidiEvent)
716 X (LV2_PATCH__property)
718 X (LV2_STATE__StateChanged)
719 X (LV2_TIME__Position)
720 X (LV2_TIME__barBeat)
722 X (LV2_TIME__beatUnit)
723 X (LV2_TIME__beatsPerBar)
724 X (LV2_TIME__beatsPerMinute)
728 X (LV2_UI__floatProtocol)
737 explicit Log (
const UsefulUrids* u) : urids (u) {}
739 LV2_Log_Log* getLogFeature() {
return &logFeature; }
742 int vprintfCallback ([[maybe_unused]] LV2_URID type,
const char* fmt,
va_list ap)
const
746 jassert (type != urids->mLV2_LOG__Error && type != urids->mLV2_LOG__Warning);
750 static int vprintfCallback (LV2_Log_Handle handle,
755 return static_cast<const Log*
> (handle)->vprintfCallback (type, fmt, ap);
758 static int printfCallback (LV2_Log_Handle handle, LV2_URID type,
const char* fmt, ...)
762 auto result = vprintfCallback (handle, type, fmt, list);
767 const UsefulUrids* urids =
nullptr;
768 LV2_Log_Log logFeature {
this, printfCallback, vprintfCallback };
783 for (
const auto& feature : features)
784 result.push_back (String::fromUTF8 (feature.URI));
798 for (
const auto& feature : features)
799 result.push_back (&feature);
809template <
typename Extension>
812 OptionalExtension() =
default;
814 explicit OptionalExtension (Extension extensionIn) : extension (extensionIn), valid (true) {}
824 void operator() (LilvInstance* ptr)
const noexcept { lilv_instance_free (ptr); }
829 using GetExtensionData =
const void* (*) (
const char*);
831 Instance (
const Plugin& pluginIn,
double sampleRate,
const LV2_Feature*
const* features)
833 instance (lilv_plugin_instantiate (plugin.
get(), sampleRate, features)) {}
835 void activate() { lilv_instance_activate (instance.get()); }
836 void run (uint32_t sampleCount) { lilv_instance_run (instance.get(), sampleCount); }
837 void deactivate() { lilv_instance_deactivate (instance.get()); }
839 const char* getUri() const noexcept {
return lilv_instance_get_uri (instance.get()); }
841 LV2_Handle getHandle() const noexcept {
return lilv_instance_get_handle (instance.get()); }
843 LilvInstance*
get() const noexcept {
return instance.get(); }
845 void connectPort (uint32_t index,
void* data)
847 lilv_instance_connect_port (instance.get(), index, data);
850 template <
typename Extension>
851 OptionalExtension<Extension> getExtensionData (
const NodeUri& uri)
const noexcept
853 if (plugin.get() ==
nullptr || ! plugin.hasExtensionData (uri) || instance.get() ==
nullptr)
856 return OptionalExtension<Extension> { readUnaligned<Extension> (lilv_instance_get_extension_data (instance.get(), uri.getTyped())) };
859 GetExtensionData getExtensionDataCallback() const noexcept
861 return instance->lv2_descriptor->extension_data;
864 bool operator== (
std::nullptr_t)
const noexcept {
return instance ==
nullptr; }
865 bool operator!= (
std::nullptr_t)
const noexcept {
return ! (*
this ==
nullptr); }
874enum class Realtime { no, yes };
879 static WorkResponder getDefault() {
return {
nullptr,
nullptr }; }
881 LV2_Worker_Status processResponse (uint32_t size,
const void* data)
const
883 return worker->work_response (handle, size, data);
886 bool isValid()
const {
return handle !=
nullptr && worker !=
nullptr; }
889 const LV2_Worker_Interface* worker;
894 virtual ~WorkerResponseListener() =
default;
895 virtual LV2_Worker_Status responseGenerated (WorkResponder, uint32_t,
const void*) = 0;
900 LV2_Worker_Status respond (uint32_t size,
const void* data)
const
902 if (realtime == Realtime::yes)
903 return listener.responseGenerated (responder, size, data);
905 return responder.processResponse (size, data);
908 static LV2_Worker_Status respond (LV2_Worker_Respond_Handle handle,
912 return static_cast<const RespondHandle*
> (handle)->respond (size, data);
915 WorkResponder responder;
916 WorkerResponseListener& listener;
923 static WorkSubmitter getDefault() {
return {
nullptr,
nullptr,
nullptr,
nullptr }; }
925 LV2_Worker_Status doWork (Realtime realtime, uint32_t size,
const void* data)
const
936 RespondHandle respondHandle { WorkResponder { handle, worker }, *listener, realtime };
937 return worker->work (handle, RespondHandle::respond, &respondHandle, size, data);
940 bool isValid()
const {
return handle !=
nullptr && worker !=
nullptr && listener !=
nullptr && workMutex !=
nullptr; }
943 const LV2_Worker_Interface* worker;
944 WorkerResponseListener* listener;
945 CriticalSection* workMutex;
948template <
typename Trivial>
951 static_assert (std::is_trivial_v<Trivial>);
957template <
typename Context>
961 static_assert (std::is_trivial_v<Context>,
"Context must be copyable as bytes");
963 explicit WorkQueue (
int size)
966 LV2_Worker_Status push (Context context,
size_t size,
const void* contents)
968 const auto* bytes =
static_cast<const char*
> (contents);
969 const auto numToWrite =
sizeof (Header) + size;
971 if (
static_cast<size_t> (fifo.getFreeSpace()) < numToWrite)
972 return LV2_WORKER_ERR_NO_SPACE;
974 Header header {
size, context };
975 const auto headerBuffer = toChars (header);
977 const auto scope = fifo.write (
static_cast<int> (numToWrite));
978 jassert (scope.blockSize1 + scope.blockSize2 ==
static_cast<int> (numToWrite));
981 scope.forEach ([&] (
int i)
983 data[
static_cast<size_t> (i)] = index < headerBuffer.size() ? headerBuffer[index]
984 : bytes[index - headerBuffer.size()];
988 return LV2_WORKER_SUCCESS;
997 const auto numReady = fifo.getNumReady();
999 if (
static_cast<size_t> (numReady) <
sizeof (Header))
1002 return Context::getDefault();
1009 fifo.read (
sizeof (Header)).forEach ([&] (
int i)
1011 headerBuffer[index++] =
data[
static_cast<size_t> (i)];
1015 const auto header = readUnaligned<Header> (headerBuffer.data());
1017 jassert (
static_cast<size_t> (fifo.getNumReady()) >= header.size);
1019 dest.
resize (header.size);
1023 fifo.read (
static_cast<int> (header.size)).forEach ([&] (
int i)
1025 dest[index++] =
data[
static_cast<size_t> (i)];
1029 return header.context;
1052 void insert (LV2_Handle handle)
1054 const SpinLock::ScopedLockType
lock (mutex);
1055 handles.insert (handle);
1058 void erase (LV2_Handle handle)
1060 const SpinLock::ScopedLockType
lock (mutex);
1061 handles.erase (handle);
1064 template <
typename Fn>
1065 LV2_Worker_Status ifContains (LV2_Handle handle,
Fn&& callback)
1067 const SpinLock::ScopedLockType
lock (mutex);
1069 if (handles.find (handle) != handles.cend())
1072 return LV2_WORKER_ERR_UNKNOWN;
1093 ~SharedThreadedWorker() noexcept
override
1099 LV2_Worker_Status schedule (WorkSubmitter submitter,
1103 return registry.ifContains (submitter.handle, [&]
1105 return incoming.push (submitter, size, data);
1109 LV2_Worker_Status responseGenerated (WorkResponder responder,
1111 const void* data)
override
1113 return registry.ifContains (responder.handle, [&]
1115 return outgoing.push (responder, size, data);
1119 void processResponses()
1123 auto workerResponder = outgoing.pop (message);
1125 if (! message.empty() && workerResponder.isValid())
1126 workerResponder.processResponse (
static_cast<uint32_t> (message.size()), message.data());
1132 void registerHandle (LV2_Handle handle) { registry.
insert (handle); }
1133 void deregisterHandle (LV2_Handle handle) { registry.erase (handle); }
1136 static constexpr auto queueSize = 8192;
1138 WorkQueue<WorkSubmitter> incoming { queueSize };
1139 WorkQueue<WorkResponder> outgoing { queueSize };
1145 while (! shouldExit)
1147 const auto submitter = incoming.pop (buffer);
1149 if (! buffer.empty() && submitter.isValid())
1150 submitter.doWork (Realtime::yes, (uint32_t) buffer.size(), buffer.data());
1155 HandleRegistry registry;
1162 virtual ~HandleHolder() =
default;
1163 virtual LV2_Handle getHandle()
const = 0;
1164 virtual const LV2_Worker_Interface* getWorkerInterface()
const = 0;
1170 explicit WorkScheduler (HandleHolder& handleHolderIn)
1171 : handleHolder (handleHolderIn) {}
1173 void processResponses() { workerThread->processResponses(); }
1175 LV2_Worker_Schedule& getWorkerSchedule() {
return schedule; }
1177 void setNonRealtime (
bool nonRealtime) { realtime = ! nonRealtime; }
1179 void registerHandle (LV2_Handle handle) { workerThread->registerHandle (handle); }
1180 void deregisterHandle (LV2_Handle handle) { workerThread->deregisterHandle (handle); }
1183 LV2_Worker_Status scheduleWork (uint32_t size,
const void* data)
1185 WorkSubmitter submitter { handleHolder.getHandle(),
1186 handleHolder.getWorkerInterface(),
1194 return realtime ? workerThread->schedule (submitter, size, data)
1195 : submitter.doWork (Realtime::no, size, data);
1198 static LV2_Worker_Status scheduleWork (LV2_Worker_Schedule_Handle handle,
1202 return static_cast<WorkScheduler*
> (handle)->scheduleWork (size, data);
1205 SharedResourcePointer<SharedThreadedWorker> workerThread;
1206 HandleHolder& handleHolder;
1207 LV2_Worker_Schedule schedule {
this, scheduleWork };
1208 CriticalSection workMutex;
1209 bool realtime =
true;
1216 virtual ~FeaturesDataListener() =
default;
1217 virtual LV2_Resize_Port_Status resizeCallback (uint32_t index,
size_t size) = 0;
1223 explicit Resize (FeaturesDataListener& l)
1226 LV2_Resize_Port_Resize& getFeature() {
return resize; }
1229 LV2_Resize_Port_Status resizeCallback (uint32_t index,
size_t size)
1231 return listener.resizeCallback (index, size);
1234 static LV2_Resize_Port_Status resizeCallback (LV2_Resize_Port_Feature_Data data, uint32_t index,
size_t size)
1236 return static_cast<Resize*
> (
data)->resizeCallback (index, size);
1239 FeaturesDataListener& listener;
1240 LV2_Resize_Port_Resize resize {
this, resizeCallback };
1246 FeaturesData (HandleHolder& handleHolder,
1247 FeaturesDataListener& l,
1248 int32_t maxBlockSizeIn,
1249 int32_t sequenceSizeIn,
1250 const UsefulUrids* u)
1253 maxBlockSize (maxBlockSizeIn),
1254 sequenceSize (sequenceSizeIn),
1255 workScheduler (handleHolder)
1258 LV2_Options_Option* getOptions() noexcept {
return options.data(); }
1260 int32_t getMaxBlockSize() const noexcept {
return maxBlockSize; }
1262 void setNonRealtime (
bool newValue) { realtime = ! newValue; }
1264 const LV2_Feature*
const* getFeatureArray() const noexcept {
return features.pointers.
data(); }
1268 return Features::getUris (makeFeatures ({}, {}, {}, {}, {}, {}));
1271 void processResponses() { workScheduler.processResponses(); }
1273 void registerHandle (LV2_Handle handle) { workScheduler.registerHandle (handle); }
1274 void deregisterHandle (LV2_Handle handle) { workScheduler.deregisterHandle (handle); }
1278 LV2_URID_Unmap* unmap,
1279 LV2_Options_Option* options,
1280 LV2_Worker_Schedule* schedule,
1281 LV2_Resize_Port_Resize* resize,
1282 [[maybe_unused]] LV2_Log_Log* log)
1284 return { LV2_Feature { LV2_STATE__loadDefaultState,
nullptr },
1285 LV2_Feature { LV2_BUF_SIZE__boundedBlockLength,
nullptr },
1286 LV2_Feature { LV2_URID__map, map },
1287 LV2_Feature { LV2_URID__unmap, unmap },
1288 LV2_Feature { LV2_OPTIONS__options, options },
1289 LV2_Feature { LV2_WORKER__schedule, schedule },
1290 LV2_Feature { LV2_STATE__threadSafeRestore,
nullptr },
1292 LV2_Feature { LV2_LOG__log,
log },
1294 LV2_Feature { LV2_RESIZE_PORT__resize, resize } };
1297 LV2_Options_Option makeOption (
const char* uid,
const int32_t* ptr)
1299 return { LV2_OPTIONS_INSTANCE,
1301 urids->symap.map (uid),
1303 urids->symap.map (LV2_ATOM__Int),
1307 const UsefulUrids* urids;
1311 const int32_t minBlockSize = 0, maxBlockSize = 0, sequenceSize = 0;
1315 makeOption (LV2_BUF_SIZE__minBlockLength, &minBlockSize),
1316 makeOption (LV2_BUF_SIZE__maxBlockLength, &maxBlockSize),
1317 makeOption (LV2_BUF_SIZE__sequenceSize, &sequenceSize),
1318 { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0,
nullptr },
1321 WorkScheduler workScheduler;
1323 LV2_URID_Map map = urids->symap.getMapFeature();
1324 LV2_URID_Unmap unmap = urids->symap.getUnmapFeature();
1325 Features features { makeFeatures (&map,
1328 &workScheduler.getWorkerSchedule(),
1329 &resize.getFeature(),
1330 log.getLogFeature()) };
1332 bool realtime =
true;
1340 template <
typename Fn>
1341 void operator() (SpinLock& mutex,
Fn&& fn)
1343 const SpinLock::ScopedTryLockType
lock (mutex);
1345 if (
lock.isLocked())
1352 template <
typename Fn>
1353 void operator() (SpinLock& mutex,
Fn&& fn)
1355 const SpinLock::ScopedLockType
lock (mutex);
1362 using Read = TryLockAndCall;
1363 using Write = LockAndCall;
1368 using Read = LockAndCall;
1369 using Write = TryLockAndCall;
1378template <
typename Header>
1381 virtual ~MessageBufferInterface() =
default;
1382 virtual void pushMessage (Header header, uint32_t size,
const void* buffer) = 0;
1385template <
typename Header,
typename LockTraits>
1388 using Read =
typename LockTraits::Read;
1389 using Write =
typename LockTraits::Write;
1398 Messages() {
data.reserve (initialBufferSize); }
1400 void pushMessage (Header header, uint32_t size,
const void* buffer)
override
1404 const auto chars = toChars (FullHeader { header,
size });
1405 const auto bufferAsChars =
static_cast<const char*
> (buffer);
1406 data.insert (
data.end(), chars.begin(), chars.end());
1407 data.insert (
data.end(), bufferAsChars, bufferAsChars +
size);
1411 template <
typename Callback>
1412 void readAllAndClear (Callback&& callback)
1421 for (
auto ptr =
data.data(); ptr <
end;)
1423 const auto header = readUnaligned<FullHeader> (ptr);
1424 callback (header.header, header.size, ptr + sizeof (header));
1425 ptr +=
sizeof (header) + header.size;
1433 static constexpr auto initialBufferSize = 8192;
1443 virtual int idle() = 0;
1448 UiEventListener* listener;
1449 MessageHeader header;
1455 ProcessorToUi() { timer.startTimerHz (60); }
1460 void pushMessage (UiMessageHeader header, uint32_t size,
const void* buffer)
override
1462 processorToUi.pushMessage (header, size, buffer);
1466 Messages<UiMessageHeader, RealtimeWriteTrait> processorToUi;
1468 TimedCallback timer { [
this]
1470 for (
auto* l : activeUis)
1474 processorToUi.readAllAndClear ([&] (
const UiMessageHeader& header, uint32_t size,
const char* data)
1476 if (activeUis.
find (header.listener) != activeUis.
cend())
1477 header.listener->pushMessage (header.header, size, data);
1485 explicit StatefulPortUrids (SymbolMap& map)
1486 : Float (map.map (LV2_ATOM__Float)),
1487 Double (map.map (LV2_ATOM__Double)),
1488 Int (map.map (LV2_ATOM__Int)),
1489 Long (map.map (LV2_ATOM__Long))
1492 const LV2_URID Float, Double, Int, Long;
1499template <
typename Value>
1506 template <
typename Other>
1507 const_iterator
find (
const Other& other)
const noexcept
1509 const auto it =
std::lower_bound (storage.cbegin(), storage.cend(), other);
1511 if (it != storage.cend() && ! (other < *it))
1514 return storage.cend();
1517 void insert (Value&& value) { insertImpl (std::move (value)); }
1518 void insert (
const Value& value) { insertImpl (value); }
1520 size_t size() const noexcept {
return storage.size(); }
1521 bool empty() const noexcept {
return storage.empty(); }
1523 iterator
begin() noexcept {
return storage.
begin(); }
1524 const_iterator
begin() const noexcept {
return storage.
begin(); }
1525 const_iterator
cbegin() const noexcept {
return storage.cbegin(); }
1527 iterator
end() noexcept {
return storage.
end(); }
1528 const_iterator
end() const noexcept {
return storage.
end(); }
1529 const_iterator
cend() const noexcept {
return storage.cend(); }
1531 auto& operator[] (
size_t index)
const {
return storage[index]; }
1534 template <
typename Arg>
1535 void insertImpl (Arg&& value)
1537 const auto it =
std::lower_bound (storage.cbegin(), storage.cend(), value);
1539 if (it == storage.cend() || value < *it)
1540 storage.insert (it, std::forward<Arg> (value));
1551 bool operator< (
const StoredScalePoint& other)
const noexcept {
return value < other.value; }
1554inline bool operator< (
const StoredScalePoint& a,
float b)
noexcept {
return a.value < b; }
1555inline bool operator< (
float a,
const StoredScalePoint& b)
noexcept {
return a < b.value; }
1559 ParameterInfo() =
default;
1561 ParameterInfo (SafeSortedSet<StoredScalePoint> scalePointsIn,
1562 String identifierIn,
1563 float defaultValueIn,
1569 : scalePoints (
std::
move (scalePointsIn)),
1570 identifier (
std::
move (identifierIn)),
1571 defaultValue (defaultValueIn),
1574 isToggle (isToggleIn),
1575 isInteger (isIntegerIn),
1579 static SafeSortedSet<StoredScalePoint> getScalePoints (
const Port& port)
1581 SafeSortedSet<StoredScalePoint> scalePoints;
1583 for (
const LilvScalePoint* p : port.getScalePoints())
1585 const ScalePoint wrapper { p };
1586 const auto value = wrapper.getValue();
1587 const auto label = wrapper.getLabel();
1589 if (lilv_node_is_float (value) || lilv_node_is_int (value))
1590 scalePoints.insert ({ lilv_node_as_string (label), lilv_node_as_float (value) });
1596 static ParameterInfo getInfoForPort (
const UsefulUris& uris,
const Port& port)
1598 const auto range = port.getRange();
1600 return { getScalePoints (port),
1601 "sym:" + String::fromUTF8 (port.getSymbol().getTyped()),
1605 port.hasProperty (uris.mLV2_CORE__toggled),
1606 port.hasProperty (uris.mLV2_CORE__integer),
1607 port.hasProperty (uris.mLV2_CORE__enumeration) };
1610 SafeSortedSet<StoredScalePoint> scalePoints;
1615 float defaultValue = 0.0f,
min = 0.0f,
max = 1.0f;
1616 bool isToggle =
false, isInteger =
false, isEnum =
false;
1626 Port::Direction direction;
1631 ControlPort (
const PortHeader& headerIn,
const ParameterInfo& infoIn)
1632 : header (headerIn), info (infoIn) {}
1636 float currentValue = info.defaultValue;
1649template <
size_t Alignment>
1653 SingleSizeAlignedStorage() =
default;
1655 explicit SingleSizeAlignedStorage (
size_t sizeInBytes)
1656 : storage (new
char[sizeInBytes + Alignment]),
1657 alignedPointer (storage.
get()),
1658 space (sizeInBytes + Alignment)
1660 alignedPointer =
std::align (Alignment, sizeInBytes, alignedPointer, space);
1663 void*
data()
const {
return alignedPointer; }
1668 void* alignedPointer =
nullptr;
1672template <
size_t Alignment>
1675 if (size <= storage.size())
1688 AtomPort (PortHeader h,
size_t bytes, SymbolMap& map, SupportsTime supportsTime)
1689 : header (h), contents (bytes), forge (map.getMapFeature()),
time (supportsTime) {}
1693 void replaceWithChunk()
1696 forge.writeChunk ((uint32_t) (
size() -
sizeof (LV2_Atom)));
1699 void replaceBufferWithAtom (
const LV2_Atom* atom)
1701 const auto totalSize = atom->size +
sizeof (LV2_Atom);
1703 if (totalSize <=
size())
1709 void beginSequence()
1712 lv2_atom_forge_sequence_head (forge.get(), &frame, 0);
1717 lv2_atom_forge_pop (forge.get(), &frame);
1724 void addAtomToSequence (int64_t timestamp,
const LV2_Atom* atom)
1729 addEventToSequence (timestamp,
1732 reinterpret_cast<const char*
> (atom) + sizeof (LV2_Atom));
1735 void addEventToSequence (int64_t timestamp, uint32_t type, uint32_t size,
const void* content)
1737 lv2_atom_forge_frame_time (forge.get(), timestamp);
1738 lv2_atom_forge_atom (forge.get(), size, type);
1739 lv2_atom_forge_write (forge.get(), content, size);
1742 void ensureSizeInBytes (
size_t size)
1744 contents = grow (std::move (contents), size);
1747 char*
data() noexcept {
return data (*
this); }
1748 const char*
data() const noexcept {
return data (*
this); }
1750 size_t size() const noexcept {
return contents.size(); }
1752 lv2_shared::AtomForge& getForge() {
return forge; }
1753 const lv2_shared::AtomForge& getForge()
const {
return forge; }
1755 bool getSupportsTime()
const {
return time == SupportsTime::yes; }
1758 template <
typename This>
1759 static auto data (This& t) ->
decltype (t.data())
1765 SingleSizeAlignedStorage<8> contents;
1766 lv2_shared::AtomForge forge;
1767 LV2_Atom_Forge_Frame frame;
1768 SupportsTime
time = SupportsTime::no;
1771struct FreeString {
void operator() (
void* ptr)
const noexcept { lilv_free (ptr); } };
1781 explicit Plugins (
const LilvPlugins* list) noexcept : plugins (list) {}
1783 unsigned size() const noexcept {
return lilv_plugins_size (plugins); }
1785 PluginsIterator
begin() const noexcept {
return PluginsIterator { plugins }; }
1786 PluginsIterator
end() const noexcept {
return PluginsIterator{}; }
1788 const LilvPlugin* getByUri (
const NodeUri& uri)
const
1790 return lilv_plugins_get_by_uri (plugins, uri.get());
1793 const LilvPlugin* getByFile (
const File& file)
const
1795 for (
const auto* plugin : *this)
1797 if (bundlePathFromUri (lilv_node_as_uri (lilv_plugin_get_bundle_uri (plugin))) == file)
1805 const LilvPlugins* plugins =
nullptr;
1808template <
typename PtrTraits>
1812 using type =
typename PtrTraits::type;
1814 explicit PluginClassesImpl (type ptr)
1815 : classes (
std::
move (ptr)) {}
1817 unsigned size() const noexcept {
return lilv_plugin_classes_size (PtrTraits::get (classes)); }
1819 PluginClassesIterator
begin() const noexcept {
return PluginClassesIterator { PtrTraits::get (classes) }; }
1820 PluginClassesIterator
end() const noexcept {
return PluginClassesIterator{}; }
1822 const LilvPluginClass* getByUri (
const NodeUri& uri)
const noexcept
1824 return lilv_plugin_classes_get_by_uri (PtrTraits::get (classes), uri.get());
1833 void operator() (LilvPluginClasses* ptr)
const noexcept { lilv_plugin_classes_free (ptr); }
1842 World() : world (lilv_world_new()) {}
1844 void loadAllFromPaths (
const NodeString& paths)
1846 lilv_world_set_option (world.get(), LILV_OPTION_LV2_PATH, paths.get());
1847 lilv_world_load_all (world.get());
1850 void loadBundle (
const NodeUri& uri) { lilv_world_load_bundle (world.get(), uri.get()); }
1851 void unloadBundle (
const NodeUri& uri) { lilv_world_unload_bundle (world.get(), uri.get()); }
1853 void loadResource (
const NodeUri& uri) { lilv_world_load_resource (world.get(), uri.get()); }
1854 void unloadResource (
const NodeUri& uri) { lilv_world_unload_resource (world.get(), uri.get()); }
1856 void loadSpecifications() { lilv_world_load_specifications (world.get()); }
1857 void loadPluginClasses() { lilv_world_load_plugin_classes (world.get()); }
1859 Plugins getAllPlugins()
const {
return Plugins { lilv_world_get_all_plugins (world.get()) }; }
1860 NonOwningPluginClasses getPluginClasses()
const {
return NonOwningPluginClasses { lilv_world_get_plugin_classes (world.get()) }; }
1862 NodeUri newUri (
const char* uri) {
return NodeUri { world.get(), uri }; }
1863 NodeUri newFileUri (
const char* host,
const char* path) {
return NodeUri { world.get(), host, path }; }
1864 NodeString newString (
const char* str) {
return NodeString { world.get(), str }; }
1866 bool ask (
const LilvNode* subject,
const LilvNode* predicate,
const LilvNode*
object)
const
1868 return lilv_world_ask (world.get(), subject, predicate,
object);
1871 OwningNode
get (
const LilvNode* subject,
const LilvNode* predicate,
const LilvNode*
object)
const
1873 return OwningNode { lilv_world_get (world.get(), subject, predicate,
object) };
1876 OwningNodes findNodes (
const LilvNode* subject,
const LilvNode* predicate,
const LilvNode*
object)
const
1878 return OwningNodes { lilv_world_find_nodes (world.get(), subject, predicate,
object) };
1881 LilvWorld*
get()
const {
return world.get(); }
1886 void operator() (LilvWorld* ptr)
const noexcept { lilv_world_free (ptr); }
1895 static constexpr auto sequenceSize = 8192;
1897 template <
typename Callback>
1898 void forEachPort (Callback&& callback)
const
1900 for (
const auto& port : controlPorts)
1901 callback (port.header);
1903 for (
const auto& port : cvPorts)
1904 callback (port.header);
1906 for (
const auto& port : audioPorts)
1907 callback (port.header);
1909 for (
const auto& port : atomPorts)
1910 callback (port.header);
1913 auto getControlPorts() {
return makeSimpleSpan (controlPorts); }
1914 auto getControlPorts()
const {
return makeSimpleSpan (controlPorts); }
1915 auto getCvPorts() {
return makeSimpleSpan (cvPorts); }
1916 auto getCvPorts()
const {
return makeSimpleSpan (cvPorts); }
1917 auto getAudioPorts() {
return makeSimpleSpan (audioPorts); }
1918 auto getAudioPorts()
const {
return makeSimpleSpan (audioPorts); }
1919 auto getAtomPorts() {
return makeSimpleSpan (atomPorts); }
1920 auto getAtomPorts()
const {
return makeSimpleSpan (atomPorts); }
1922 static Optional<Ports> getPorts (World& world,
const UsefulUris& uris,
const Plugin& plugin, SymbolMap& symap)
1925 bool successful =
true;
1927 const auto numPorts = plugin.getNumPorts();
1928 const auto timeNode = world.newUri (LV2_TIME__Position);
1930 for (uint32_t i = 0; i != numPorts; ++i)
1932 const auto port = plugin.getPortByIndex (i);
1934 const PortHeader header { String::fromUTF8 (port.getName().getTyped()),
1935 String::fromUTF8 (port.getSymbol().getTyped()),
1937 port.getDirection (uris) };
1939 switch (port.getKind (uris))
1941 case Port::Kind::control:
1943 value.controlPorts.push_back ({ header, ParameterInfo::getInfoForPort (uris, port) });
1947 case Port::Kind::cv:
1948 value.cvPorts.push_back ({ header });
1951 case Port::Kind::audio:
1953 value.audioPorts.push_back ({ header });
1957 case Port::Kind::atom:
1959 const auto supportsTime = port.supportsEvent (timeNode.get());
1960 value.atomPorts.push_back ({ header,
1961 (
size_t) Ports::sequenceSize,
1963 supportsTime ? SupportsTime::yes : SupportsTime::no });
1967 case Port::Kind::unknown:
1973 for (
auto& atomPort : value.atomPorts)
1975 const auto port = plugin.getPortByIndex (atomPort.header.index);
1976 const auto minSize = port.get (uris.mLV2_RESIZE_PORT__minimumSize.get());
1978 if (minSize !=
nullptr)
1979 atomPort.ensureSizeInBytes ((
size_t) lilv_node_as_int (minSize.get()));
1982 return successful ? makeOptional (std::move (value)) : nullopt;
1992class InstanceWithSupports final :
private FeaturesDataListener,
1993 private HandleHolder
1996 InstanceWithSupports (World& world,
1998 const Plugin& plugin,
2000 int32_t initialBufferSize,
2002 : symap (
std::
move (symapIn)),
2004 features (*this, *this, initialBufferSize, lv2_host::Ports::sequenceSize, &urids),
2005 instance (plugin, sampleRate, features.getFeatureArray()),
2006 workerInterface (instance.getExtensionData<LV2_Worker_Interface> (world.newUri (LV2_WORKER__interface)))
2008 if (instance ==
nullptr)
2011 for (
auto& port : ports.getControlPorts())
2012 instance.connectPort (port.header.index, &port.currentValue);
2014 for (
auto& port : ports.getAtomPorts())
2015 instance.connectPort (port.header.index, port.
data());
2017 for (
auto& port : ports.getCvPorts())
2018 instance.connectPort (port.header.index, nullptr);
2020 for (
auto& port : ports.getAudioPorts())
2021 instance.connectPort (port.header.index, nullptr);
2023 features.registerHandle (instance.getHandle());
2026 ~InstanceWithSupports()
override
2028 if (instance !=
nullptr)
2029 features.deregisterHandle (instance.getHandle());
2033 const UsefulUrids urids { *symap };
2035 FeaturesData features;
2037 Messages<MessageHeader, RealtimeReadTrait> uiToProcessor;
2038 SharedResourcePointer<ProcessorToUi> processorToUi;
2041 LV2_Handle handle = instance ==
nullptr ? nullptr : instance.getHandle();
2042 OptionalExtension<LV2_Worker_Interface> workerInterface;
2044 LV2_Handle getHandle()
const override {
return handle; }
2045 const LV2_Worker_Interface* getWorkerInterface()
const override {
return workerInterface.valid ? &workerInterface.extension :
nullptr; }
2047 LV2_Resize_Port_Status resizeCallback (uint32_t index,
size_t size)
override
2049 if (ports.getAtomPorts().size() <= index)
2050 return LV2_RESIZE_PORT_ERR_UNKNOWN;
2052 auto& port = ports.getAtomPorts()[index];
2054 if (port.header.direction != Port::Direction::output)
2055 return LV2_RESIZE_PORT_ERR_UNKNOWN;
2057 port.ensureSizeInBytes (size);
2058 instance.connectPort (port.header.index, port.data());
2060 return LV2_RESIZE_PORT_SUCCESS;
2078 explicit PortMap (Ports& ports)
2080 for (
auto& port : ports.getControlPorts())
2081 symbolToControlPortMap.emplace (port.header.symbol, &port);
2084 PortState getState (
const String& symbol,
const StatefulPortUrids& urids)
2086 if (
auto* port = getControlPortForSymbol (symbol))
2087 return { &port->currentValue,
sizeof (
float), urids.Float };
2092 return {
nullptr, 0, 0 };
2095 void restoreState (
const String& symbol,
const StatefulPortUrids& urids, PortState ps)
2097 if (
auto* port = getControlPortForSymbol (symbol))
2099 port->currentValue = [&]() ->
float
2101 if (ps.kind == urids.Float)
2102 return getValueFrom<float> (ps.data, ps.size);
2104 if (ps.kind == urids.Double)
2105 return getValueFrom<double> (ps.data, ps.size);
2107 if (ps.kind == urids.Int)
2108 return getValueFrom<int32_t> (ps.data, ps.size);
2110 if (ps.kind == urids.Long)
2111 return getValueFrom<int64_t> (ps.data, ps.size);
2122 template <
typename Value>
2123 static float getValueFrom (
const void* data, [[maybe_unused]] uint32_t size)
2125 jassert (size ==
sizeof (Value));
2126 return (
float) readUnaligned<Value> (data);
2129 ControlPort* getControlPortForSymbol (
const String& symbol)
const
2131 const auto iter = symbolToControlPortMap.find (symbol);
2132 return iter != symbolToControlPortMap.cend() ? iter->second :
nullptr;
2142 PluginState() =
default;
2144 explicit PluginState (LilvState* ptr)
2147 const LilvState*
get() const noexcept {
return state.get(); }
2149 void restore (InstanceWithSupports& instance, PortMap& portMap)
const
2151 if (state !=
nullptr)
2152 SaveRestoreHandle { instance, portMap }.restore (state.get());
2155 std::string toString (LilvWorld* world, LV2_URID_Map* map, LV2_URID_Unmap* unmap,
const char* uri)
const
2166 String getLabel()
const
2168 return String::fromUTF8 (lilv_state_get_label (state.get()));
2171 void setLabel (
const String& label)
2173 lilv_state_set_label (state.get(), label.toRawUTF8());
2176 class SaveRestoreHandle
2179 explicit SaveRestoreHandle (InstanceWithSupports& instanceIn, PortMap& portMap)
2180 : instance (instanceIn.instance.
get()),
2181 features (instanceIn.features.getFeatureArray()),
2182 urids (*instanceIn.symap),
2186 PluginState save (
const LilvPlugin* plugin, LV2_URID_Map* mapFeature)
2188 return PluginState { lilv_state_new_from_instance (plugin,
2197 LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE,
2201 void restore (
const LilvState* stateIn)
2203 lilv_state_restore (stateIn,
2212 static const void* getPortValue (
const char* portSymbol,
2217 auto& handle = *
static_cast<SaveRestoreHandle*
> (userData);
2219 const auto state = handle.map.getState (portSymbol, handle.urids);
2225 static void setPortValue (
const char* portSymbol,
2231 const auto& handle = *
static_cast<const SaveRestoreHandle*
> (userData);
2232 handle.map.restoreState (portSymbol, handle.urids, {
static_cast<const char*
> (value),
size, type });
2235 LilvInstance* instance =
nullptr;
2236 const LV2_Feature*
const* features =
nullptr;
2237 const StatefulPortUrids urids;
2244 void operator() (LilvState* ptr)
const noexcept { lilv_state_free (ptr); }
2257 using GetDescriptor = LV2UI_Descriptor* (*) (uint32_t);
2259 UiDescriptorLibrary() =
default;
2261 explicit UiDescriptorLibrary (
const String& libraryPath)
2263 getDescriptor (lv2_shared::wordCast<GetDescriptor> (library->getFunction (
"lv2ui_descriptor"))) {}
2266 GetDescriptor getDescriptor =
nullptr;
2275 auto withLibraryPath (String v)
const noexcept {
return with (&UiDescriptorArgs::libraryPath, v); }
2276 auto withUiUri (String v)
const noexcept {
return with (&UiDescriptorArgs::uiUri, v); }
2279 UiDescriptorArgs with (String UiDescriptorArgs::* member, String value) const noexcept
2281 return juce::lv2_host::with (*
this, member, std::move (value));
2291 UiDescriptor() =
default;
2293 explicit UiDescriptor (
const UiDescriptorArgs& args)
2294 : library (args.libraryPath),
2295 descriptor (extractUiDescriptor (library, args.uiUri.toRawUTF8()))
2298 void portEvent (LV2UI_Handle ui,
2300 uint32_t bufferSize,
2302 const void* buffer)
const
2306 if (
auto* lv2Descriptor =
get())
2307 if (
auto* callback = lv2Descriptor->port_event)
2308 callback (ui, portIndex, bufferSize, format, buffer);
2311 bool hasExtensionData (World& world,
const char* uid)
const
2313 return world.ask (world.newUri (descriptor->URI).get(),
2314 world.newUri (LV2_CORE__extensionData).get(),
2315 world.newUri (uid).get());
2318 template <
typename Extension>
2319 OptionalExtension<Extension> getExtensionData (World& world,
const char* uid)
const
2321 if (! hasExtensionData (world, uid))
2324 if (
auto* lv2Descriptor =
get())
2325 if (
auto* extension = lv2Descriptor->extension_data)
2326 return OptionalExtension<Extension> (readUnaligned<Extension> (extension (uid)));
2331 const LV2UI_Descriptor*
get() const noexcept {
return descriptor; }
2334 static const LV2UI_Descriptor* extractUiDescriptor (
const UiDescriptorLibrary& lib,
const char* uiUri)
2336 if (lib.getDescriptor ==
nullptr)
2339 for (uint32_t i = 0;; ++i)
2341 const auto* descriptor = lib.getDescriptor (i);
2343 if (descriptor ==
nullptr)
2346 if (strcmp (uiUri, descriptor->URI) == 0)
2351 UiDescriptorLibrary library;
2352 const LV2UI_Descriptor* descriptor =
nullptr;
2357enum class Update { no, yes };
2368 ParameterValuesAndFlags() =
default;
2370 explicit ParameterValuesAndFlags (
size_t sizeIn)
2372 needsUiUpdate (sizeIn),
2373 needsProcessorUpdate (sizeIn)
2375 std::fill (values.begin(), values.end(), 0.0f);
2378 size_t size() const noexcept {
return values.size(); }
2380 void set (
size_t index,
float value, Update update)
2383 values[index].store (value, std::memory_order_relaxed);
2384 needsUiUpdate .set (index, update == Update::yes ? 1 : 0);
2385 needsProcessorUpdate.set (index, update == Update::yes ? 1 : 0);
2388 float get (
size_t index)
const noexcept
2391 return values[index].load (std::memory_order_relaxed);
2394 template <
typename Callback>
2395 void ifProcessorValuesChanged (Callback&& callback)
2397 ifChanged (needsProcessorUpdate, std::forward<Callback> (callback));
2400 template <
typename Callback>
2401 void ifUiValuesChanged (Callback&& callback)
2403 ifChanged (needsUiUpdate, std::forward<Callback> (callback));
2406 void clearUiFlags() { needsUiUpdate.clear(); }
2409 template <
typename Callback>
2410 void ifChanged (FlagCache<1>& flags, Callback&& callback)
2412 flags.ifSet ([
this, &callback] (
size_t groupIndex, uint32_t)
2414 callback (groupIndex, values[groupIndex].load (std::memory_order_relaxed));
2419 FlagCache<1> needsUiUpdate;
2420 FlagCache<1> needsProcessorUpdate;
2425class LV2Parameter :
public AudioPluginInstance::HostedParameter
2428 LV2Parameter (
const String& nameIn,
2429 const ParameterInfo& infoIn,
2430 ParameterValuesAndFlags& floatCache)
2431 : cache (floatCache),
2433 range (info.
min, info.
max),
2435 normalisedDefault (range.convertTo0to1 (infoIn.defaultValue))
2438 float getValue() const noexcept
override
2440 return range.convertTo0to1 (getDenormalisedValue());
2443 void setValue (
float f)
override
2445 cache.set ((
size_t) getParameterIndex(), range.convertFrom0to1 (f), Update::yes);
2448 void setDenormalisedValue (
float denormalised)
2450 cache.set ((
size_t) getParameterIndex(), denormalised, Update::yes);
2451 sendValueChangedMessageToListeners (range.convertTo0to1 (denormalised));
2454 void setDenormalisedValueWithoutTriggeringUpdate (
float denormalised)
2456 cache.set ((
size_t) getParameterIndex(), denormalised, Update::no);
2457 sendValueChangedMessageToListeners (range.convertTo0to1 (denormalised));
2460 float getDenormalisedValue() const noexcept
2462 return cache.get ((
size_t) getParameterIndex());
2465 float getDefaultValue()
const override {
return normalisedDefault; }
2466 float getDenormalisedDefaultValue()
const {
return info.defaultValue; }
2468 float getValueForText (
const String& text)
const override
2471 return range.convertTo0to1 (text.getFloatValue());
2473 const auto it =
std::find_if (info.scalePoints.begin(),
2474 info.scalePoints.end(),
2475 [&] (
const StoredScalePoint& stored) { return stored.label == text; });
2476 return it != info.scalePoints.end() ? range.convertTo0to1 (it->value) : normalisedDefault;
2479 int getNumSteps()
const override
2485 return static_cast<int> (info.scalePoints.size());
2488 return static_cast<int> (range.getRange().getLength()) + 1;
2490 return AudioProcessorParameter::getNumSteps();
2493 bool isDiscrete()
const override {
return info.isEnum || info.isInteger || info.isToggle; }
2494 bool isBoolean()
const override {
return info.isToggle; }
2496 StringArray getAllValueStrings()
const override
2501 return AudioProcessorParameter::getAllValueStrings();
2504 String getText (
float normalisedValue,
int)
const override
2506 const auto denormalised = range.convertFrom0to1 (normalisedValue);
2508 if (info.isEnum && ! info.scalePoints.empty())
2515 jassert (isPositiveAndBelow (index, info.scalePoints.size()));
2516 return info.scalePoints[(
size_t) index].label;
2519 return getFallbackParameterString (denormalised);
2522 String getParameterID()
const override
2524 return info.identifier;
2527 String getName (
int maxLength)
const override
2529 return name.substring (0, maxLength);
2532 String getLabel()
const override
2539 String getFallbackParameterString (
float denormalised)
const
2542 return denormalised > 0.0f ?
"On" :
"Off";
2545 return String {
static_cast<int> (denormalised) };
2547 return String { denormalised };
2550 static std::vector<float> findScalePointMidPoints (
const SafeSortedSet<StoredScalePoint>& set)
2556 result.
reserve (set.size() - 1);
2558 for (
auto it =
std::next (set.begin()); it != set.end(); ++it)
2566 ParameterValuesAndFlags& cache;
2567 const ParameterInfo info;
2569 const NormalisableRange<float> range;
2571 const float normalisedDefault;
2582 auto withBundlePath (File v)
const noexcept {
return withMember (*
this, &UiInstanceArgs::bundlePath, std::move (v)); }
2583 auto withPluginUri (URL v)
const noexcept {
return withMember (*
this, &UiInstanceArgs::pluginUri, std::move (v)); }
2592 UiInstance (World& world,
2593 const UiDescriptor* descriptorIn,
2594 const UiInstanceArgs& args,
2595 const LV2_Feature*
const* features,
2596 MessageBufferInterface<MessageHeader>& messagesIn,
2598 PhysicalResizeListener& rl)
2599 : descriptor (descriptorIn),
2600 resizeListener (rl),
2601 uiToProcessor (messagesIn),
2602 mLV2_UI__floatProtocol (map.map (LV2_UI__floatProtocol)),
2603 mLV2_ATOM__atomTransfer (map.map (LV2_ATOM__atomTransfer)),
2604 mLV2_ATOM__eventTransfer (map.map (LV2_ATOM__eventTransfer)),
2605 instance (makeInstance (args, features)),
2606 idleCallback (getExtensionData<LV2UI_Idle_Interface> (world, LV2_UI__idleInterface))
2608 jassert (descriptor !=
nullptr);
2614 LV2UI_Handle getHandle() const noexcept {
return instance.get(); }
2616 void pushMessage (MessageHeader header, uint32_t size,
const void* buffer)
2618 descriptor->portEvent (getHandle(), header.portIndex, size, header.protocol, buffer);
2623 if (idleCallback.valid && idleCallback.extension.idle !=
nullptr)
2624 return idleCallback.extension.idle (getHandle());
2629 template <
typename Extension>
2630 OptionalExtension<Extension> getExtensionData (World& world,
const char* uid)
const
2632 return descriptor->getExtensionData<Extension> (world, uid);
2635 Rectangle<int> getDetectedViewBounds()
const
2638 const auto frame = [(NSView*) widget frame];
2639 return { (
int) frame.size.width, (
int) frame.size.height };
2640 #elif JUCE_LINUX || JUCE_BSD
2643 unsigned int ww = 0, wh = 0, bw = 0, bitDepth = 0;
2645 XWindowSystemUtilities::ScopedXLock xLock;
2646 auto* display = XWindowSystem::getInstance()->getDisplay();
2647 X11Symbols::getInstance()->xGetGeometry (display,
2648 (::Drawable) widget,
2657 return { (
int) ww, (
int) wh };
2660 GetWindowRect ((HWND) widget, &rect);
2661 return { rect.right - rect.left, rect.bottom - rect.top };
2667 const UiDescriptor* descriptor =
nullptr;
2671 using Idle =
int (*) (LV2UI_Handle);
2673 Instance makeInstance (
const UiInstanceArgs& args,
const LV2_Feature*
const* features)
2675 if (descriptor->get() ==
nullptr)
2676 return {
nullptr, [] (LV2UI_Handle) {} };
2678 return Instance { descriptor->get()->instantiate (descriptor->get(),
2679 args.pluginUri.toString (
true).toRawUTF8(),
2680 File::addTrailingSeparator (args.bundlePath.getFullPathName()).toRawUTF8(),
2685 descriptor->get()->cleanup };
2688 void write (uint32_t portIndex, uint32_t bufferSize, uint32_t protocol,
const void* buffer)
2690 const LV2_URID protocols[] { 0, mLV2_UI__floatProtocol, mLV2_ATOM__atomTransfer, mLV2_ATOM__eventTransfer };
2695 uiToProcessor.pushMessage ({ portIndex, protocol }, bufferSize, buffer);
2699 static void writeFunction (LV2UI_Controller controller,
2701 uint32_t bufferSize,
2702 uint32_t portProtocol,
2705 jassert (controller !=
nullptr);
2706 static_cast<UiInstance*
> (controller)->write (portIndex, bufferSize, portProtocol, buffer);
2709 PhysicalResizeListener& resizeListener;
2710 MessageBufferInterface<MessageHeader>& uiToProcessor;
2711 LV2UI_Widget widget =
nullptr;
2712 const LV2_URID mLV2_UI__floatProtocol;
2713 const LV2_URID mLV2_ATOM__atomTransfer;
2714 const LV2_URID mLV2_ATOM__eventTransfer;
2716 OptionalExtension<LV2UI_Idle_Interface> idleCallback;
2719 NSViewFrameWatcher frameWatcher { (NSView*) widget, [
this]
2721 const auto bounds = getDetectedViewBounds();
2722 resizeListener.viewRequestedResizeInPhysicalPixels (bounds.getWidth(), bounds.getHeight());
2725 WindowSizeChangeListener frameWatcher { (HWND) widget, resizeListener };
2733 virtual ~TouchListener() =
default;
2734 virtual void controlGrabbed (uint32_t port,
bool grabbed) = 0;
2741 : callback (
std::
move (callbackIn)) {}
2743 ~AsyncFn()
override { cancelPendingUpdate(); }
2745 void handleAsyncUpdate()
override { callback(); }
2754 float initialScaleFactor = 0.0f, sampleRate = 0.0f;
2756 auto withInitialScaleFactor (
float v)
const {
return with (&UiFeaturesDataOptions::initialScaleFactor, v); }
2757 auto withSampleRate (
float v)
const {
return with (&UiFeaturesDataOptions::sampleRate, v); }
2760 UiFeaturesDataOptions with (
float UiFeaturesDataOptions::* member,
float value) const
2762 return juce::lv2_host::with (*
this, member, value);
2769 UiFeaturesData (PhysicalResizeListener& rl,
2771 LV2_Handle instanceIn,
2772 LV2UI_Widget parentIn,
2773 Instance::GetExtensionData getExtensionData,
2776 const UiFeaturesDataOptions& optIn)
2778 resizeListener (rl),
2780 instance (instanceIn),
2783 dataAccess { getExtensionData },
2784 portIndices (makePortIndices (ports))
2788 const LV2_Feature*
const* getFeatureArray() const noexcept {
return features.pointers.data(); }
2792 return Features::getUris (makeFeatures ({}, {}, {}, {}, {}, {}, {}, {}, {}, {}));
2795 Rectangle<int> getLastRequestedBounds()
const {
return { lastRequestedWidth, lastRequestedHeight }; }
2799 LV2UI_Widget parent,
2801 LV2_Extension_Data_Feature* data,
2803 LV2_URID_Unmap* unmap,
2804 LV2UI_Port_Map* portMap,
2806 LV2_Options_Option* options,
2809 return { LV2_Feature { LV2_UI__resize, resize },
2810 LV2_Feature { LV2_UI__parent, parent },
2811 LV2_Feature { LV2_UI__idleInterface,
nullptr },
2812 LV2_Feature { LV2_INSTANCE_ACCESS_URI, handle },
2813 LV2_Feature { LV2_DATA_ACCESS_URI,
data },
2814 LV2_Feature { LV2_URID__map, map },
2815 LV2_Feature { LV2_URID__unmap, unmap},
2816 LV2_Feature { LV2_UI__portMap, portMap },
2817 LV2_Feature { LV2_UI__touch, touch },
2818 LV2_Feature { LV2_OPTIONS__options, options },
2819 LV2_Feature { LV2_LOG__log,
log } };
2822 int resizeCallback (
int width,
int height)
2824 lastRequestedWidth = width;
2825 lastRequestedHeight = height;
2826 resizeListener.viewRequestedResizeInPhysicalPixels (width, height);
2830 static int resizeCallback (LV2UI_Feature_Handle handle,
int width,
int height)
2832 return static_cast<UiFeaturesData*
> (handle)->resizeCallback (width, height);
2835 uint32_t portIndexCallback (
const char* symbol)
const
2837 const auto it = portIndices.find (symbol);
2838 return it != portIndices.cend() ? it->second : LV2UI_INVALID_PORT_INDEX;
2841 static uint32_t portIndexCallback (LV2UI_Feature_Handle handle,
const char* symbol)
2843 return static_cast<const UiFeaturesData*
> (handle)->portIndexCallback (symbol);
2846 void touchCallback (uint32_t portIndex,
bool grabbed)
const
2848 touchListener.controlGrabbed (portIndex, grabbed);
2851 static void touchCallback (LV2UI_Feature_Handle handle, uint32_t index,
bool b)
2853 return static_cast<const UiFeaturesData*
> (handle)->touchCallback (index, b);
2860 ports.forEachPort ([&] (
const PortHeader& header)
2862 [[maybe_unused]]
const auto emplaced = result.
emplace (header.symbol, header.index);
2871 const UiFeaturesDataOptions opts;
2872 PhysicalResizeListener& resizeListener;
2873 TouchListener& touchListener;
2874 LV2_Handle instance{};
2875 LV2UI_Widget parent{};
2877 const UsefulUrids urids { symap };
2879 int lastRequestedWidth = 0, lastRequestedHeight = 0;
2882 symap.map (LV2_UI__scaleFactor),
2884 symap.map (LV2_ATOM__Float),
2885 &opts.initialScaleFactor },
2886 { LV2_OPTIONS_INSTANCE,
2888 symap.map (LV2_PARAMETERS__sampleRate),
2890 symap.map (LV2_ATOM__Float),
2892 { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0,
nullptr } };
2893 LV2UI_Resize resize {
this, resizeCallback };
2894 LV2_URID_Map map = symap.getMapFeature();
2895 LV2_URID_Unmap unmap = symap.getUnmapFeature();
2896 LV2UI_Port_Map portMap {
this, portIndexCallback };
2897 LV2UI_Touch touch {
this, touchCallback };
2898 LV2_Extension_Data_Feature dataAccess;
2900 Features features { makeFeatures (&resize,
2909 log.getLogFeature()) };
2917 UiInstanceWithSupports (World& world,
2918 PhysicalResizeListener& resizeListener,
2919 TouchListener& touchListener,
2920 const UiDescriptor* descriptor,
2921 const UiInstanceArgs& args,
2922 LV2UI_Widget parent,
2923 InstanceWithSupports& engineInstance,
2924 const UiFeaturesDataOptions& opts)
2925 : features (resizeListener,
2927 engineInstance.instance.getHandle(),
2929 engineInstance.instance.getExtensionDataCallback(),
2930 engineInstance.ports,
2931 *engineInstance.symap,
2936 features.getFeatureArray(),
2937 engineInstance.uiToProcessor,
2938 *engineInstance.symap,
2942 UiFeaturesData features;
2943 UiInstance instance;
2950 explicit RequiredFeatures (OwningNodes nodes)
2951 : values (
std::
move (nodes)) {}
2958 explicit OptionalFeatures (OwningNodes nodes)
2959 : values (
std::
move (nodes)) {}
2964template <
typename Range,
typename Predicate>
2976 PeerChangedListener (Component& c,
std::function<
void()> peerChangedIn)
2977 : ComponentMovementWatcher (&c), peerChanged (
std::
move (peerChangedIn))
2981 void componentMovedOrResized (
bool,
bool)
override {}
2982 void componentPeerChanged()
override { NullCheckedInvocation::invoke (peerChanged); }
2983 void componentVisibilityChanged()
override {}
2985 using ComponentMovementWatcher::componentVisibilityChanged;
2986 using ComponentMovementWatcher::componentMovedOrResized;
2994 ViewSizeListener (Component& c, PhysicalResizeListener& l)
2995 : ComponentMovementWatcher (&c), listener (l)
2999 void componentMovedOrResized (
bool,
bool wasResized)
override
3003 const auto physicalSize = Desktop::getInstance().getDisplays()
3004 .logicalToPhysical (getComponent()->localAreaToGlobal (getComponent()->getLocalBounds()));
3005 const auto width = physicalSize.getWidth();
3006 const auto height = physicalSize.getHeight();
3008 if (width > 10 && height > 10)
3009 listener.viewRequestedResizeInPhysicalPixels (width, height);
3013 void componentPeerChanged()
override {}
3014 void componentVisibilityChanged()
override {}
3016 using ComponentMovementWatcher::componentVisibilityChanged;
3017 using ComponentMovementWatcher::componentMovedOrResized;
3019 PhysicalResizeListener& listener;
3026 ConfiguredEditorComponent (World& world,
3027 InstanceWithSupports& instance,
3028 UiDescriptor& uiDescriptor,
3029 LogicalResizeListener& resizeListenerIn,
3030 TouchListener& touchListener,
3031 const String& uiBundleUri,
3032 const UiFeaturesDataOptions& opts)
3033 : resizeListener (resizeListenerIn),
3034 floatUrid (instance.symap->map (LV2_ATOM__Float)),
3035 scaleFactorUrid (instance.symap->map (LV2_UI__scaleFactor)),
3036 uiInstance (new UiInstanceWithSupports (world,
3040 UiInstanceArgs{}.withBundlePath (bundlePathFromUri (uiBundleUri.toRawUTF8()))
3041 .withPluginUri (URL (instance.instance.getUri())),
3042 viewComponent.getWidget(),
3045 resizeClient (uiInstance->instance.getExtensionData<LV2UI_Resize> (world, LV2_UI__resize)),
3046 optionsInterface (uiInstance->instance.getExtensionData<LV2_Options_Interface> (world, LV2_OPTIONS__interface))
3048 jassert (uiInstance !=
nullptr);
3051 addAndMakeVisible (viewComponent);
3053 const auto boundsToUse = [&]
3055 const auto requested = uiInstance->features.getLastRequestedBounds();
3057 if (requested.getWidth() > 10 && requested.getHeight() > 10)
3060 return uiInstance->instance.getDetectedViewBounds();
3063 const auto scaled = lv2ToComponentRect (boundsToUse);
3064 lastWidth = scaled.getWidth();
3065 lastHeight = scaled.getHeight();
3066 setSize (lastWidth, lastHeight);
3069 ~ConfiguredEditorComponent()
override
3071 viewComponent.prepareForDestruction();
3074 void paint (Graphics& g)
override
3076 g.fillAll (Colours::black);
3079 void resized()
override
3081 viewComponent.setBounds (getLocalBounds());
3084 void updateViewBounds()
3088 if (uiInstance !=
nullptr)
3090 if (resizeClient.valid && resizeClient.extension.ui_resize !=
nullptr)
3092 const auto physicalSize = componentToLv2Rect (getLocalBounds());
3094 resizeClient.extension.ui_resize (uiInstance->instance.getHandle(),
3095 physicalSize.getWidth(),
3096 physicalSize.getHeight());
3101 void pushMessage (MessageHeader header, uint32_t size,
const void* buffer)
3103 if (uiInstance !=
nullptr)
3104 uiInstance->instance.pushMessage (header, size, buffer);
3109 if (uiInstance !=
nullptr)
3110 return uiInstance->instance.idle();
3115 void childBoundsChanged (Component* c)
override
3121 void setUserScaleFactor (
float userScale) { userScaleFactor = userScale; }
3123 void sendScaleFactorToPlugin()
3125 const auto factor = getEffectiveScale();
3127 const LV2_Options_Option options[]
3129 { LV2_OPTIONS_INSTANCE, 0, scaleFactorUrid,
sizeof (
float), floatUrid, &factor },
3130 { {}, {}, {}, {}, {}, {} }
3133 if (optionsInterface.valid)
3134 optionsInterface.extension.set (uiInstance->instance.getHandle(), options);
3136 applyLastRequestedPhysicalSize();
3140 void viewRequestedResizeInPhysicalPixels (
int width,
int height)
override
3143 lastHeight = height;
3144 const auto logical = lv2ToComponentRect ({ width, height });
3145 resizeListener.viewRequestedResizeInLogicalPixels (logical.getWidth(), logical.getHeight());
3148 void resizeToFitView()
3150 viewComponent.fitToView();
3151 resizeListener.viewRequestedResizeInLogicalPixels (viewComponent.getWidth(), viewComponent.getHeight());
3154 void applyLastRequestedPhysicalSize()
3156 viewRequestedResizeInPhysicalPixels (lastWidth, lastHeight);
3157 viewComponent.forceViewToSize();
3161 Rectangle<int> componentToLv2Rect (Rectangle<int> r)
const
3163 return localAreaToGlobal (r) * nativeScaleFactor * getDesktopScaleFactor();
3167 Rectangle<int> lv2ToComponentRect (Rectangle<int> vr)
const
3169 return getLocalArea (
nullptr, vr / (nativeScaleFactor * getDesktopScaleFactor()));
3172 float getEffectiveScale()
const {
return nativeScaleFactor * userScaleFactor; }
3176 #if JUCE_LINUX || JUCE_BSD
3179 struct Inner final :
public XEmbedComponent
3181 Inner() : XEmbedComponent (true, true)
3191 struct ViewComponent final :
public InnerHolder,
3192 public XEmbedComponent
3194 explicit ViewComponent (PhysicalResizeListener& l)
3195 : XEmbedComponent ((unsigned
long) inner.getPeer()->getNativeHandle(), true, false),
3206 void prepareForDestruction()
3208 inner.removeClient();
3211 LV2UI_Widget getWidget() {
return lv2_shared::wordCast<LV2UI_Widget> (inner.getHostWindowID()); }
3212 void forceViewToSize() {}
3215 ViewSizeListener listener;
3218 struct ViewComponent final :
public NSViewComponentWithParent
3220 explicit ViewComponent (PhysicalResizeListener&)
3221 : NSViewComponentWithParent (WantsNudge::no) {}
3222 LV2UI_Widget getWidget() {
return getView(); }
3223 void forceViewToSize() {}
3224 void fitToView() { resizeToFitView(); }
3225 void prepareForDestruction() {}
3228 struct ViewComponent final :
public HWNDComponent
3230 explicit ViewComponent (PhysicalResizeListener&)
3233 inner.addToDesktop (0);
3235 if (
auto* peer = inner.getPeer())
3236 setHWND (peer->getNativeHandle());
3239 void paint (Graphics& g)
override { g.fillAll (Colours::black); }
3241 LV2UI_Widget getWidget() {
return getHWND(); }
3243 void forceViewToSize() { updateHWNDBounds(); }
3244 void fitToView() { resizeToFit(); }
3246 void prepareForDestruction() {}
3249 struct Inner final :
public Component
3251 Inner() { setOpaque (
true); }
3252 void paint (Graphics& g)
override { g.fillAll (Colours::black); }
3258 struct ViewComponent final :
public Component
3260 explicit ViewComponent (PhysicalResizeListener&) {}
3261 void* getWidget() {
return nullptr; }
3262 void forceViewToSize() {}
3264 void prepareForDestruction() {}
3268 struct ScaleNotifierCallback
3270 ConfiguredEditorComponent& window;
3272 void operator() (
float platformScale)
const
3274 MessageManager::callAsync ([ref = Component::SafePointer<ConfiguredEditorComponent> (&window), platformScale]
3276 if (
auto* r =
ref.getComponent())
3278 if (approximatelyEqual (std::exchange (r->nativeScaleFactor, platformScale), platformScale))
3281 r->nativeScaleFactor = platformScale;
3282 r->sendScaleFactorToPlugin();
3288 LogicalResizeListener& resizeListener;
3289 int lastWidth = 0, lastHeight = 0;
3290 float nativeScaleFactor = 1.0f, userScaleFactor = 1.0f;
3291 NativeScaleFactorNotifier scaleNotifier {
this, ScaleNotifierCallback { *
this } };
3292 ViewComponent viewComponent { *
this };
3293 LV2_URID floatUrid, scaleFactorUrid;
3295 OptionalExtension<LV2UI_Resize> resizeClient;
3296 OptionalExtension<LV2_Options_Interface> optionsInterface;
3297 PeerChangedListener peerListener { *
this, [
this]
3299 applyLastRequestedPhysicalSize();
3309 virtual ~EditorListener() =
default;
3321 virtual void viewCreated (UiEventListener* newListener) = 0;
3323 virtual void notifyEditorBeingDeleted() = 0;
3331 virtual ~InstanceProvider() noexcept = default;
3333 virtual InstanceWithSupports* getInstanceWithSupports() const = 0;
3336class Editor final : public AudioProcessorEditor,
3337 public UiEventListener,
3338 private LogicalResizeListener
3341 Editor (World& worldIn,
3342 AudioPluginInstance& p,
3343 InstanceProvider& instanceProviderIn,
3344 UiDescriptor& uiDescriptorIn,
3345 TouchListener& touchListenerIn,
3346 EditorListener& listenerIn,
3347 const String& uiBundleUriIn,
3348 RequiredFeatures requiredIn,
3349 OptionalFeatures optionalIn)
3350 : AudioProcessorEditor (p),
3352 instanceProvider (&instanceProviderIn),
3353 uiDescriptor (&uiDescriptorIn),
3354 touchListener (&touchListenerIn),
3355 listener (&listenerIn),
3356 uiBundleUri (uiBundleUriIn),
3357 required (
std::
move (requiredIn)),
3358 optional (
std::
move (optionalIn))
3360 setResizable (isResizable (required, optional),
false);
3366 instanceProvider->getInstanceWithSupports()->processorToUi->addUi (*
this);
3369 ~Editor() noexcept
override
3371 instanceProvider->getInstanceWithSupports()->processorToUi->removeUi (*
this);
3373 listener->notifyEditorBeingDeleted();
3378 const auto initialScale = userScaleFactor * (
float) [&]
3380 if (
auto* p = getPeer())
3381 return p->getPlatformScaleFactor();
3386 const auto opts = UiFeaturesDataOptions{}.withInitialScaleFactor (initialScale)
3387 .withSampleRate ((
float) processor.getSampleRate());
3388 configuredEditor =
nullptr;
3389 configuredEditor =
rawToUniquePtr (
new ConfiguredEditorComponent (world,
3390 *instanceProvider->getInstanceWithSupports(),
3396 parentHierarchyChanged();
3397 const auto initialSize = configuredEditor->getBounds();
3398 setSize (initialSize.getWidth(), initialSize.getHeight());
3400 listener->viewCreated (
this);
3405 configuredEditor =
nullptr;
3408 void paint (Graphics& g)
override
3410 g.fillAll (Colours::black);
3413 void resized()
override
3415 const ScopedValueSetter<bool> scope (resizeFromHost,
true);
3417 if (
auto* inner = configuredEditor.get())
3419 inner->setBounds (getLocalBounds());
3420 inner->updateViewBounds();
3424 void parentHierarchyChanged()
override
3426 if (
auto* comp = configuredEditor.get())
3429 addAndMakeVisible (comp);
3431 removeChildComponent (comp);
3435 void pushMessage (MessageHeader header, uint32_t size,
const void* buffer)
override
3437 if (
auto* comp = configuredEditor.get())
3438 comp->pushMessage (header, size, buffer);
3443 if (
auto* comp = configuredEditor.get())
3444 return comp->idle();
3449 void setScaleFactor (
float newScale)
override
3451 userScaleFactor = newScale;
3453 if (configuredEditor !=
nullptr)
3455 configuredEditor->setUserScaleFactor (userScaleFactor);
3456 configuredEditor->sendScaleFactorToPlugin();
3461 bool isResizable (
const RequiredFeatures& requiredFeatures,
3462 const OptionalFeatures& optionalFeatures)
const
3464 const auto uriMatches = [] (
const LilvNode* node)
3466 const auto* uri = lilv_node_as_uri (node);
3467 return std::strcmp (uri, LV2_UI__noUserResize) == 0;
3470 return uiDescriptor->hasExtensionData (world, LV2_UI__resize)
3471 && ! uiDescriptor->hasExtensionData (world, LV2_UI__noUserResize)
3472 && noneOf (requiredFeatures.values, uriMatches)
3473 && noneOf (optionalFeatures.values, uriMatches);
3476 bool isScalable()
const
3478 return uiDescriptor->hasExtensionData (world, LV2_OPTIONS__interface);
3481 void viewRequestedResizeInLogicalPixels (
int width,
int height)
override
3483 if (! resizeFromHost)
3484 setSize (width, height);
3488 InstanceProvider* instanceProvider;
3489 UiDescriptor* uiDescriptor;
3490 TouchListener* touchListener;
3491 EditorListener* listener;
3493 const RequiredFeatures required;
3494 const OptionalFeatures optional;
3496 float userScaleFactor = 1.0f;
3497 bool resizeFromHost =
false;
3505 explicit Uis (
const LilvPlugin* plugin) noexcept : uis (lilv_plugin_get_uis (plugin)) {}
3507 unsigned size() const noexcept {
return lilv_uis_size (uis.get()); }
3509 UisIterator
begin() const noexcept {
return UisIterator { uis.get() }; }
3510 UisIterator
end() const noexcept {
return UisIterator{}; }
3512 const LilvUI* getByUri (
const NodeUri& uri)
const
3514 return lilv_uis_get_by_uri (uis.get(), uri.get());
3520 void operator() (LilvUIs* ptr)
const noexcept { lilv_uis_free (ptr); }
3530 explicit PluginClass (
const LilvPluginClass* c) : pluginClass (c) {}
3532 NodeUri getParentUri() const noexcept {
return NodeUri::copy (lilv_plugin_class_get_parent_uri (pluginClass)); }
3533 NodeUri getUri() const noexcept {
return NodeUri::copy (lilv_plugin_class_get_uri (pluginClass)); }
3534 NodeString getLabel() const noexcept {
return NodeString::copy (lilv_plugin_class_get_label (pluginClass)); }
3535 OwningPluginClasses getChildren() const noexcept
3537 return OwningPluginClasses { OwningPluginClasses::type { lilv_plugin_class_get_children (pluginClass) } };
3541 const LilvPluginClass* pluginClass =
nullptr;
3544using FloatWriter = void (*) (LV2_Atom_Forge*,
float);
3546struct ParameterWriterUrids
3548 LV2_URID mLV2_PATCH__Set;
3549 LV2_URID mLV2_PATCH__property;
3550 LV2_URID mLV2_PATCH__value;
3551 LV2_URID mLV2_ATOM__eventTransfer;
3554struct MessageHeaderAndSize
3556 MessageHeader header;
3560class ParameterWriter
3563 ParameterWriter (ControlPort* p)
3564 :
data (PortBacking { p }), kind (Kind::port) {}
3566 ParameterWriter (FloatWriter write, LV2_URID urid, uint32_t controlPortIndex)
3567 :
data (PatchBacking {
write, urid, controlPortIndex }), kind (Kind::patch) {}
3569 void writeToProcessor (
const ParameterWriterUrids urids, LV2_Atom_Forge* forge,
float value)
const
3575 if (forge !=
nullptr)
3577 lv2_atom_forge_frame_time (forge, 0);
3578 writeSetToForge (urids, *forge, value);
3585 data.port.port->currentValue = value;
3590 MessageHeaderAndSize writeToUi (
const ParameterWriterUrids urids, LV2_Atom_Forge& forge,
float value)
const
3592 const auto getWrittenBytes = [&]() -> uint32_t
3594 if (
const auto* atom = convertToAtomPtr (forge.buf, forge.size))
3595 return (uint32_t) (atom->size +
sizeof (LV2_Atom));
3604 writeSetToForge (urids, forge, value);
3605 return { {
data.patch.controlPortIndex, urids.mLV2_ATOM__eventTransfer }, getWrittenBytes() };
3608 lv2_atom_forge_raw (&forge, &value,
sizeof (value));
3609 return { {
data.port.port->header.index, 0 },
sizeof (value) };
3612 return { { 0, 0 }, 0 };
3615 const LV2_URID* getUrid()
const
3617 return kind == Kind::patch ? &
data.patch.urid :
nullptr;
3620 const uint32_t* getPortIndex()
const
3622 return kind == Kind::port ? &
data.port.port->header.index :
nullptr;
3626 void writeSetToForge (
const ParameterWriterUrids urids, LV2_Atom_Forge& forge,
float value)
const
3628 lv2_shared::ObjectFrame
object { &forge, (
uint32_t) 0, urids.mLV2_PATCH__Set };
3630 lv2_atom_forge_key (&forge, urids.mLV2_PATCH__property);
3631 lv2_atom_forge_urid (&forge,
data.patch.urid);
3633 lv2_atom_forge_key (&forge, urids.mLV2_PATCH__value);
3634 data.patch.write (&forge, value);
3651 static_assert (std::is_trivial_v<PortBacking>,
"PortBacking must be trivial");
3652 static_assert (std::is_trivial_v<PatchBacking>,
"PatchBacking must be trivial");
3654 explicit Data (PortBacking p) : port (p) {}
3655 explicit Data (PatchBacking p) : patch (p) {}
3661 enum class Kind { port, patch };
3669static String lilvNodeToUriString (
const LilvNode* node)
3671 return node !=
nullptr ? String::fromUTF8 (lilv_node_as_uri (node)) : String{};
3674static String lilvNodeToString (
const LilvNode* node)
3676 return node !=
nullptr ? String::fromUTF8 (lilv_node_as_string (node)) : String{};
3686class IntermediateParameterTree
3689 explicit IntermediateParameterTree (World& worldIn)
3692 const auto groups = getGroups (world);
3693 const auto symbolNode = world.newUri (LV2_CORE__symbol);
3694 const auto nameNode = world.newUri (LV2_CORE__name);
3696 for (
const auto& group : groups)
3698 const auto symbol = lilvNodeToString (world.get (group.get(), symbolNode.get(),
nullptr).get());
3699 const auto name = lilvNodeToString (world.get (group.get(), nameNode .get(),
nullptr).get());
3700 owning.emplace (lilvNodeToUriString (group.get()),
3701 std::make_unique<AudioProcessorParameterGroup> (symbol, name,
"|"));
3707 if (param ==
nullptr)
3710 const auto it = owning.find (group);
3711 (it != owning.cend() ? *it->second : topLevel).addChild (std::move (param));
3714 static AudioProcessorParameterGroup getTree (IntermediateParameterTree tree)
3718 for (
const auto& pair : tree.owning)
3719 nonowning.emplace (pair.first, pair.second.
get());
3721 const auto groups = getGroups (tree.world);
3722 const auto subgroupNode = tree.world.newUri (LV2_PORT_GROUPS__subGroupOf);
3724 for (
const auto& group : groups)
3726 const auto innerIt = tree.owning.find (lilvNodeToUriString (group.get()));
3728 if (innerIt == tree.owning.cend())
3731 const auto outer = lilvNodeToUriString (tree.world.get (group.get(), subgroupNode.get(),
nullptr).get());
3732 const auto outerIt = nonowning.
find (outer);
3734 if (outerIt != nonowning.
cend() && containsParameters (outerIt->second))
3735 outerIt->second->addChild (std::move (innerIt->second));
3738 for (
auto& subgroup : tree.owning)
3739 if (containsParameters (subgroup.second.
get()))
3740 tree.topLevel.addChild (
std::
move (subgroup.second));
3742 return std::move (tree.topLevel);
3750 for (
auto* uri : { LV2_PORT_GROUPS__Group, LV2_PORT_GROUPS__InputGroup, LV2_PORT_GROUPS__OutputGroup })
3751 for (
const auto* group : world.findNodes (nullptr, world.newUri (LILV_NS_RDF
"type").
get(), world.newUri (uri).
get()))
3752 names.push_back (OwningNode { lilv_node_duplicate (group) });
3757 static bool containsParameters (
const AudioProcessorParameterGroup* g)
3762 for (
auto* node : *g)
3764 if (node->getParameter() !=
nullptr)
3767 if (
auto* group = node->getGroup())
3768 if (containsParameters (group))
3776 AudioProcessorParameterGroup topLevel;
3782struct BypassParameter final :
public LV2Parameter
3784 BypassParameter (
const ParameterInfo& parameterInfo, ParameterValuesAndFlags& cacheIn)
3785 : LV2Parameter (
"Bypass", parameterInfo, cacheIn) {}
3787 float getValue() const noexcept
override
3789 return LV2Parameter::getValue() > 0.0f ? 0.0f : 1.0f;
3792 void setValue (
float newValue)
override
3794 LV2Parameter::setValue (newValue > 0.0f ? 0.0f : 1.0f);
3797 float getDefaultValue()
const override {
return 0.0f; }
3798 bool isAutomatable()
const override {
return true; }
3799 bool isDiscrete()
const override {
return true; }
3800 bool isBoolean()
const override {
return true; }
3801 int getNumSteps()
const override {
return 2; }
3802 StringArray getAllValueStrings()
const override {
return {
TRANS (
"Off"),
TRANS (
"On") }; }
3808 ParameterWriter writer;
3813template <
typename T>
3814static auto getPortPointers (SimpleSpan<T> range)
3819 for (
auto& port : range)
3821 result.resize (
std::max ((
size_t) (port.header.index + 1), result.
size()),
nullptr);
3822 result[port.header.index] = &port;
3829 const ParameterData& data,
3830 ParameterValuesAndFlags& cache)
3837 if (enabledPortIndex !=
nullptr)
3838 if (
auto* index =
data.writer.getPortIndex())
3839 if (*index == *enabledPortIndex)
3840 return std::make_unique<BypassParameter> (
data.info, cache);
3842 return std::make_unique<LV2Parameter> (
data.name,
data.info, cache);
3845class ControlPortAccelerationStructure
3848 ControlPortAccelerationStructure (SimpleSpan<ControlPort> controlPorts)
3849 : indexedControlPorts (getPortPointers (controlPorts))
3851 for (
const auto& port : controlPorts)
3852 if (port.header.direction == Port::Direction::output)
3853 outputPorts.push_back (&port);
3858 ControlPort* getControlPortByIndex (uint32_t index)
const
3860 if (isPositiveAndBelow (index, indexedControlPorts.size()))
3861 return indexedControlPorts[index];
3866 void writeOutputPorts (UiEventListener* target, MessageBufferInterface<UiMessageHeader>& uiMessages)
const
3868 if (target ==
nullptr)
3871 for (
const auto* port : outputPorts)
3873 const auto chars = toChars (port->currentValue);
3874 uiMessages.pushMessage ({ target, { port->header.index, 0 } }, (
uint32_t) chars.size(), chars.data());
3883class ParameterValueCache
3889 ParameterValueCache (AudioPluginInstance& processor,
3891 LV2_URID_Map mapFeature,
3893 ControlPort* enabledPort)
3894 : uiForge (mapFeature),
3904 IntermediateParameterTree tree { world };
3906 const auto* enabledPortIndex = enabledPort !=
nullptr ? &enabledPort->header.index
3909 for (
const auto& item :
data)
3911 auto param = makeParameter (enabledPortIndex, item, cache);
3913 if (
auto* urid = item.writer.getUrid())
3914 urids.emplace (*urid, param.
get());
3916 if (
auto* index = item.writer.getPortIndex())
3917 portIndices.emplace (*index, param.
get());
3919 writerForParameter.
emplace (param.
get(), item.writer);
3921 tree.addParameter (item.group, std::move (param));
3924 processor.setHostedParameterTree (IntermediateParameterTree::getTree (std::move (tree)));
3927 writers.reserve (
data.size());
3929 for (
auto* param : processor.getParameters())
3931 const auto it = writerForParameter.
find (param);
3933 writers.push_back (it->second);
3937 jassert (processor.getParameters().size() == (
int) (urids.size() + portIndices.size()));
3940 const auto setToDefault = [] (
auto& container)
3942 for (
auto& item : container)
3943 item.second->setDenormalisedValueWithoutTriggeringUpdate (item.second->getDenormalisedDefaultValue());
3946 setToDefault (urids);
3947 setToDefault (portIndices);
3950 void postChangedParametersToProcessor (
const ParameterWriterUrids helperUrids,
3951 LV2_Atom_Forge* forge)
3953 cache.ifProcessorValuesChanged ([&] (
size_t index,
float value)
3955 writers[index].writeToProcessor (helperUrids, forge, value);
3959 void postChangedParametersToUi (UiEventListener* target,
3960 const ParameterWriterUrids helperUrids,
3961 MessageBufferInterface<UiMessageHeader>& uiMessages)
3963 if (target ==
nullptr)
3966 cache.ifUiValuesChanged ([&] (
size_t index,
float value)
3968 writeParameterToUi (target, writers[index], value, helperUrids, uiMessages);
3972 void postAllParametersToUi (UiEventListener* target,
3973 const ParameterWriterUrids helperUrids,
3974 MessageBufferInterface<UiMessageHeader>& uiMessages)
3976 if (target ==
nullptr)
3979 const auto numWriters = writers.size();
3981 for (
size_t i = 0; i < numWriters; ++i)
3982 writeParameterToUi (target, writers[i], cache.get (i), helperUrids, uiMessages);
3984 cache.clearUiFlags();
3987 LV2Parameter* getParamByUrid (LV2_URID urid)
const
3989 const auto it = urids.find (urid);
3990 return it != urids.end() ? it->second :
nullptr;
3993 LV2Parameter* getParamByPortIndex (uint32_t portIndex)
const
3995 const auto it = portIndices.find (portIndex);
3996 return it != portIndices.end() ? it->second :
nullptr;
3999 void updateFromControlPorts (
const ControlPortAccelerationStructure& ports)
const
4001 for (
const auto& pair : portIndices)
4002 if (auto* port = ports.getControlPortByIndex (pair.first))
4003 if (auto* param = pair.second)
4004 param->setDenormalisedValueWithoutTriggeringUpdate (port->currentValue);
4008 void writeParameterToUi (UiEventListener* target,
4009 const ParameterWriter& writer,
4011 const ParameterWriterUrids helperUrids,
4012 MessageBufferInterface<UiMessageHeader>& uiMessages)
4016 uiForge.setBuffer (forgeStorage.data(), forgeStorage.size());
4017 const auto messageHeader = writer.writeToUi (helperUrids, *uiForge.get(), value);
4018 uiMessages.pushMessage ({ target, messageHeader.header }, messageHeader.size, forgeStorage.data());
4021 SingleSizeAlignedStorage<8> forgeStorage { 256 };
4022 lv2_shared::AtomForge uiForge;
4024 ParameterValuesAndFlags cache;
4032struct PatchSetCallback
4034 explicit PatchSetCallback (ParameterValueCache& x) : cache (x) {}
4038 void setParameter (LV2_URID property,
float value)
const noexcept
4040 if (
auto* param = cache.getParamByUrid (property))
4041 param->setDenormalisedValueWithoutTriggeringUpdate (value);
4046 ParameterValueCache& cache;
4049struct SupportedParameter
4056static SupportedParameter getInfoForPatchParameter (World& worldIn,
4057 const UsefulUrids& urids,
4058 const NodeUri& property)
4060 const auto rangeUri = worldIn.newUri (LILV_NS_RDFS
"range");
4061 const auto type = worldIn.get (property.get(), rangeUri.get(),
nullptr);
4063 if (type ==
nullptr)
4064 return { {},
false, {} };
4066 const auto typeUrid = urids.symap.map (lilv_node_as_uri (type.get()));
4068 const LV2_URID types[] { urids.mLV2_ATOM__Int,
4069 urids.mLV2_ATOM__Long,
4070 urids.mLV2_ATOM__Float,
4071 urids.mLV2_ATOM__Double,
4072 urids.mLV2_ATOM__Bool };
4075 return { {},
false, {} };
4077 const auto getValue = [&] (
const char* uri,
float fallback)
4079 return Port::getFloatValue (worldIn.get (property.get(), worldIn.newUri (uri).get(),
nullptr).get(), fallback);
4082 const auto hasPortProperty = [&] (
const char* uri)
4084 return worldIn.ask (property.get(),
4085 worldIn.newUri (LV2_CORE__portProperty).get(),
4086 worldIn.newUri (uri).get());
4089 const auto metadataScalePoints = worldIn.findNodes (property.get(),
4090 worldIn.newUri (LV2_CORE__scalePoint).get(),
4092 SafeSortedSet<StoredScalePoint> parsedScalePoints;
4094 for (
const auto* scalePoint : metadataScalePoints)
4096 const auto label = worldIn.get (scalePoint, worldIn.newUri (LILV_NS_RDFS
"label").get(),
nullptr);
4097 const auto value = worldIn.get (scalePoint, worldIn.newUri (LILV_NS_RDF
"value").get(),
nullptr);
4099 if (label !=
nullptr && value !=
nullptr)
4100 parsedScalePoints.insert ({ lilv_node_as_string (label.get()), lilv_node_as_float (value.get()) });
4105 const auto minimum = getValue (LV2_CORE__minimum, 0.0f);
4106 const auto maximum = getValue (LV2_CORE__maximum, 1.0f);
4108 return { { std::move (parsedScalePoints),
4109 "des:" + String::fromUTF8 (property.getTyped()),
4110 getValue (LV2_CORE__default, (minimum + maximum) * 0.5f),
4113 typeUrid == urids.mLV2_ATOM__Bool || hasPortProperty (LV2_CORE__toggled),
4114 typeUrid == urids.mLV2_ATOM__Int || typeUrid == urids.mLV2_ATOM__Long,
4115 hasPortProperty (LV2_CORE__enumeration) },
4121 const Plugin& plugin,
4123 SimpleSpan<ControlPort> controlPorts)
4127 const auto groupNode = world.newUri (LV2_PORT_GROUPS__group);
4129 for (
auto& port : controlPorts)
4131 if (port.header.direction != Port::Direction::input)
4137 const auto lilvPort = plugin.getPortByIndex (port.header.index);
4138 const auto group = lilvNodeToUriString (lilvPort.get (groupNode.get()).get());
4140 result.
push_back ({ port.info, ParameterWriter { &port }, group, port.header.name });
4146static void writeFloatToForge (LV2_Atom_Forge* forge,
float value) { lv2_atom_forge_float (forge, value); }
4147static void writeDoubleToForge (LV2_Atom_Forge* forge,
float value) { lv2_atom_forge_double (forge, (
double) value); }
4148static void writeIntToForge (LV2_Atom_Forge* forge,
float value) { lv2_atom_forge_int (forge, (int32_t) value); }
4149static void writeLongToForge (LV2_Atom_Forge* forge,
float value) { lv2_atom_forge_long (forge, (int64_t) value); }
4150static void writeBoolToForge (LV2_Atom_Forge* forge,
float value) { lv2_atom_forge_bool (forge, value > 0.5f); }
4153 const Plugin& plugin,
4154 const UsefulUrids& urids,
4155 uint32_t controlPortIndex)
4161 const auto writableControls = world.findNodes (plugin.getUri().get(),
4162 world.newUri (LV2_PATCH__writable).get(),
4173 const auto groupNode = world.newUri (LV2_PORT_GROUPS__group);
4175 for (
auto* ctrl : writableControls)
4177 const auto labelString = [&]
4179 if (
auto label = world.get (ctrl, world.newUri (LILV_NS_RDFS
"label").get(),
nullptr))
4180 return String::fromUTF8 (lilv_node_as_string (label.get()));
4185 const auto uri = String::fromUTF8 (lilv_node_as_uri (ctrl));
4186 const auto info = getInfoForPatchParameter (world, urids, world.newUri (uri.toRawUTF8()));
4188 if (! info.supported)
4191 const auto write = [&]
4193 if (info.type == urids.mLV2_ATOM__Int)
4194 return writeIntToForge;
4196 if (info.type == urids.mLV2_ATOM__Long)
4197 return writeLongToForge;
4199 if (info.type == urids.mLV2_ATOM__Double)
4200 return writeDoubleToForge;
4202 if (info.type == urids.mLV2_ATOM__Bool)
4203 return writeBoolToForge;
4205 return writeFloatToForge;
4208 const auto group = lilvNodeToUriString (world.get (ctrl, groupNode.get(),
nullptr).get());
4209 resultWithUris.
push_back ({ { info.info,
4210 ParameterWriter {
write, urids.symap.map (uri.toRawUTF8()), controlPortIndex },
4216 const auto compareUris = [] (
const DataAndUri& a,
const DataAndUri& b) {
return a.uri < b.uri; };
4221 for (
const auto& item : resultWithUris)
4222 result.push_back (item.
data);
4228 const Plugin& plugin,
4229 const UsefulUrids& urids,
4231 SimpleSpan<ControlPort> controlPorts,
4232 uint32_t controlPortIndex)
4234 auto port = getPortBasedParameters (world, plugin, hiddenPorts, controlPorts);
4235 auto patch = getPatchBasedParameters (world, plugin, urids, controlPortIndex);
4237 port.insert (port.end(), patch.begin(), patch.end());
4245#if JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD
4246 constexpr auto editorFunctionalityEnabled =
true;
4248 constexpr auto editorFunctionalityEnabled =
false;
4251template <
bool editorEnabled = editorFunctionalityEnabled>
class OptionalEditor;
4254class OptionalEditor<true>
4257 OptionalEditor (String uiBundleUriIn, UiDescriptor uiDescriptorIn,
std::function<
void()> timerCallback)
4258 : uiBundleUri (
std::
move (uiBundleUriIn)),
4259 uiDescriptor (
std::
move (uiDescriptorIn)),
4260 changedParameterFlusher (
std::
move (timerCallback)) {}
4264 if (
auto* editor = editorPointer.getComponent())
4265 editor->createView();
4270 if (
auto* editor = editorPointer.getComponent())
4271 editor->destroyView();
4275 AudioPluginInstance& p,
4276 InstanceProvider& instanceProviderIn,
4277 TouchListener& touchListenerIn,
4278 EditorListener& listenerIn)
4283 const auto queryFeatures = [
this, &world] (
const char* kind)
4285 return world.findNodes (world.newUri (uiDescriptor.get()->URI).get(),
4286 world.newUri (kind).get(),
4290 auto newEditor = std::make_unique<Editor> (world,
4297 RequiredFeatures { queryFeatures (LV2_CORE__requiredFeature) },
4298 OptionalFeatures { queryFeatures (LV2_CORE__optionalFeature) });
4300 editorPointer = newEditor.get();
4302 changedParameterFlusher.startTimerHz (60);
4307 bool hasEditor()
const
4309 return uiDescriptor.get() !=
nullptr;
4312 void prepareToDestroyEditor()
4314 changedParameterFlusher.stopTimer();
4318 Component::SafePointer<Editor> editorPointer =
nullptr;
4320 UiDescriptor uiDescriptor;
4321 TimedCallback changedParameterFlusher;
4325class OptionalEditor<false>
4328 OptionalEditor (String, UiDescriptor,
std::function<
void()>) {}
4330 void createView() {}
4331 void destroyView() {}
4334 AudioPluginInstance&,
4342 bool hasEditor()
const {
return false; }
4343 void prepareToDestroyEditor() {}
4347class LV2AudioPluginInstance final :
public AudioPluginInstance,
4348 private TouchListener,
4349 private EditorListener,
4350 private InstanceProvider
4354 const Plugin& pluginIn,
4355 const UsefulUris& uris,
4357 PluginDescription&& desc,
4359 PluginState stateToApply,
4360 String uiBundleUriIn,
4361 UiDescriptor uiDescriptorIn)
4362 : LV2AudioPluginInstance (worldIn,
4370 getParsedBuses (*worldIn, pluginIn, uris)) {}
4372 void fillInPluginDescription (PluginDescription& d)
const override { d = description; }
4374 const String getName()
const override {
return description.name; }
4376 void prepareToPlay (
double sampleRate,
int numSamples)
override
4394 getStateInformation (mb);
4396 instance = std::make_unique<InstanceWithSupports> (*world,
4397 std::move (instance->symap),
4399 std::move (instance->ports),
4404 setStateInformationImpl (mb.getData(), (
int) mb.getSize(), ConcurrentWithAudioCallback::no);
4406 jassert (numSamples == instance->features.getMaxBlockSize());
4408 optionalEditor.createView();
4412 void releaseResources()
override { deactivate(); }
4414 using AudioPluginInstance::processBlock;
4415 using AudioPluginInstance::processBlockBypassed;
4417 void processBlock (AudioBuffer<float>& audio, MidiBuffer& midi)
override
4419 processBlockImpl (audio, midi);
4422 void processBlockBypassed (AudioBuffer<float>& audio, MidiBuffer& midi)
override
4424 if (bypassParam !=
nullptr)
4425 processBlockImpl (audio, midi);
4427 AudioPluginInstance::processBlockBypassed (audio, midi);
4430 double getTailLengthSeconds()
const override {
return {}; }
4432 bool acceptsMidi()
const override
4434 if (instance ==
nullptr)
4437 auto ports = instance->ports.getAtomPorts();
4439 return std::any_of (ports.begin(), ports.end(), [&] (
const AtomPort& a)
4441 if (a.header.direction != Port::Direction::input)
4444 return portAtIndexSupportsMidi (a.header.index);
4448 bool producesMidi()
const override
4450 if (instance ==
nullptr)
4453 auto ports = instance->ports.getAtomPorts();
4455 return std::any_of (ports.begin(), ports.end(), [&] (
const AtomPort& a)
4457 if (a.header.direction != Port::Direction::output)
4460 return portAtIndexSupportsMidi (a.header.index);
4464 AudioProcessorEditor* createEditor()
override
4466 return optionalEditor.createEditor (*world, *
this, *
this, *
this, *
this).release();
4469 bool hasEditor()
const override
4471 return optionalEditor.hasEditor();
4474 int getNumPrograms()
override {
return (
int) presetUris.size(); }
4476 int getCurrentProgram()
override
4478 return lastAppliedPreset;
4481 void setCurrentProgram (
int newProgram)
override
4485 if (! isPositiveAndBelow (newProgram, presetUris.size()))
4488 lastAppliedPreset = newProgram;
4489 applyStateWithAppropriateLocking (loadStateWithUri (presetUris[(
size_t) newProgram]),
4490 ConcurrentWithAudioCallback::yes);
4493 const String getProgramName (
int program)
override
4497 if (isPositiveAndBelow (program, presetUris.size()))
4498 return loadStateWithUri (presetUris[(
size_t) program]).getLabel();
4503 void changeProgramName (
int program,
const String& label)
override
4507 if (isPositiveAndBelow (program, presetUris.size()))
4508 loadStateWithUri (presetUris[(
size_t) program]).setLabel (label);
4511 void getStateInformation (MemoryBlock& block)
override
4516 PortMap portStateManager (instance->ports);
4517 const auto stateUri = String::fromUTF8 (instance->instance.getUri()) +
"/savedState";
4518 auto mapFeature = instance->symap->getMapFeature();
4519 auto unmapFeature = instance->symap->getUnmapFeature();
4520 const auto state = PluginState::SaveRestoreHandle (*instance, portStateManager).save (plugin.get(), &mapFeature);
4521 const auto string = state.toString (world->get(), &mapFeature, &unmapFeature, stateUri.toRawUTF8());
4522 block.replaceAll (
string.
data(),
string.
size());
4525 void setStateInformation (
const void* data,
int size)
override
4527 setStateInformationImpl (data, size, ConcurrentWithAudioCallback::yes);
4530 void setNonRealtime (
bool newValue)
noexcept override
4534 AudioPluginInstance::setNonRealtime (newValue);
4535 instance->features.setNonRealtime (newValue);
4538 bool isBusesLayoutSupported (
const BusesLayout& layout)
const override
4540 for (
const auto& pair : {
std::make_tuple (&layout.inputBuses, &declaredBusLayout.inputs),
4543 const auto& requested = *std::get<0> (pair);
4544 const auto& allowed = *std::get<1> (pair);
4546 if ((
size_t) requested.size() != allowed.size())
4549 for (
size_t busIndex = 0; busIndex < allowed.size(); ++busIndex)
4551 const auto& requestedBus = requested[(
int) busIndex];
4552 const auto& allowedBus = allowed[busIndex];
4554 if (! allowedBus.isCompatible (requestedBus))
4562 void processorLayoutsChanged()
override { ioMap = lv2_shared::PortToAudioBufferMap { getBusesLayout(), declaredBusLayout }; }
4564 AudioProcessorParameter* getBypassParameter()
const override {
return bypassParam; }
4567 enum class ConcurrentWithAudioCallback { no, yes };
4570 const Plugin& pluginIn,
4572 PluginDescription&& desc,
4574 PluginState stateToApply,
4575 String uiBundleUriIn,
4576 UiDescriptor uiDescriptorIn,
4577 const lv2_shared::ParsedBuses& parsedBuses)
4578 : AudioPluginInstance (getBusesProperties (parsedBuses, *worldIn)),
4579 declaredBusLayout (parsedBuses),
4582 description (
std::
move (desc)),
4583 presetUris (
std::
move (knownPresetUris)),
4585 optionalEditor (
std::
move (uiBundleUriIn),
4587 [this] { postChangedParametersToUi(); })
4589 applyStateWithAppropriateLocking (std::move (stateToApply), ConcurrentWithAudioCallback::no);
4592 void setStateInformationImpl (
const void* data,
int size, ConcurrentWithAudioCallback concurrent)
4596 if (data ==
nullptr || size == 0)
4599 auto begin =
static_cast<const char*
> (
data);
4602 auto mapFeature = instance->symap->getMapFeature();
4603 applyStateWithAppropriateLocking (PluginState { lilv_state_new_from_string (world->get(), &mapFeature,
copy.data()) },
4614 optionalEditor.destroyView();
4620 instance->instance.activate();
4628 instance->instance.deactivate();
4633 void processBlockImpl (AudioBuffer<float>& audio, MidiBuffer& midi)
4635 preparePortsForRun (audio, midi);
4637 instance->instance.run (
static_cast<uint32_t> (audio.getNumSamples()));
4638 instance->features.processResponses();
4640 processPortsAfterRun (midi);
4643 bool portAtIndexSupportsMidi (uint32_t index)
const noexcept
4645 const auto port = plugin.getPortByIndex (index);
4647 if (! port.isValid())
4650 return port.supportsEvent (world->newUri (LV2_MIDI__MidiEvent).get());
4653 void controlGrabbed (uint32_t port,
bool grabbed)
override
4655 if (
auto* param = parameterValues.getParamByPortIndex (port))
4658 param->beginChangeGesture();
4660 param->endChangeGesture();
4664 void viewCreated (UiEventListener* newListener)
override
4666 uiEventListener = newListener;
4667 postAllParametersToUi();
4670 ParameterWriterUrids getParameterWriterUrids()
const
4672 return { instance->urids.mLV2_PATCH__Set,
4673 instance->urids.mLV2_PATCH__property,
4674 instance->urids.mLV2_PATCH__value,
4675 instance->urids.mLV2_ATOM__eventTransfer };
4678 void postAllParametersToUi()
4680 parameterValues.postAllParametersToUi (uiEventListener, getParameterWriterUrids(), *instance->processorToUi);
4681 controlPortStructure.writeOutputPorts (uiEventListener, *instance->processorToUi);
4684 void postChangedParametersToUi()
4686 parameterValues.postChangedParametersToUi (uiEventListener, getParameterWriterUrids(), *instance->processorToUi);
4687 controlPortStructure.writeOutputPorts (uiEventListener, *instance->processorToUi);
4690 void notifyEditorBeingDeleted()
override
4692 optionalEditor.prepareToDestroyEditor();
4693 uiEventListener =
nullptr;
4694 editorBeingDeleted (getActiveEditor());
4697 InstanceWithSupports* getInstanceWithSupports()
const override
4699 return instance.get();
4702 void applyStateWithAppropriateLocking (PluginState&& state, ConcurrentWithAudioCallback concurrent)
4704 PortMap portStateManager (instance->ports);
4708 if (hasThreadSafeRestore || concurrent == ConcurrentWithAudioCallback::no)
4710 state.restore (*instance, portStateManager);
4715 state.restore (*instance, portStateManager);
4718 parameterValues.updateFromControlPorts (controlPortStructure);
4719 asyncFullUiParameterUpdate.triggerAsyncUpdate();
4722 PluginState loadStateWithUri (
const String& str)
4724 auto mapFeature = instance->symap->getMapFeature();
4725 const auto presetUri = world->newUri (str.toRawUTF8());
4726 lilv_world_load_resource (world->get(), presetUri.get());
4727 return PluginState { lilv_state_new_from_world (world->get(), &mapFeature, presetUri.get()) };
4730 void connectPorts (AudioBuffer<float>& audio)
4735 for (
const auto& port : instance->ports.getAudioPorts())
4737 const auto channel = ioMap.getChannelForPort (port.header.index);
4738 auto* ptr =
isPositiveAndBelow (channel, audio.getNumChannels()) ? audio.getWritePointer (channel)
4740 instance->instance.connectPort (port.header.index, ptr);
4743 for (
const auto& port : instance->ports.getCvPorts())
4744 instance->instance.connectPort (port.header.index, nullptr);
4746 for (
auto& port : instance->ports.getAtomPorts())
4747 instance->instance.connectPort (port.header.index, port.
data());
4750 void writeTimeInfoToPort (AtomPort& port)
4752 if (port.header.direction != Port::Direction::input || ! port.getSupportsTime())
4755 auto* forge = port.getForge().get();
4756 auto* playhead = getPlayHead();
4758 if (playhead ==
nullptr)
4762 const auto info = playhead->getPosition();
4764 if (! info.hasValue())
4767 const auto& urids = instance->urids;
4769 lv2_atom_forge_frame_time (forge, 0);
4771 lv2_shared::ObjectFrame
object { forge, (
uint32_t) 0, urids.mLV2_TIME__Position };
4773 lv2_atom_forge_key (forge, urids.mLV2_TIME__speed);
4774 lv2_atom_forge_float (forge, info->getIsPlaying() ? 1.0f : 0.0f);
4776 if (
const auto samples = info->getTimeInSamples())
4778 lv2_atom_forge_key (forge, urids.mLV2_TIME__frame);
4779 lv2_atom_forge_long (forge, *samples);
4782 if (
const auto bar = info->getBarCount())
4784 lv2_atom_forge_key (forge, urids.mLV2_TIME__bar);
4785 lv2_atom_forge_long (forge, *bar);
4788 if (
const auto beat = info->getPpqPosition())
4790 if (
const auto barStart = info->getPpqPositionOfLastBarStart())
4792 lv2_atom_forge_key (forge, urids.mLV2_TIME__barBeat);
4793 lv2_atom_forge_float (forge, (
float) (*beat - *barStart));
4796 lv2_atom_forge_key (forge, urids.mLV2_TIME__beat);
4797 lv2_atom_forge_double (forge, *beat);
4800 if (
const auto sig = info->getTimeSignature())
4802 lv2_atom_forge_key (forge, urids.mLV2_TIME__beatUnit);
4803 lv2_atom_forge_int (forge, sig->denominator);
4805 lv2_atom_forge_key (forge, urids.mLV2_TIME__beatsPerBar);
4806 lv2_atom_forge_float (forge, (
float) sig->numerator);
4809 if (
const auto bpm = info->getBpm())
4811 lv2_atom_forge_key (forge, urids.mLV2_TIME__beatsPerMinute);
4812 lv2_atom_forge_float (forge, (
float) *bpm);
4816 void preparePortsForRun (AudioBuffer<float>& audio, MidiBuffer& midiBuffer)
4818 connectPorts (audio);
4820 for (
auto& port : instance->ports.getAtomPorts())
4822 switch (port.header.direction)
4824 case Port::Direction::input:
4825 port.beginSequence();
4828 case Port::Direction::output:
4829 port.replaceWithChunk();
4832 case Port::Direction::unknown:
4838 for (
auto& port : instance->ports.getAtomPorts())
4839 writeTimeInfoToPort (port);
4841 const auto controlPortForge = controlPort !=
nullptr ? controlPort->getForge().get()
4844 parameterValues.postChangedParametersToProcessor (getParameterWriterUrids(), controlPortForge);
4846 instance->uiToProcessor.readAllAndClear ([
this] (MessageHeader header, uint32_t size,
const void* buffer)
4848 pushMessage (header, size, buffer);
4851 for (
auto& port : instance->ports.getAtomPorts())
4853 if (port.header.direction == Port::Direction::input)
4855 for (
const auto meta : midiBuffer)
4857 port.addEventToSequence (meta.samplePosition,
4858 instance->urids.mLV2_MIDI__MidiEvent,
4859 static_cast<uint32_t> (meta.numBytes),
4867 if (freeWheelingPort !=
nullptr)
4868 freeWheelingPort->currentValue = isNonRealtime() ? freeWheelingPort->info.max
4869 : freeWheelingPort->info.min;
4872 void pushMessage (MessageHeader header, [[maybe_unused]] uint32_t size,
const void* data)
4874 if (header.protocol == 0 || header.protocol == instance->urids.mLV2_UI__floatProtocol)
4876 const auto value = readUnaligned<float> (data);
4878 if (
auto* param = parameterValues.getParamByPortIndex (header.portIndex))
4880 param->setDenormalisedValue (value);
4882 else if (
auto* port = controlPortStructure.getControlPortByIndex (header.portIndex))
4885 port->currentValue = value;
4888 else if (
auto* atomPort = header.portIndex < atomPorts.size() ? atomPorts[header.portIndex] : nullptr)
4890 if (header.protocol == instance->urids.mLV2_ATOM__eventTransfer)
4892 if (
const auto* atom = convertToAtomPtr (data, (
size_t) size))
4894 atomPort->addAtomToSequence (0, atom);
4897 if (atom->type == instance->urids.mLV2_ATOM__Object)
4898 patchSetHelper.processPatchSet (
reinterpret_cast<const LV2_Atom_Object*
> (data), PatchSetCallback { parameterValues });
4901 else if (header.protocol == instance->urids.mLV2_ATOM__atomTransfer)
4903 if (
const auto* atom = convertToAtomPtr (data, (
size_t) size))
4904 atomPort->replaceBufferWithAtom (atom);
4909 void processPortsAfterRun (MidiBuffer& midi)
4913 for (
auto& port : instance->ports.getAtomPorts())
4914 processAtomPort (port, midi);
4916 if (latencyPort !=
nullptr)
4917 setLatencySamples ((
int) latencyPort->currentValue);
4920 void processAtomPort (
const AtomPort& port, MidiBuffer& midi)
4922 if (port.header.direction != Port::Direction::output)
4926 const auto* atom =
reinterpret_cast<const LV2_Atom*
> (port.data());
4928 if (atom->type != instance->urids.mLV2_ATOM__Sequence)
4932 const auto* sequence =
reinterpret_cast<const LV2_Atom_Sequence*
> (port.data());
4935 jassert (sequence->body.unit == 0 || sequence->body.unit == instance->urids.mLV2_UNITS__frame);
4937 for (
const auto* event : lv2_shared::SequenceIterator { lv2_shared::SequenceWithSize { sequence } })
4940 instance->processorToUi->pushMessage ({ uiEventListener, { port.header.index, instance->urids.mLV2_ATOM__eventTransfer } },
4941 (
uint32_t) (event->body.size + sizeof (LV2_Atom)),
4944 if (event->body.type == instance->urids.mLV2_MIDI__MidiEvent)
4945 midi.addEvent (event + 1,
static_cast<int> (event->body.size),
static_cast<int> (
event->time.frames));
4947 if (lv2_atom_forge_is_object_type (port.getForge().get(),
event->body.type))
4948 if (
reinterpret_cast<const LV2_Atom_Object_Body*
> (event + 1)->otype == instance->urids.mLV2_STATE__StateChanged)
4949 updateHostDisplay (ChangeDetails{}.withNonParameterStateChanged (
true));
4951 patchSetHelper.processPatchSet (event, PatchSetCallback { parameterValues });
4959 const auto channelSet = lv2_shared::ParsedGroup::getEquivalentSet (info);
4961 if ((
int) info.
size() == channelSet.size())
4965 auto designation = (
int) AudioChannelSet::discreteChannel0;
4967 for (
auto& item : info)
4970 copy.designation = (AudioChannelSet::ChannelType) designation++;
4977 static AudioChannelSet::ChannelType getPortDesignation (World& world,
const Port& port,
size_t indexInGroup)
4979 const auto defaultResult = (AudioChannelSet::ChannelType) (AudioChannelSet::discreteChannel0 + indexInGroup);
4980 const auto node = port.get (world.newUri (LV2_CORE__designation).get());
4982 if (node ==
nullptr)
4983 return defaultResult;
4985 const auto it = lv2_shared::channelDesignationMap.find (lilvNodeToUriString (node.get()));
4987 if (it == lv2_shared::channelDesignationMap.
end())
4988 return defaultResult;
4993 static lv2_shared::ParsedBuses getParsedBuses (World& world,
const Plugin& p,
const UsefulUris& uris)
4995 const auto groupPropertyUri = world.newUri (LV2_PORT_GROUPS__group);
4996 const auto optionalUri = world.newUri (LV2_CORE__connectionOptional);
5001 for (uint32_t i = 0, numPorts = p.getNumPorts(); i < numPorts; ++i)
5003 const auto port = p.getPortByIndex (i);
5005 if (port.getKind (uris) != Port::Kind::audio)
5008 const auto groupUri = lilvNodeToUriString (port.get (groupPropertyUri.get()).get());
5010 auto& set = [&]() ->
auto&
5012 if (groupUri.isEmpty())
5013 return port.getDirection (uris) == Port::Direction::input ? ungroupedInputs : ungroupedOutputs;
5015 auto& group = port.getDirection (uris) == Port::Direction::input ? inputGroups : outputGroups;
5016 return group[groupUri];
5019 set.insert ({ port.getIndex(), getPortDesignation (world, port, set.size()), port.hasProperty (optionalUri) });
5022 for (
auto* groups : { &inputGroups, &outputGroups })
5023 for (
auto& pair : *groups)
5024 pair.second = validateAndRedesignatePorts (
std::
move (pair.second));
5026 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4702)
5027 const auto getMainGroupName = [&] (const
char* propertyName)
5029 for (
const auto* item : p.getValue (world.newUri (propertyName).
get()))
5030 return lilvNodeToUriString (item);
5034 JUCE_END_IGNORE_WARNINGS_MSVC
5036 return { findStableBusOrder (getMainGroupName (LV2_PORT_GROUPS__mainInput), inputGroups, ungroupedInputs),
5037 findStableBusOrder (getMainGroupName (LV2_PORT_GROUPS__mainOutput), outputGroups, ungroupedOutputs) };
5040 static String getNameForUri (World& world, StringRef uri)
5045 const auto node = world.get (world.newUri (uri).get(),
5046 world.newUri (LV2_CORE__name).get(),
5049 if (node ==
nullptr)
5052 return String::fromUTF8 (lilv_node_as_string (node.get()));
5055 static BusesProperties getBusesProperties (
const lv2_shared::ParsedBuses& parsedBuses, World& world)
5057 BusesProperties result;
5059 for (
const auto& pair : {
std::make_tuple (&parsedBuses.inputs, &result.inputLayouts),
5062 const auto& buses = *std::get<0> (pair);
5063 auto& layout = *std::get<1> (pair);
5065 for (
const auto& bus : buses)
5067 layout.add (AudioProcessor::BusProperties { getNameForUri (world, bus.uid),
5068 bus.getEquivalentSet(),
5069 bus.isRequired() });
5076 LV2_URID map (
const char* str)
const
5078 return instance !=
nullptr ? instance->symap->map (str)
5082 ControlPort* findControlPortWithIndex (uint32_t index)
const
5084 auto ports = instance->ports.getControlPorts();
5085 const auto indexMatches = [&] (
const ControlPort& p) {
return p.header.index == index; };
5086 const auto it =
std::find_if (ports.begin(), ports.end(), indexMatches);
5088 return it != ports.end() ? &(*it) :
nullptr;
5091 const lv2_shared::ParsedBuses declaredBusLayout;
5092 lv2_shared::PortToAudioBufferMap ioMap { getBusesLayout(), declaredBusLayout };
5095 PluginDescription description;
5098 AsyncFn asyncFullUiParameterUpdate { [
this] { postAllParametersToUi(); } };
5102 AtomPort*
const controlPort = [&]() -> AtomPort*
5104 const auto port = plugin.getPortByDesignation (world->newUri (LV2_CORE__InputPort).
get(),
5105 world->newUri (LV2_CORE__control).
get());
5107 if (! port.isValid())
5110 const auto index = port.getIndex();
5112 if (! isPositiveAndBelow (index, atomPorts.
size()))
5115 return atomPorts[index];
5118 ControlPort*
const latencyPort = [&]() -> ControlPort*
5120 if (! plugin.hasLatency())
5123 return findControlPortWithIndex (plugin.getLatencyPortIndex());
5126 ControlPort*
const freeWheelingPort = [&]() -> ControlPort*
5128 const auto port = plugin.getPortByDesignation (world->newUri (LV2_CORE__InputPort).
get(),
5129 world->newUri (LV2_CORE__freeWheeling).
get());
5131 if (! port.isValid())
5134 return findControlPortWithIndex (port.getIndex());
5137 ControlPort*
const enabledPort = [&]() -> ControlPort*
5139 const auto port = plugin.getPortByDesignation (world->newUri (LV2_CORE__InputPort).
get(),
5140 world->newUri (LV2_CORE_PREFIX
"enabled").
get());
5142 if (! port.isValid())
5145 return findControlPortWithIndex (port.getIndex());
5148 lv2_shared::PatchSetHelper patchSetHelper { instance->symap->getMapFeature(), plugin.getUri().getTyped() };
5149 ControlPortAccelerationStructure controlPortStructure { instance->ports.getControlPorts() };
5150 ParameterValueCache parameterValues { *
this,
5152 instance->symap->getMapFeature(),
5153 getJuceParameterInfo (*world,
5156 { latencyPort, freeWheelingPort },
5157 instance->ports.getControlPorts(),
5158 controlPort !=
nullptr ? controlPort->header.index : 0),
5160 LV2Parameter* bypassParam = enabledPort !=
nullptr ? parameterValues.getParamByPortIndex (enabledPort->header.index)
5164 OptionalEditor<> optionalEditor;
5165 int lastAppliedPreset = 0;
5166 bool hasThreadSafeRestore = plugin.hasExtensionData (world->newUri (LV2_STATE__threadSafeRestore));
5167 bool active {
false };
5175class LV2PluginFormat::Pimpl
5180 loadAllPluginsFromPaths (getDefaultLocationsToSearch());
5182 const auto tempFile = lv2ResourceFolder.getFile();
5184 if (tempFile.createDirectory())
5186 for (
const auto& bundle : lv2::Bundle::getAllBundles())
5188 const auto pathToBundle = tempFile.getChildFile (bundle.name + String (
".lv2"));
5190 if (! pathToBundle.createDirectory())
5193 for (
const auto& resource : bundle.contents)
5194 pathToBundle.getChildFile (resource.name).replaceWithText (resource.contents);
5196 const auto pathString = File::addTrailingSeparator (pathToBundle.getFullPathName());
5197 world->loadBundle (world->newFileUri (
nullptr, pathString.toRawUTF8()));
5204 lv2ResourceFolder.getFile().deleteRecursively();
5207 void findAllTypesForFile (OwnedArray<PluginDescription>& result,
5208 const String& identifier)
5210 if (File::isAbsolutePath (identifier))
5211 world->loadBundle (world->newFileUri (
nullptr, File::addTrailingSeparator (identifier).toRawUTF8()));
5213 for (
const auto& plugin : { findPluginByUri (identifier), findPluginByFile (identifier) })
5215 if (
auto desc = getDescription (plugin); desc.fileOrIdentifier.isNotEmpty())
5217 result.add (std::make_unique<PluginDescription> (desc));
5223 bool fileMightContainThisPluginType (
const String& file)
const
5226 const auto*
data = file.toRawUTF8();
5227 const auto numBytes = file.getNumBytesAsUTF8();
5229 std::copy (data, data + numBytes, vec.begin());
5230 return serd_uri_string_has_scheme (vec.data()) || file.endsWith (
".lv2");
5233 String getNameOfPluginFromIdentifier (
const String& identifier)
5240 bool pluginNeedsRescanning (
const PluginDescription&)
5245 bool doesPluginStillExist (
const PluginDescription& description)
5247 return findPluginByUri (description.fileOrIdentifier) !=
nullptr;
5250 StringArray searchPathsForPlugins (
const FileSearchPath& paths,
bool,
bool)
5252 loadAllPluginsFromPaths (paths);
5256 for (
const auto* plugin : world->getAllPlugins())
5257 result.add (lv2_host::Plugin { plugin }.getUri().getTyped());
5262 FileSearchPath getDefaultLocationsToSearch()
5265 return {
"~/Library/Audio/Plug-Ins/LV2;"
5267 "/usr/local/lib/lv2;"
5269 "/Library/Audio/Plug-Ins/LV2;" };
5271 return {
"%APPDATA%\\LV2;"
5272 "%COMMONPROGRAMFILES%\\LV2" };
5275 if (File (
"/usr/lib64/lv2").
exists() || File (
"/usr/local/lib64/lv2").
exists())
5278 "/usr/local/lib64/lv2" };
5283 "/usr/local/lib/lv2" };
5287 const LilvUI* findEmbeddableUi (
const lv2_host::Uis* pluginUis,
std::true_type)
5289 if (pluginUis ==
nullptr)
5297 constexpr const char* rawUri =
5302 #elif JUCE_LINUX || JUCE_BSD
5309 const auto nativeUiUri = world->newUri (rawUri);
5311 struct UiWithSuitability
5314 unsigned suitability;
5316 bool operator< (
const UiWithSuitability& other)
const noexcept
5318 return suitability < other.suitability;
5321 static unsigned uiIsSupported (
const char* hostUri,
const char* pluginUri)
5323 if (strcmp (hostUri, pluginUri) == 0)
5331 uisWithSuitability.
reserve (allUis.size());
5335 const LilvNode* type = nullptr;
5336 return UiWithSuitability { ui, lilv_ui_is_supported (ui, UiWithSuitability::uiIsSupported, nativeUiUri.get(), &type) };
5341 if (uisWithSuitability.
back().suitability != 0)
5342 return uisWithSuitability.
back().ui;
5347 const LilvUI* findEmbeddableUi (
const lv2_host::Uis*,
std::false_type)
5352 const LilvUI* findEmbeddableUi (
const lv2_host::Uis* pluginUis)
5357 static lv2_host::UiDescriptor getUiDescriptor (
const LilvUI* ui)
5362 const auto libraryFile = StringPtr { lilv_file_uri_parse (lilv_node_as_uri (lilv_ui_get_binary_uri (ui)),
nullptr) };
5364 return lv2_host::UiDescriptor { lv2_host::UiDescriptorArgs{}.withLibraryPath (libraryFile.get())
5365 .withUiUri (lilv_node_as_uri (lilv_ui_get_uri (ui))) };
5369 template <
typename RequiredFeatures,
typename AvailableFeatures>
5371 AvailableFeatures&& available)
5375 for (
const auto* node : required)
5377 const auto nodeString = String::fromUTF8 (lilv_node_as_uri (node));
5386 void createPluginInstance (
const PluginDescription& desc,
5387 double initialSampleRate,
5388 int initialBufferSize,
5389 PluginCreationCallback callback)
5391 const auto* pluginPtr = findPluginByUri (desc.fileOrIdentifier);
5393 if (pluginPtr ==
nullptr)
5394 return callback (
nullptr,
"Unable to locate plugin with the requested URI");
5396 const lv2_host::Plugin plugin { pluginPtr };
5398 auto symap = std::make_unique<lv2_host::SymbolMap>();
5400 const auto missingFeatures = findMissingFeatures (plugin.getRequiredFeatures(),
5401 lv2_host::FeaturesData::getFeatureUris());
5403 if (! missingFeatures.empty())
5405 const auto missingFeaturesString = StringArray (missingFeatures.data(), (
int) missingFeatures.size()).joinIntoString (
", ");
5407 return callback (
nullptr,
"plugin requires missing features: " + missingFeaturesString);
5410 auto stateToApply = [&]
5412 if (! plugin.hasFeature (world->newUri (LV2_STATE__loadDefaultState)))
5413 return lv2_host::PluginState{};
5415 auto map = symap->getMapFeature();
5416 return lv2_host::PluginState { lilv_state_new_from_world (world->
get(), &map, plugin.getUri().get()) };
5419 auto ports = lv2_host::Ports::getPorts (*world, uris, plugin, *symap);
5421 if (! ports.hasValue())
5422 return callback (
nullptr,
"Plugin has ports of an unsupported type");
5424 auto instance = std::make_unique<lv2_host::InstanceWithSupports> (*world,
5428 (int32_t) initialBufferSize,
5431 if (instance->instance ==
nullptr)
5432 return callback (
nullptr,
"Plugin was located, but could not be opened");
5434 auto potentialPresets = world->findNodes (
nullptr,
5435 world->newUri (LV2_CORE__appliesTo).
get(),
5436 plugin.getUri().get());
5438 const lv2_host::Uis pluginUis { plugin.get() };
5440 const auto uiToUse = [&]() ->
const LilvUI*
5442 const auto bestMatch = findEmbeddableUi (&pluginUis);
5444 if (bestMatch ==
nullptr)
5447 const auto uiUri = lilv_ui_get_uri (bestMatch);
5448 lilv_world_load_resource (world->
get(), uiUri);
5450 const auto queryUi = [&] (
const char* featureUri)
5452 const auto featureUriNode = world->newUri (featureUri);
5453 return world->findNodes (uiUri, featureUriNode.get(),
nullptr);
5456 const auto missingUiFeatures = findMissingFeatures (queryUi (LV2_CORE__requiredFeature),
5457 lv2_host::UiFeaturesData::getFeatureUris());
5459 return missingUiFeatures.empty() ? bestMatch :
nullptr;
5462 auto uiBundleUri = uiToUse !=
nullptr ? String::fromUTF8 (lilv_node_as_uri (lilv_ui_get_bundle_uri (uiToUse)))
5465 auto wrapped = std::make_unique<lv2_host::LV2AudioPluginInstance> (world,
5468 std::move (instance),
5469 getDescription (pluginPtr),
5470 findPresetUrisForPlugin (plugin.get()),
5471 std::move (stateToApply),
5472 std::move (uiBundleUri),
5473 getUiDescriptor (uiToUse));
5474 callback (std::move (wrapped), {});
5478 void loadAllPluginsFromPaths (
const FileSearchPath& path)
5480 const auto joined = path.toStringWithSeparator (LILV_PATH_SEP);
5481 world->loadAllFromPaths (world->newString (joined.toRawUTF8()));
5484 struct Free {
void operator() (
char* ptr)
const noexcept {
free (ptr); } };
5487 const LilvPlugin* findPluginByUri (
const String& s)
5489 return world->getAllPlugins().getByUri (world->newUri (s.toRawUTF8()));
5492 const LilvPlugin* findPluginByFile (
const File& f)
5494 return world->getAllPlugins().getByFile (f);
5497 template <
typename Fn>
5498 void visitParentClasses (
const LilvPluginClass* c,
Fn&& fn)
const
5503 const lv2_host::PluginClass wrapped { c };
5506 const auto parentUri = wrapped.getParentUri();
5508 if (parentUri.get() !=
nullptr)
5509 visitParentClasses (world->getPluginClasses().getByUri (parentUri), fn);
5516 visitParentClasses (c, [&results] (
const lv2_host::PluginClass& wrapped)
5524 PluginDescription getDescription (
const LilvPlugin* plugin)
5526 if (plugin ==
nullptr)
5529 const auto wrapped = lv2_host::Plugin { plugin };
5530 const auto bundle = wrapped.getBundleUri().getTyped();
5531 const auto bundleFile = File { StringPtr { lilv_file_uri_parse (bundle,
nullptr) }.get() };
5533 const auto numInputs = wrapped.getNumPortsOfClass (uris.mLV2_CORE__AudioPort, uris.mLV2_CORE__InputPort);
5534 const auto numOutputs = wrapped.getNumPortsOfClass (uris.mLV2_CORE__AudioPort, uris.mLV2_CORE__OutputPort);
5536 PluginDescription result;
5537 result.name = wrapped.getName().getTyped();
5538 result.descriptiveName = wrapped.getName().getTyped();
5539 result.lastFileModTime = bundleFile.getLastModificationTime();
5540 result.lastInfoUpdateTime = Time::getCurrentTime();
5541 result.manufacturerName = wrapped.getAuthorName().getTyped();
5542 result.pluginFormatName = LV2PluginFormat::getFormatName();
5543 result.numInputChannels =
static_cast<int> (numInputs);
5544 result.numOutputChannels =
static_cast<int> (numOutputs);
5546 const auto classPtr = wrapped.getClass();
5547 const auto classes = collectPluginClassUris (classPtr);
5548 const auto isInstrument =
std::any_of (classes.cbegin(),
5550 [
this] (
const lv2_host::NodeUri& uri)
5552 return uri.equals (uris.mLV2_CORE__GeneratorPlugin);
5555 result.category = lv2_host::PluginClass { classPtr }.getLabel().getTyped();
5556 result.isInstrument = isInstrument;
5559 result.fileOrIdentifier = wrapped.getUri().getTyped();
5562 result.deprecatedUid = result.uniqueId = uid;
5570 lv2_host::OwningNodes potentialPresets { lilv_plugin_get_related (plugin, world->newUri (LV2_PRESETS__Preset).
get()) };
5572 for (
const auto* potentialPreset : potentialPresets)
5573 presetUris.push_back (lilv_node_as_string (potentialPreset));
5578 TemporaryFile lv2ResourceFolder;
5580 lv2_host::UsefulUris uris { world->
get() };
5584LV2PluginFormat::LV2PluginFormat()
5587LV2PluginFormat::~LV2PluginFormat() =
default;
5589void LV2PluginFormat::findAllTypesForFile (OwnedArray<PluginDescription>& results,
5590 const String& fileOrIdentifier)
5592 pimpl->findAllTypesForFile (results, fileOrIdentifier);
5595bool LV2PluginFormat::fileMightContainThisPluginType (
const String& fileOrIdentifier)
5597 return pimpl->fileMightContainThisPluginType (fileOrIdentifier);
5600String LV2PluginFormat::getNameOfPluginFromIdentifier (
const String& fileOrIdentifier)
5602 return pimpl->getNameOfPluginFromIdentifier (fileOrIdentifier);
5605bool LV2PluginFormat::pluginNeedsRescanning (
const PluginDescription& desc)
5607 return pimpl->pluginNeedsRescanning (desc);
5610bool LV2PluginFormat::doesPluginStillExist (
const PluginDescription& desc)
5612 return pimpl->doesPluginStillExist (desc);
5615bool LV2PluginFormat::canScanForPlugins()
const {
return true; }
5616bool LV2PluginFormat::isTrivialToScan()
const {
return true; }
5618StringArray LV2PluginFormat::searchPathsForPlugins (
const FileSearchPath& directoriesToSearch,
5622 return pimpl->searchPathsForPlugins (directoriesToSearch, recursive, allowAsync);
5625FileSearchPath LV2PluginFormat::getDefaultLocationsToSearch()
5627 return pimpl->getDefaultLocationsToSearch();
5630bool LV2PluginFormat::requiresUnblockedMessageThreadDuringCreation (
const PluginDescription&)
const
5635void LV2PluginFormat::createPluginInstance (
const PluginDescription& desc,
5638 PluginCreationCallback callback)
5640 pimpl->createPluginInstance (desc, sampleRate, bufferSize, std::move (callback));
T back_inserter(T... args)
T emplace_back(T... args)
#define JUCE_LEAK_DETECTOR(OwnerClass)
This macro lets you embed a leak-detecting object inside a class.
#define TRANS(stringLiteral)
Uses the LocalisedStrings class to translate the given string literal.
#define JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
This macro is used to catch unsafe use of functions which expect to only be called on the message thr...
#define JUCE_ASSERT_MESSAGE_THREAD
This macro is used to catch unsafe use of functions which expect to only be called on the message thr...
auto & get(ProcessorChain< Processors... > &chain) noexcept
Non-member equivalent of ProcessorChain::get which avoids awkward member template syntax.
@ copy
The command ID that should be used to send a "Copy to clipboard" command.
CriticalSection::ScopedLockType ScopedLock
Automatically locks and unlocks a CriticalSection object.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
RangedDirectoryIterator end(const RangedDirectoryIterator &)
Returns a default-constructed sentinel value.
void ignoreUnused(Types &&...) noexcept
Handy function for avoiding unused variables warning.
Object withMember(Object copy, Member OtherObject::*member, Other &&value)
Copies an object, sets one of the copy's members to the specified value, and then returns the copy.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Returns true if a value is at least zero, and also below a specified upper limit.
void writeUnaligned(void *dstPtr, Type value) noexcept
A handy function to write un-aligned memory without a performance penalty or bus-error.
RangedDirectoryIterator begin(const RangedDirectoryIterator &it)
Returns the iterator that was passed in.
std::unique_ptr< T > rawToUniquePtr(T *ptr)
Converts an owning raw pointer into a unique_ptr, deriving the type of the unique_ptr automatically.
std::u16string toString(NumberT value)
convert an number to an UTF-16 string