LMMS
Loading...
Searching...
No Matches
juce_LV2PluginFormat.cpp
Go to the documentation of this file.
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26#if JUCE_PLUGINHOST_LV2
27
28#include "juce_LV2Common.h"
29#include "juce_LV2Resources.h"
30
32
33#include <thread>
34
35namespace juce
36{
37namespace lv2_host
38{
39
40template <typename Struct, typename Value>
41auto with (Struct s, Value Struct::* member, Value value) noexcept
42{
43 s.*member = std::move (value);
44 return s;
45}
46
47/* Converts a void* to an LV2_Atom* if the buffer looks like it holds a well-formed Atom, or
48 returns nullptr otherwise.
49*/
50static const LV2_Atom* convertToAtomPtr (const void* ptr, size_t size)
51{
52 if (size < sizeof (LV2_Atom))
53 {
55 return nullptr;
56 }
57
58 const auto header = readUnaligned<LV2_Atom> (ptr);
59
60 if (size < header.size + sizeof (LV2_Atom))
61 {
63 return nullptr;
64 }
65
66 // This is UB _if_ the ptr doesn't really point to an LV2_Atom.
67 return reinterpret_cast<const LV2_Atom*> (ptr);
68}
69
70// Allows mutable access to the items in a vector, without allowing the vector itself
71// to be modified.
72template <typename T>
73class SimpleSpan
74{
75public:
76 constexpr SimpleSpan (T* beginIn, T* endIn) : b (beginIn), e (endIn) {}
77
78 constexpr auto begin() const { return b; }
79 constexpr auto end() const { return e; }
80
82 constexpr auto& operator[] (size_t index) { return b[index]; }
84
85 constexpr auto size() const { return (size_t) (e - b); }
86
87private:
88 T* b;
89 T* e;
90};
91
92template <typename T>
93constexpr auto makeSimpleSpan (T* b, T* e) { return SimpleSpan<T> { b, e }; }
94
95template <typename R>
96constexpr auto makeSimpleSpan (R& r) { return makeSimpleSpan (r.data(), r.data() + r.size()); }
97
98struct PhysicalResizeListener
99{
100 virtual ~PhysicalResizeListener() = default;
101 virtual void viewRequestedResizeInPhysicalPixels (int width, int height) = 0;
102};
103
104struct LogicalResizeListener
105{
106 virtual ~LogicalResizeListener() = default;
107 virtual void viewRequestedResizeInLogicalPixels (int width, int height) = 0;
108};
109
110#if JUCE_WINDOWS
111class WindowSizeChangeDetector
112{
113public:
114 WindowSizeChangeDetector()
115 : hook (SetWindowsHookEx (WH_CALLWNDPROC,
116 callWndProc,
119 {}
120
121 ~WindowSizeChangeDetector() noexcept
122 {
123 UnhookWindowsHookEx (hook);
124 }
125
126 static void addListener (HWND hwnd, PhysicalResizeListener& listener)
127 {
128 getActiveEditors().emplace (hwnd, &listener);
129 }
130
131 static void removeListener (HWND hwnd)
132 {
133 getActiveEditors().erase (hwnd);
134 }
135
136private:
137 static std::map<HWND, PhysicalResizeListener*>& getActiveEditors()
138 {
139 static std::map<HWND, PhysicalResizeListener*> map;
140 return map;
141 }
142
143 static void processMessage (int nCode, const CWPSTRUCT* info)
144 {
145 if (nCode < 0 || info == nullptr)
146 return;
147
148 constexpr UINT events[] { WM_SIZING, WM_SIZE, WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGED };
149
150 if (std::find (std::begin (events), std::end (events), info->message) == std::end (events))
151 return;
152
153 auto& map = getActiveEditors();
154 auto iter = map.find (info->hwnd);
155
156 if (iter == map.end())
157 return;
158
159 RECT rect;
160 GetWindowRect (info->hwnd, &rect);
161 iter->second->viewRequestedResizeInPhysicalPixels (rect.right - rect.left, rect.bottom - rect.top);
162 }
163
164 static LRESULT CALLBACK callWndProc (int nCode, WPARAM wParam, LPARAM lParam)
165 {
166 processMessage (nCode, lv2_shared::wordCast<CWPSTRUCT*> (lParam));
167 return CallNextHookEx ({}, nCode, wParam, lParam);
168 }
169
170 HHOOK hook;
171};
172
173class WindowSizeChangeListener
174{
175public:
176 WindowSizeChangeListener (HWND hwndIn, PhysicalResizeListener& l)
177 : hwnd (hwndIn)
178 {
179 detector->addListener (hwnd, l);
180 }
181
182 ~WindowSizeChangeListener()
183 {
184 detector->removeListener (hwnd);
185 }
186
187private:
188 SharedResourcePointer<WindowSizeChangeDetector> detector;
189 HWND hwnd;
190
191 JUCE_LEAK_DETECTOR (WindowSizeChangeListener)
192};
193#endif
194
195struct FreeLilvNode
196{
197 void operator() (LilvNode* ptr) const noexcept { lilv_node_free (ptr); }
198};
199
200using OwningNode = std::unique_ptr<LilvNode, FreeLilvNode>;
201
202template <typename Traits>
203class TypesafeLilvNode
204{
205public:
206 template <typename... Ts>
207 TypesafeLilvNode (Ts&&... ts)
208 : node (Traits::construct (std::forward<Ts> (ts)...)) {}
209
210 bool equals (const TypesafeLilvNode& other) const noexcept
211 {
212 return lilv_node_equals (node.get(), other.node.get());
213 }
214
215 const LilvNode* get() const noexcept { return node.get(); }
216
217 auto getTyped() const noexcept -> decltype (Traits::access (nullptr))
218 {
219 return Traits::access (node.get());
220 }
221
222 static TypesafeLilvNode claim (LilvNode* node)
223 {
224 return TypesafeLilvNode { node };
225 }
226
227 static TypesafeLilvNode copy (const LilvNode* node)
228 {
229 return TypesafeLilvNode { lilv_node_duplicate (node) };
230 }
231
232private:
233 explicit TypesafeLilvNode (LilvNode* ptr)
234 : node (ptr)
235 {
236 jassert (ptr == nullptr || Traits::verify (node.get()));
237 }
238
239 OwningNode node;
240
241 JUCE_LEAK_DETECTOR (TypesafeLilvNode)
242};
243
244struct UriConstructorTrait
245{
246 static LilvNode* construct (LilvWorld* world, const char* uri) noexcept
247 {
248 return lilv_new_uri (world, uri);
249 }
250
251 static LilvNode* construct (LilvWorld* world, const char* host, const char* path) noexcept
252 {
253 return lilv_new_file_uri (world, host, path);
254 }
255
256 static constexpr auto verify = lilv_node_is_uri;
257 static constexpr auto access = lilv_node_as_uri;
258};
259
260struct StringConstructorTrait { static constexpr auto construct = lilv_new_string;
261 static constexpr auto verify = lilv_node_is_string;
262 static constexpr auto access = lilv_node_as_string; };
263
264using NodeUri = TypesafeLilvNode<UriConstructorTrait>;
265using NodeString = TypesafeLilvNode<StringConstructorTrait>;
266
267struct UsefulUris
268{
269 explicit UsefulUris (LilvWorld* worldIn)
270 : world (worldIn) {}
271
272 LilvWorld* const world = nullptr;
273
274 #define X(str) const NodeUri m##str { world, str };
291 #undef X
292};
293
294template <typename Ptr, typename Free>
295struct OwningPtrTraits
296{
297 using type = std::unique_ptr<Ptr, Free>;
298 static const Ptr* get (const type& t) noexcept { return t.get(); }
299};
300
301template <typename Ptr>
302struct NonOwningPtrTraits
303{
304 using type = const Ptr*;
305 static const Ptr* get (const type& t) noexcept { return t; }
306};
307
308struct PluginsIteratorTraits
309{
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;
316};
317
318using PluginsIterator = lv2_shared::Iterator<PluginsIteratorTraits>;
319
320struct PluginClassesIteratorTraits
321{
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;
328};
329
330using PluginClassesIterator = lv2_shared::Iterator<PluginClassesIteratorTraits>;
331
332struct NodesIteratorTraits
333{
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;
340};
341
342using NodesIterator = lv2_shared::Iterator<NodesIteratorTraits>;
343
344struct ScalePointsIteratorTraits
345{
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;
352};
353
354using ScalePointsIterator = lv2_shared::Iterator<ScalePointsIteratorTraits>;
355
356struct UisIteratorTraits
357{
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;
364};
365
366using UisIterator = lv2_shared::Iterator<UisIteratorTraits>;
367
368template <typename PtrTraits>
369class NodesImpl
370{
371public:
372 using type = typename PtrTraits::type;
373
374 template <typename Ptr>
375 explicit NodesImpl (Ptr* ptr)
376 : nodes (type { ptr }) {}
377
378 explicit NodesImpl (type ptr)
379 : nodes (std::move (ptr)) {}
380
381 unsigned size() const noexcept { return lilv_nodes_size (PtrTraits::get (nodes)); }
382
383 NodesIterator begin() const noexcept
384 {
385 return nodes == nullptr ? NodesIterator{}
386 : NodesIterator { PtrTraits::get (nodes) };
387 }
388
389 NodesIterator end() const noexcept { return {}; }
390
391private:
392 type nodes{};
393};
394
395struct NodesFree
396{
397 void operator() (LilvNodes* ptr) const noexcept { lilv_nodes_free (ptr); }
398};
399
400using OwningNodes = NodesImpl<OwningPtrTraits<LilvNodes, NodesFree>>;
401using NonOwningNodes = NodesImpl<NonOwningPtrTraits<LilvNodes>>;
402
403class ScalePoints
404{
405public:
406 explicit ScalePoints (const LilvScalePoints* pt)
407 : points (pt) {}
408
409 ScalePointsIterator begin() const noexcept
410 {
411 return points == nullptr ? ScalePointsIterator{}
412 : ScalePointsIterator { points };
413 }
414
415 ScalePointsIterator end() const noexcept { return {}; }
416
417private:
418 const LilvScalePoints* points = nullptr;
419};
420
421class ScalePoint
422{
423public:
424 explicit ScalePoint (const LilvScalePoint* pt)
425 : point (pt) {}
426
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); }
429
430private:
431 const LilvScalePoint* point = nullptr;
432};
433
434struct PortRange
435{
436 float defaultValue, min, max;
437};
438
439class Port
440{
441public:
442 enum class Kind
443 {
444 control,
445 audio,
446 cv,
447 atom,
448 unknown,
449 };
450
451 enum class Direction
452 {
453 input,
454 output,
455 unknown,
456 };
457
458 Port (const LilvPlugin* pluginIn, const LilvPort* portIn)
459 : plugin (pluginIn), port (portIn) {}
460
461 Direction getDirection (const UsefulUris& uris) const noexcept
462 {
463 if (isA (uris.mLV2_CORE__InputPort))
464 return Direction::input;
465
466 if (isA (uris.mLV2_CORE__OutputPort))
467 return Direction::output;
468
469 return Direction::unknown;
470 }
471
472 Kind getKind (const UsefulUris& uris) const noexcept
473 {
474 if (isA (uris.mLV2_CORE__ControlPort))
475 return Kind::control;
476
477 if (isA (uris.mLV2_CORE__AudioPort))
478 return Kind::audio;
479
480 if (isA (uris.mLV2_CORE__CVPort))
481 return Kind::cv;
482
483 if (isA (uris.mLV2_ATOM__AtomPort))
484 return Kind::atom;
485
486 return Kind::unknown;
487 }
488
489 OwningNode get (const LilvNode* predicate) const noexcept
490 {
491 return OwningNode { lilv_port_get (plugin, port, predicate) };
492 }
493
494 NonOwningNodes getClasses() const noexcept
495 {
496 return NonOwningNodes { lilv_port_get_classes (plugin, port) };
497 }
498
499 NodeString getName() const noexcept
500 {
501 return NodeString::claim (lilv_port_get_name (plugin, port));
502 }
503
504 NodeString getSymbol() const noexcept
505 {
506 return NodeString::copy (lilv_port_get_symbol (plugin, port));
507 }
508
509 OwningNodes getProperties() const noexcept
510 {
511 return OwningNodes { lilv_port_get_properties (plugin, port) };
512 }
513
514 ScalePoints getScalePoints() const noexcept
515 {
516 return ScalePoints { lilv_port_get_scale_points (plugin, port) };
517 }
518
519 bool hasProperty (const NodeUri& uri) const noexcept
520 {
521 return lilv_port_has_property (plugin, port, uri.get());
522 }
523
524 uint32_t getIndex() const noexcept { return lilv_port_get_index (plugin, port); }
525
526 static float getFloatValue (const LilvNode* node, float fallback)
527 {
528 if (lilv_node_is_float (node) || lilv_node_is_int (node))
529 return lilv_node_as_float (node);
530
531 return fallback;
532 }
533
534 bool supportsEvent (const LilvNode* node) const noexcept
535 {
536 return lilv_port_supports_event (plugin, port, node);
537 }
538
539 PortRange getRange() const noexcept
540 {
541 LilvNode* def = nullptr;
542 LilvNode* min = nullptr;
543 LilvNode* max = nullptr;
544
545 lilv_port_get_range (plugin, port, &def, &min, &max);
546
547 const OwningNode defOwner { def };
548 const OwningNode minOwner { min };
549 const OwningNode maxOwner { max };
550
551 return { getFloatValue (def, 0.0f),
552 getFloatValue (min, 0.0f),
553 getFloatValue (max, 1.0f) };
554 }
555
556 bool isValid() const noexcept { return port != nullptr; }
557
558private:
559 bool isA (const NodeUri& uri) const noexcept
560 {
561 return lilv_port_is_a (plugin, port, uri.get());
562 }
563
564 const LilvPlugin* plugin = nullptr;
565 const LilvPort* port = nullptr;
566
567 JUCE_LEAK_DETECTOR (Port)
568};
569
570class Plugin
571{
572public:
573 explicit Plugin (const LilvPlugin* p) : plugin (p) {}
574
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) }; }
584
585 Port getPortByIndex (uint32_t index) const noexcept
586 {
587 return Port { plugin, lilv_plugin_get_port_by_index (plugin, index) };
588 }
589
590 Port getPortByDesignation (const LilvNode* portClass, const LilvNode* designation) const noexcept
591 {
592 return Port { plugin, lilv_plugin_get_port_by_designation (plugin, portClass, designation) };
593 }
594
595 OwningNodes getRequiredFeatures() const noexcept
596 {
597 return OwningNodes { lilv_plugin_get_required_features (plugin) };
598 }
599
600 OwningNodes getOptionalFeatures() const noexcept
601 {
602 return OwningNodes { lilv_plugin_get_optional_features (plugin) };
603 }
604
605 bool hasExtensionData (const NodeUri& uri) const noexcept
606 {
607 return lilv_plugin_has_extension_data (plugin, uri.get());
608 }
609
610 bool hasFeature (const NodeUri& uri) const noexcept
611 {
612 return lilv_plugin_has_feature (plugin, uri.get());
613 }
614
615 template <typename... Classes>
616 uint32_t getNumPortsOfClass (const Classes&... classes) const noexcept
617 {
618 return lilv_plugin_get_num_ports_of_class (plugin, classes.get()..., 0);
619 }
620
621 const LilvPlugin* get() const noexcept { return plugin; }
622
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); }
625
626private:
627 const LilvPlugin* plugin = nullptr;
628
629 JUCE_LEAK_DETECTOR (Plugin)
630};
631
632/*
633 This is very similar to the symap implementation in jalv.
634*/
635class SymbolMap
636{
637public:
638 SymbolMap() = default;
639
640 SymbolMap (std::initializer_list<const char*> uris)
641 {
642 for (const auto* str : uris)
643 map (str);
644 }
645
646 LV2_URID map (const char* uri)
647 {
648 const auto comparator = [this] (size_t index, const String& str)
649 {
650 return strings[index] < str;
651 };
652
653 const auto uriString = String::fromUTF8 (uri);
654 const auto it = std::lower_bound (indices.cbegin(), indices.cend(), uriString, comparator);
655
656 if (it != indices.cend() && strings[*it] == uriString)
657 return static_cast<LV2_URID> (*it + 1);
658
659 const auto index = strings.size();
660 indices.insert (it, index);
661 strings.push_back (uriString);
662 return static_cast<LV2_URID> (index + 1);
663 }
664
665 const char* unmap (LV2_URID urid) const
666 {
667 const auto index = urid - 1;
668 return index < strings.size() ? strings[index].toRawUTF8()
669 : nullptr;
670 }
671
672 static LV2_URID mapUri (LV2_URID_Map_Handle handle, const char* uri)
673 {
674 return static_cast<SymbolMap*> (handle)->map (uri);
675 }
676
677 static const char* unmapUri (LV2_URID_Unmap_Handle handle, LV2_URID urid)
678 {
679 return static_cast<SymbolMap*> (handle)->unmap (urid);
680 }
681
682 LV2_URID_Map getMapFeature() { return { this, mapUri }; }
683 LV2_URID_Unmap getUnmapFeature() { return { this, unmapUri }; }
684
685private:
686 std::vector<String> strings;
687 std::vector<size_t> indices;
688
689 JUCE_LEAK_DETECTOR (SymbolMap)
690};
691
692struct UsefulUrids
693{
694 explicit UsefulUrids (SymbolMap& m) : symap (m) {}
695
696 SymbolMap& symap;
697
698 #define X(token) const LV2_URID m##token = symap.map (token);
731 #undef X
732};
733
734class Log
735{
736public:
737 explicit Log (const UsefulUrids* u) : urids (u) {}
738
739 LV2_Log_Log* getLogFeature() { return &logFeature; }
740
741private:
742 int vprintfCallback (LV2_URID type, const char* fmt, va_list ap) const
743 {
744 // If this is hit, the plugin has encountered some kind of error
745 jassertquiet (type != urids->mLV2_LOG__Error && type != urids->mLV2_LOG__Warning);
746 return std::vfprintf (stderr, fmt, ap);
747 }
748
749 static int vprintfCallback (LV2_Log_Handle handle,
751 const char* fmt,
752 va_list ap)
753 {
754 return static_cast<const Log*> (handle)->vprintfCallback (type, fmt, ap);
755 }
756
757 static int printfCallback (LV2_Log_Handle handle, LV2_URID type, const char* fmt, ...)
758 {
759 va_list list;
760 va_start (list, fmt);
761 auto result = vprintfCallback (handle, type, fmt, list);
762 va_end (list);
763 return result;
764 }
765
766 const UsefulUrids* urids = nullptr;
767 LV2_Log_Log logFeature { this, printfCallback, vprintfCallback };
768
770};
771
772struct Features
773{
774 explicit Features (std::vector<LV2_Feature>&& f)
775 : features (std::move (f)) {}
776
777 static std::vector<String> getUris (const std::vector<LV2_Feature>& features)
778 {
779 std::vector<String> result;
780 result.reserve (features.size());
781
782 for (const auto& feature : features)
783 result.push_back (String::fromUTF8 (feature.URI));
784
785 return result;
786 }
787
788 std::vector<LV2_Feature> features;
789 std::vector<const LV2_Feature*> pointers = makeNullTerminatedArray();
790
791private:
792 std::vector<const LV2_Feature*> makeNullTerminatedArray()
793 {
794 std::vector<const LV2_Feature*> result;
795 result.reserve (features.size() + 1);
796
797 for (const auto& feature : features)
798 result.push_back (&feature);
799
800 result.push_back (nullptr);
801
802 return result;
803 }
804
805 JUCE_LEAK_DETECTOR (Features)
806};
807
808template <typename Extension>
809struct OptionalExtension
810{
811 OptionalExtension() = default;
812
813 explicit OptionalExtension (Extension extensionIn) : extension (extensionIn), valid (true) {}
814
815 Extension extension;
816 bool valid = false;
817};
818
819class Instance
820{
821 struct Free
822 {
823 void operator() (LilvInstance* ptr) const noexcept { lilv_instance_free (ptr); }
824 };
825
826public:
827 using Ptr = std::unique_ptr<LilvInstance, Free>;
828 using GetExtensionData = const void* (*) (const char*);
829
830 Instance (const Plugin& pluginIn, double sampleRate, const LV2_Feature* const* features)
831 : plugin (pluginIn),
832 instance (lilv_plugin_instantiate (plugin.get(), sampleRate, features)) {}
833
834 void activate() { lilv_instance_activate (instance.get()); }
835 void run (uint32_t sampleCount) { lilv_instance_run (instance.get(), sampleCount); }
836 void deactivate() { lilv_instance_deactivate (instance.get()); }
837
838 const char* getUri() const noexcept { return lilv_instance_get_uri (instance.get()); }
839
840 LV2_Handle getHandle() const noexcept { return lilv_instance_get_handle (instance.get()); }
841
842 LilvInstance* get() const noexcept { return instance.get(); }
843
844 void connectPort (uint32_t index, void* data)
845 {
846 lilv_instance_connect_port (instance.get(), index, data);
847 }
848
849 template <typename Extension>
850 OptionalExtension<Extension> getExtensionData (const NodeUri& uri) const noexcept
851 {
852 if (plugin.get() == nullptr || ! plugin.hasExtensionData (uri) || instance.get() == nullptr)
853 return {};
854
855 return OptionalExtension<Extension> { readUnaligned<Extension> (lilv_instance_get_extension_data (instance.get(), uri.getTyped())) };
856 }
857
858 GetExtensionData getExtensionDataCallback() const noexcept
859 {
860 return instance->lv2_descriptor->extension_data;
861 }
862
863 bool operator== (std::nullptr_t) const noexcept { return instance == nullptr; }
864 bool operator!= (std::nullptr_t) const noexcept { return ! (*this == nullptr); }
865
866private:
867 Plugin plugin;
868 Ptr instance;
869
870 JUCE_LEAK_DETECTOR (Instance)
871};
872
873enum class Realtime { no, yes };
874
875// Must be trivial!
876struct WorkResponder
877{
878 static WorkResponder getDefault() { return { nullptr, nullptr }; }
879
880 LV2_Worker_Status processResponse (uint32_t size, const void* data) const
881 {
882 return worker->work_response (handle, size, data);
883 }
884
885 bool isValid() const { return handle != nullptr && worker != nullptr; }
886
887 LV2_Handle handle;
888 const LV2_Worker_Interface* worker;
889};
890
891struct WorkerResponseListener
892{
893 virtual ~WorkerResponseListener() = default;
894 virtual LV2_Worker_Status responseGenerated (WorkResponder, uint32_t, const void*) = 0;
895};
896
897struct RespondHandle
898{
899 LV2_Worker_Status respond (uint32_t size, const void* data) const
900 {
901 if (realtime == Realtime::yes)
902 return listener.responseGenerated (responder, size, data);
903
904 return responder.processResponse (size, data);
905 }
906
907 static LV2_Worker_Status respond (LV2_Worker_Respond_Handle handle,
909 const void* data)
910 {
911 return static_cast<const RespondHandle*> (handle)->respond (size, data);
912 }
913
914 WorkResponder responder;
915 WorkerResponseListener& listener;
916 Realtime realtime;
917};
918
919// Must be trivial!
920struct WorkSubmitter
921{
922 static WorkSubmitter getDefault() { return { nullptr, nullptr, nullptr, nullptr }; }
923
924 LV2_Worker_Status doWork (Realtime realtime, uint32_t size, const void* data) const
925 {
926 // The Worker spec says that the host "MUST NOT make concurrent calls to [work] from
927 // several threads".
928 // Taking the work mutex here ensures that only one piece of work is done at a time.
929 // If we didn't take the work mutex, there would be a danger of work happening
930 // simultaneously on the worker thread and the render thread when switching between
931 // realtime/offline modes (in realtime mode, work happens on the worker thread; in
932 // offline mode, work happens immediately on the render/audio thread).
933 const ScopedLock lock (*workMutex);
934
935 RespondHandle respondHandle { WorkResponder { handle, worker }, *listener, realtime };
936 return worker->work (handle, RespondHandle::respond, &respondHandle, size, data);
937 }
938
939 bool isValid() const { return handle != nullptr && worker != nullptr && listener != nullptr && workMutex != nullptr; }
940
941 LV2_Handle handle;
942 const LV2_Worker_Interface* worker;
943 WorkerResponseListener* listener;
944 CriticalSection* workMutex;
945};
946
947template <typename Trivial, std::enable_if_t<std::is_trivial<Trivial>::value, int> = 0>
948static auto toChars (Trivial value)
949{
950 std::array<char, sizeof (Trivial)> result;
951 writeUnaligned (result.data(), value);
952 return result;
953}
954
955template <typename Context>
956class WorkQueue
957{
958public:
959 static_assert (std::is_trivial<Context>::value, "Context must be copyable as bytes");
960
961 explicit WorkQueue (int size)
962 : fifo (size), data (static_cast<size_t> (size)) {}
963
964 LV2_Worker_Status push (Context context, size_t size, const void* contents)
965 {
966 const auto* bytes = static_cast<const char*> (contents);
967 const auto numToWrite = sizeof (Header) + size;
968
969 if (static_cast<size_t> (fifo.getFreeSpace()) < numToWrite)
971
972 Header header { size, context };
973 const auto headerBuffer = toChars (header);
974
975 const auto scope = fifo.write (static_cast<int> (numToWrite));
976 jassert (scope.blockSize1 + scope.blockSize2 == static_cast<int> (numToWrite));
977
978 size_t index = 0;
979 scope.forEach ([&] (int i)
980 {
981 data[static_cast<size_t> (i)] = index < headerBuffer.size() ? headerBuffer[index]
982 : bytes[index - headerBuffer.size()];
983 ++index;
984 });
985
986 return LV2_WORKER_SUCCESS;
987 }
988
989 Context pop (std::vector<char>& dest)
990 {
991 // If the vector is too small we'll have to resize it on the audio thread
992 jassert (dest.capacity() >= data.size());
993 dest.clear();
994
995 const auto numReady = fifo.getNumReady();
996
997 if (static_cast<size_t> (numReady) < sizeof (Header))
998 {
999 jassert (numReady == 0);
1000 return Context::getDefault();
1001 }
1002
1003 std::array<char, sizeof (Header)> headerBuffer;
1004
1005 {
1006 size_t index = 0;
1007 fifo.read (sizeof (Header)).forEach ([&] (int i)
1008 {
1009 headerBuffer[index++] = data[static_cast<size_t> (i)];
1010 });
1011 }
1012
1013 const auto header = readUnaligned<Header> (headerBuffer.data());
1014
1015 jassert (static_cast<size_t> (fifo.getNumReady()) >= header.size);
1016
1017 dest.resize (header.size);
1018
1019 {
1020 size_t index = 0;
1021 fifo.read (static_cast<int> (header.size)).forEach ([&] (int i)
1022 {
1023 dest[index++] = data[static_cast<size_t> (i)];
1024 });
1025 }
1026
1027 return header.context;
1028 }
1029
1030private:
1031 struct Header
1032 {
1033 size_t size;
1034 Context context;
1035 };
1036
1037 AbstractFifo fifo;
1038 std::vector<char> data;
1039
1040 JUCE_LEAK_DETECTOR (WorkQueue)
1041};
1042
1043/*
1044 Keeps track of active plugin instances, so that we can avoid sending work
1045 messages to dead plugins.
1046*/
1047class HandleRegistry
1048{
1049public:
1050 void insert (LV2_Handle handle)
1051 {
1052 const SpinLock::ScopedLockType lock (mutex);
1053 handles.insert (handle);
1054 }
1055
1056 void erase (LV2_Handle handle)
1057 {
1058 const SpinLock::ScopedLockType lock (mutex);
1059 handles.erase (handle);
1060 }
1061
1062 template <typename Fn>
1063 LV2_Worker_Status ifContains (LV2_Handle handle, Fn&& callback)
1064 {
1065 const SpinLock::ScopedLockType lock (mutex);
1066
1067 if (handles.find (handle) != handles.cend())
1068 return callback();
1069
1071 }
1072
1073private:
1074 std::set<LV2_Handle> handles;
1075 SpinLock mutex;
1076
1077 JUCE_LEAK_DETECTOR (HandleRegistry)
1078};
1079
1080/*
1081 Implements an LV2 Worker, allowing work to be scheduled in realtime
1082 by the plugin instance.
1083
1084 IMPORTANT this will die pretty hard if `getExtensionData (LV2_WORKER__interface)`
1085 returns garbage, so make sure to check that the plugin `hasExtensionData` before
1086 constructing one of these!
1087*/
1088class SharedThreadedWorker : public WorkerResponseListener
1089{
1090public:
1091 ~SharedThreadedWorker() noexcept override
1092 {
1093 shouldExit = true;
1094 thread.join();
1095 }
1096
1097 LV2_Worker_Status schedule (WorkSubmitter submitter,
1098 uint32_t size,
1099 const void* data)
1100 {
1101 return registry.ifContains (submitter.handle, [&]
1102 {
1103 return incoming.push (submitter, size, data);
1104 });
1105 }
1106
1107 LV2_Worker_Status responseGenerated (WorkResponder responder,
1108 uint32_t size,
1109 const void* data) override
1110 {
1111 return registry.ifContains (responder.handle, [&]
1112 {
1113 return outgoing.push (responder, size, data);
1114 });
1115 }
1116
1117 void processResponses()
1118 {
1119 for (;;)
1120 {
1121 auto workerResponder = outgoing.pop (message);
1122
1123 if (! message.empty() && workerResponder.isValid())
1124 workerResponder.processResponse (static_cast<uint32_t> (message.size()), message.data());
1125 else
1126 break;
1127 }
1128 }
1129
1130 void registerHandle (LV2_Handle handle) { registry.insert (handle); }
1131 void deregisterHandle (LV2_Handle handle) { registry.erase (handle); }
1132
1133private:
1134 static constexpr auto queueSize = 8192;
1135 std::atomic<bool> shouldExit { false };
1136 WorkQueue<WorkSubmitter> incoming { queueSize };
1137 WorkQueue<WorkResponder> outgoing { queueSize };
1138 std::vector<char> message = std::vector<char> (queueSize);
1139 std::thread thread { [this]
1140 {
1141 std::vector<char> buffer (queueSize);
1142
1143 while (! shouldExit)
1144 {
1145 const auto submitter = incoming.pop (buffer);
1146
1147 if (! buffer.empty() && submitter.isValid())
1148 submitter.doWork (Realtime::yes, (uint32_t) buffer.size(), buffer.data());
1149 else
1150 std::this_thread::sleep_for (std::chrono::milliseconds (1));
1151 }
1152 } };
1153 HandleRegistry registry;
1154
1155 JUCE_LEAK_DETECTOR (SharedThreadedWorker)
1156};
1157
1158struct HandleHolder
1159{
1160 virtual ~HandleHolder() = default;
1161 virtual LV2_Handle getHandle() const = 0;
1162 virtual const LV2_Worker_Interface* getWorkerInterface() const = 0;
1163};
1164
1165class WorkScheduler
1166{
1167public:
1168 explicit WorkScheduler (HandleHolder& handleHolderIn)
1169 : handleHolder (handleHolderIn) {}
1170
1171 void processResponses() { workerThread->processResponses(); }
1172
1173 LV2_Worker_Schedule& getWorkerSchedule() { return schedule; }
1174
1175 void setNonRealtime (bool nonRealtime) { realtime = ! nonRealtime; }
1176
1177 void registerHandle (LV2_Handle handle) { workerThread->registerHandle (handle); }
1178 void deregisterHandle (LV2_Handle handle) { workerThread->deregisterHandle (handle); }
1179
1180private:
1181 LV2_Worker_Status scheduleWork (uint32_t size, const void* data)
1182 {
1183 WorkSubmitter submitter { handleHolder.getHandle(),
1184 handleHolder.getWorkerInterface(),
1185 workerThread,
1186 &workMutex };
1187
1188 // If we're in realtime mode, the work should go onto a background thread,
1189 // and we'll process it later.
1190 // If we're offline, we can just do the work immediately, without worrying about
1191 // drop-outs
1192 return realtime ? workerThread->schedule (submitter, size, data)
1193 : submitter.doWork (Realtime::no, size, data);
1194 }
1195
1196 static LV2_Worker_Status scheduleWork (LV2_Worker_Schedule_Handle handle,
1197 uint32_t size,
1198 const void* data)
1199 {
1200 return static_cast<WorkScheduler*> (handle)->scheduleWork (size, data);
1201 }
1202
1203 SharedResourcePointer<SharedThreadedWorker> workerThread;
1204 HandleHolder& handleHolder;
1205 LV2_Worker_Schedule schedule { this, scheduleWork };
1206 CriticalSection workMutex;
1207 bool realtime = true;
1208
1209 JUCE_LEAK_DETECTOR (WorkScheduler)
1210};
1211
1212struct FeaturesDataListener
1213{
1214 virtual ~FeaturesDataListener() = default;
1215 virtual LV2_Resize_Port_Status resizeCallback (uint32_t index, size_t size) = 0;
1216};
1217
1218class Resize
1219{
1220public:
1221 explicit Resize (FeaturesDataListener& l)
1222 : listener (l) {}
1223
1224 LV2_Resize_Port_Resize& getFeature() { return resize; }
1225
1226private:
1227 LV2_Resize_Port_Status resizeCallback (uint32_t index, size_t size)
1228 {
1229 return listener.resizeCallback (index, size);
1230 }
1231
1232 static LV2_Resize_Port_Status resizeCallback (LV2_Resize_Port_Feature_Data data, uint32_t index, size_t size)
1233 {
1234 return static_cast<Resize*> (data)->resizeCallback (index, size);
1235 }
1236
1237 FeaturesDataListener& listener;
1238 LV2_Resize_Port_Resize resize { this, resizeCallback };
1239};
1240
1241class FeaturesData
1242{
1243public:
1244 FeaturesData (HandleHolder& handleHolder,
1245 FeaturesDataListener& l,
1246 int32_t maxBlockSizeIn,
1247 int32_t sequenceSizeIn,
1248 const UsefulUrids* u)
1249 : urids (u),
1250 resize (l),
1251 maxBlockSize (maxBlockSizeIn),
1252 sequenceSize (sequenceSizeIn),
1253 workScheduler (handleHolder)
1254 {}
1255
1256 LV2_Options_Option* getOptions() noexcept { return options.data(); }
1257
1258 int32_t getMaxBlockSize() const noexcept { return maxBlockSize; }
1259
1260 void setNonRealtime (bool newValue) { realtime = ! newValue; }
1261
1262 const LV2_Feature* const* getFeatureArray() const noexcept { return features.pointers.data(); }
1263
1264 static std::vector<String> getFeatureUris()
1265 {
1266 return Features::getUris (makeFeatures ({}, {}, {}, {}, {}, {}));
1267 }
1268
1269 void processResponses() { workScheduler.processResponses(); }
1270
1271 void registerHandle (LV2_Handle handle) { workScheduler.registerHandle (handle); }
1272 void deregisterHandle (LV2_Handle handle) { workScheduler.deregisterHandle (handle); }
1273
1274private:
1275 static std::vector<LV2_Feature> makeFeatures (LV2_URID_Map* map,
1277 LV2_Options_Option* options,
1278 LV2_Worker_Schedule* schedule,
1279 LV2_Resize_Port_Resize* resize,
1280 LV2_Log_Log* log)
1281 {
1282 ignoreUnused (log);
1283
1284 return { LV2_Feature { LV2_STATE__loadDefaultState, nullptr },
1286 LV2_Feature { LV2_URID__map, map },
1288 LV2_Feature { LV2_OPTIONS__options, options },
1289 LV2_Feature { LV2_WORKER__schedule, schedule },
1291 #if JUCE_DEBUG
1292 LV2_Feature { LV2_LOG__log, log },
1293 #endif
1295 }
1296
1297 LV2_Options_Option makeOption (const char* uid, const int32_t* ptr)
1298 {
1299 return { LV2_OPTIONS_INSTANCE,
1300 0, // INSTANCE kinds must have a subject of 0
1301 urids->symap.map (uid),
1302 sizeof (int32_t),
1303 urids->symap.map (LV2_ATOM__Int),
1304 ptr };
1305 }
1306
1307 const UsefulUrids* urids;
1308 Resize resize;
1309 Log log { urids };
1310
1311 const int32_t minBlockSize = 0, maxBlockSize = 0, sequenceSize = 0;
1312
1313 std::vector<LV2_Options_Option> options
1314 {
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 }, // The final entry must be nulled out
1319 };
1320
1321 WorkScheduler workScheduler;
1322
1323 LV2_URID_Map map = urids->symap.getMapFeature();
1324 LV2_URID_Unmap unmap = urids->symap.getUnmapFeature();
1325 Features features { makeFeatures (&map,
1326 &unmap,
1327 options.data(),
1328 &workScheduler.getWorkerSchedule(),
1329 &resize.getFeature(),
1330 log.getLogFeature()) };
1331
1332 bool realtime = true;
1333
1334 JUCE_LEAK_DETECTOR (FeaturesData)
1335};
1336
1337//==============================================================================
1338struct TryLockAndCall
1339{
1340 template <typename Fn>
1341 void operator() (SpinLock& mutex, Fn&& fn)
1342 {
1343 const SpinLock::ScopedTryLockType lock (mutex);
1344
1345 if (lock.isLocked())
1346 fn();
1347 }
1348};
1349
1350struct LockAndCall
1351{
1352 template <typename Fn>
1353 void operator() (SpinLock& mutex, Fn&& fn)
1354 {
1355 const SpinLock::ScopedLockType lock (mutex);
1356 fn();
1357 }
1358};
1359
1360struct RealtimeReadTrait
1361{
1362 using Read = TryLockAndCall;
1363 using Write = LockAndCall;
1364};
1365
1366struct RealtimeWriteTrait
1367{
1368 using Read = LockAndCall;
1369 using Write = TryLockAndCall;
1370};
1371
1372struct MessageHeader
1373{
1374 uint32_t portIndex;
1375 uint32_t protocol;
1376};
1377
1378template <typename Header>
1379struct MessageBufferInterface
1380{
1381 virtual ~MessageBufferInterface() = default;
1382 virtual void pushMessage (Header header, uint32_t size, const void* buffer) = 0;
1383};
1384
1385template <typename Header, typename LockTraits>
1386class Messages : public MessageBufferInterface<Header>
1387{
1388 using Read = typename LockTraits::Read;
1389 using Write = typename LockTraits::Write;
1390
1391 struct FullHeader
1392 {
1393 Header header;
1394 uint32_t size;
1395 };
1396
1397public:
1398 Messages() { data.reserve (initialBufferSize); }
1399
1400 void pushMessage (Header header, uint32_t size, const void* buffer) override
1401 {
1402 Write{} (mutex, [&]
1403 {
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);
1408 });
1409 }
1410
1411 template <typename Callback>
1412 void readAllAndClear (Callback&& callback)
1413 {
1414 Read{} (mutex, [&]
1415 {
1416 if (data.empty())
1417 return;
1418
1419 const auto end = data.data() + data.size();
1420
1421 for (auto ptr = data.data(); ptr < end;)
1422 {
1423 const auto header = readUnaligned<FullHeader> (ptr);
1424 callback (header.header, header.size, ptr + sizeof (header));
1425 ptr += sizeof (header) + header.size;
1426 }
1427
1428 data.clear();
1429 });
1430 }
1431
1432private:
1433 static constexpr auto initialBufferSize = 8192;
1434 SpinLock mutex;
1435 std::vector<char> data;
1436
1437 JUCE_LEAK_DETECTOR (Messages)
1438};
1439
1440//==============================================================================
1441class LambdaTimer : private Timer
1442{
1443public:
1444 explicit LambdaTimer (std::function<void()> c) : callback (c) {}
1445
1446 ~LambdaTimer() noexcept override { stopTimer(); }
1447
1448 using Timer::startTimer;
1449 using Timer::startTimerHz;
1450 using Timer::stopTimer;
1451
1452private:
1453 void timerCallback() override { callback(); }
1454
1455 std::function<void()> callback;
1456};
1457
1458struct UiEventListener : public MessageBufferInterface<MessageHeader>
1459{
1460 virtual int idle() = 0;
1461};
1462
1463struct UiMessageHeader
1464{
1465 UiEventListener* listener;
1466 MessageHeader header;
1467};
1468
1469class ProcessorToUi : public MessageBufferInterface<UiMessageHeader>
1470{
1471public:
1472 ProcessorToUi() { timer.startTimerHz (60); }
1473
1474 void addUi (UiEventListener& l) { JUCE_ASSERT_MESSAGE_THREAD; activeUis.insert (&l); }
1475 void removeUi (UiEventListener& l) { JUCE_ASSERT_MESSAGE_THREAD; activeUis.erase (&l); }
1476
1477 void pushMessage (UiMessageHeader header, uint32_t size, const void* buffer) override
1478 {
1479 processorToUi.pushMessage (header, size, buffer);
1480 }
1481
1482private:
1483 Messages<UiMessageHeader, RealtimeWriteTrait> processorToUi;
1484 std::set<UiEventListener*> activeUis;
1485 LambdaTimer timer { [this]
1486 {
1487 for (auto* l : activeUis)
1488 if (l->idle() != 0)
1489 return;
1490
1491 processorToUi.readAllAndClear ([&] (const UiMessageHeader& header, uint32_t size, const char* data)
1492 {
1493 if (activeUis.find (header.listener) != activeUis.cend())
1494 header.listener->pushMessage (header.header, size, data);
1495 });
1496 } };
1497};
1498
1499/* These type identifiers may be used to check the type of the incoming data. */
1500struct StatefulPortUrids
1501{
1502 explicit StatefulPortUrids (SymbolMap& map)
1503 : Float (map.map (LV2_ATOM__Float)),
1504 Double (map.map (LV2_ATOM__Double)),
1505 Int (map.map (LV2_ATOM__Int)),
1506 Long (map.map (LV2_ATOM__Long))
1507 {}
1508
1509 const LV2_URID Float, Double, Int, Long;
1510};
1511
1512/*
1513 A bit like SortedSet, but only requires `operator<` and not `operator==`, so
1514 it behaves a bit more like a std::set.
1515*/
1516template <typename Value>
1517class SafeSortedSet
1518{
1519public:
1520 using iterator = typename std::vector<Value>:: iterator;
1521 using const_iterator = typename std::vector<Value>::const_iterator;
1522
1523 template <typename Other>
1524 const_iterator find (const Other& other) const noexcept
1525 {
1526 const auto it = std::lower_bound (storage.cbegin(), storage.cend(), other);
1527
1528 if (it != storage.cend() && ! (other < *it))
1529 return it;
1530
1531 return storage.cend();
1532 }
1533
1534 void insert (Value&& value) { insertImpl (std::move (value)); }
1535 void insert (const Value& value) { insertImpl (value); }
1536
1537 size_t size() const noexcept { return storage.size(); }
1538 bool empty() const noexcept { return storage.empty(); }
1539
1540 iterator begin() noexcept { return storage. begin(); }
1541 const_iterator begin() const noexcept { return storage. begin(); }
1542 const_iterator cbegin() const noexcept { return storage.cbegin(); }
1543
1544 iterator end() noexcept { return storage. end(); }
1545 const_iterator end() const noexcept { return storage. end(); }
1546 const_iterator cend() const noexcept { return storage.cend(); }
1547
1548 auto& operator[] (size_t index) const { return storage[index]; }
1549
1550private:
1551 template <typename Arg>
1552 void insertImpl (Arg&& value)
1553 {
1554 const auto it = std::lower_bound (storage.cbegin(), storage.cend(), value);
1555
1556 if (it == storage.cend() || value < *it)
1557 storage.insert (it, std::forward<Arg> (value));
1558 }
1559
1560 std::vector<Value> storage;
1561};
1562
1563struct StoredScalePoint
1564{
1565 String label;
1566 float value;
1567
1568 bool operator< (const StoredScalePoint& other) const noexcept { return value < other.value; }
1569};
1570
1571inline bool operator< (const StoredScalePoint& a, float b) noexcept { return a.value < b; }
1572inline bool operator< (float a, const StoredScalePoint& b) noexcept { return a < b.value; }
1573
1574struct ParameterInfo
1575{
1576 ParameterInfo() = default;
1577
1578 ParameterInfo (SafeSortedSet<StoredScalePoint> scalePointsIn,
1579 String identifierIn,
1580 float defaultValueIn,
1581 float minIn,
1582 float maxIn,
1583 bool isToggleIn,
1584 bool isIntegerIn,
1585 bool isEnumIn)
1586 : scalePoints (std::move (scalePointsIn)),
1587 identifier (std::move (identifierIn)),
1588 defaultValue (defaultValueIn),
1589 min (minIn),
1590 max (maxIn),
1591 isToggle (isToggleIn),
1592 isInteger (isIntegerIn),
1593 isEnum (isEnumIn)
1594 {}
1595
1596 static SafeSortedSet<StoredScalePoint> getScalePoints (const Port& port)
1597 {
1598 SafeSortedSet<StoredScalePoint> scalePoints;
1599
1600 for (const LilvScalePoint* p : port.getScalePoints())
1601 {
1602 const ScalePoint wrapper { p };
1603 const auto value = wrapper.getValue();
1604 const auto label = wrapper.getLabel();
1605
1607 scalePoints.insert ({ lilv_node_as_string (label), lilv_node_as_float (value) });
1608 }
1609
1610 return scalePoints;
1611 }
1612
1613 static ParameterInfo getInfoForPort (const UsefulUris& uris, const Port& port)
1614 {
1615 const auto range = port.getRange();
1616
1617 return { getScalePoints (port),
1618 "sym:" + String::fromUTF8 (port.getSymbol().getTyped()),
1619 range.defaultValue,
1620 range.min,
1621 range.max,
1622 port.hasProperty (uris.mLV2_CORE__toggled),
1623 port.hasProperty (uris.mLV2_CORE__integer),
1624 port.hasProperty (uris.mLV2_CORE__enumeration) };
1625 }
1626
1627 SafeSortedSet<StoredScalePoint> scalePoints;
1628
1629 /* This is the 'symbol' of a port, or the 'designation' of a parameter without a symbol. */
1630 String identifier;
1631
1632 float defaultValue = 0.0f, min = 0.0f, max = 1.0f;
1633 bool isToggle = false, isInteger = false, isEnum = false;
1634
1635 JUCE_LEAK_DETECTOR (ParameterInfo)
1636};
1637
1638struct PortHeader
1639{
1640 String name;
1641 String symbol;
1642 uint32_t index;
1643 Port::Direction direction;
1644};
1645
1646struct ControlPort
1647{
1648 ControlPort (const PortHeader& headerIn, const ParameterInfo& infoIn)
1649 : header (headerIn), info (infoIn) {}
1650
1651 PortHeader header;
1652 ParameterInfo info;
1653 float currentValue = info.defaultValue;
1654};
1655
1656struct CVPort
1657{
1658 PortHeader header;
1659};
1660
1661struct AudioPort
1662{
1663 PortHeader header;
1664};
1665
1666template <size_t Alignment>
1667class SingleSizeAlignedStorage
1668{
1669public:
1670 SingleSizeAlignedStorage() = default;
1671
1672 explicit SingleSizeAlignedStorage (size_t sizeInBytes)
1673 : storage (new char[sizeInBytes + Alignment]),
1674 alignedPointer (storage.get()),
1675 space (sizeInBytes + Alignment)
1676 {
1677 alignedPointer = std::align (Alignment, sizeInBytes, alignedPointer, space);
1678 }
1679
1680 void* data() const { return alignedPointer; }
1681 size_t size() const { return space; }
1682
1683private:
1684 std::unique_ptr<char[]> storage;
1685 void* alignedPointer = nullptr;
1686 size_t space = 0;
1687};
1688
1689template <size_t Alignment>
1690static SingleSizeAlignedStorage<Alignment> grow (SingleSizeAlignedStorage<Alignment> storage, size_t size)
1691{
1692 if (size <= storage.size())
1693 return storage;
1694
1695 SingleSizeAlignedStorage<Alignment> newStorage { jmax (size, (storage.size() * 3) / 2) };
1696 std::memcpy (newStorage.data(), storage.data(), storage.size());
1697 return newStorage;
1698}
1699
1700enum class SupportsTime { no, yes };
1701
1702class AtomPort
1703{
1704public:
1705 AtomPort (PortHeader h, size_t bytes, SymbolMap& map, SupportsTime supportsTime)
1706 : header (h), contents (bytes), forge (map.getMapFeature()), time (supportsTime) {}
1707
1708 PortHeader header;
1709
1710 void replaceWithChunk()
1711 {
1712 forge.setBuffer (data(), size());
1713 forge.writeChunk ((uint32_t) (size() - sizeof (LV2_Atom)));
1714 }
1715
1716 void replaceBufferWithAtom (const LV2_Atom* atom)
1717 {
1718 const auto totalSize = atom->size + sizeof (LV2_Atom);
1719
1720 if (totalSize <= size())
1721 std::memcpy (data(), atom, totalSize);
1722 else
1723 replaceWithChunk();
1724 }
1725
1726 void beginSequence()
1727 {
1728 forge.setBuffer (data(), size());
1729 lv2_atom_forge_sequence_head (forge.get(), &frame, 0);
1730 }
1731
1732 void endSequence()
1733 {
1734 lv2_atom_forge_pop (forge.get(), &frame);
1735 }
1736
1737 /* For this to work, the 'atom' pointer must be well-formed.
1738
1739 It must be followed by an atom header, then at least 'size' bytes of body.
1740 */
1741 void addAtomToSequence (int64_t timestamp, const LV2_Atom* atom)
1742 {
1743 // This reinterpret_cast is not UB, casting to a char* is acceptable.
1744 // Doing arithmetic on this pointer is dubious, but I can't think of a better alternative
1745 // given that we don't have any way of knowing the concrete type of the atom.
1746 addEventToSequence (timestamp,
1747 atom->type,
1748 atom->size,
1749 reinterpret_cast<const char*> (atom) + sizeof (LV2_Atom));
1750 }
1751
1752 void addEventToSequence (int64_t timestamp, uint32_t type, uint32_t size, const void* content)
1753 {
1754 lv2_atom_forge_frame_time (forge.get(), timestamp);
1755 lv2_atom_forge_atom (forge.get(), size, type);
1756 lv2_atom_forge_write (forge.get(), content, size);
1757 }
1758
1759 void ensureSizeInBytes (size_t size)
1760 {
1761 contents = grow (std::move (contents), size);
1762 }
1763
1764 char* data() noexcept { return data (*this); }
1765 const char* data() const noexcept { return data (*this); }
1766
1767 size_t size() const noexcept { return contents.size(); }
1768
1769 lv2_shared::AtomForge& getForge() { return forge; }
1770 const lv2_shared::AtomForge& getForge() const { return forge; }
1771
1772 bool getSupportsTime() const { return time == SupportsTime::yes; }
1773
1774private:
1775 template <typename This>
1776 static auto data (This& t) -> decltype (t.data())
1777 {
1778 return unalignedPointerCast<decltype (t.data())> (t.contents.data());
1779 }
1780
1781 // Atoms are required to be 64-bit aligned
1782 SingleSizeAlignedStorage<8> contents;
1783 lv2_shared::AtomForge forge;
1785 SupportsTime time = SupportsTime::no;
1786};
1787
1788class Plugins
1789{
1790public:
1791 explicit Plugins (const LilvPlugins* list) noexcept : plugins (list) {}
1792
1793 unsigned size() const noexcept { return lilv_plugins_size (plugins); }
1794
1795 PluginsIterator begin() const noexcept { return PluginsIterator { plugins }; }
1796 PluginsIterator end() const noexcept { return PluginsIterator{}; }
1797
1798 const LilvPlugin* getByUri (const NodeUri& uri) const
1799 {
1800 return lilv_plugins_get_by_uri (plugins, uri.get());
1801 }
1802
1803private:
1804 const LilvPlugins* plugins = nullptr;
1805};
1806
1807template <typename PtrTraits>
1808class PluginClassesImpl
1809{
1810public:
1811 using type = typename PtrTraits::type;
1812
1813 explicit PluginClassesImpl (type ptr)
1814 : classes (std::move (ptr)) {}
1815
1816 unsigned size() const noexcept { return lilv_plugin_classes_size (PtrTraits::get (classes)); }
1817
1818 PluginClassesIterator begin() const noexcept { return PluginClassesIterator { PtrTraits::get (classes) }; }
1819 PluginClassesIterator end() const noexcept { return PluginClassesIterator{}; }
1820
1821 const LilvPluginClass* getByUri (const NodeUri& uri) const noexcept
1822 {
1823 return lilv_plugin_classes_get_by_uri (PtrTraits::get (classes), uri.get());
1824 }
1825
1826private:
1827 type classes{};
1828};
1829
1830struct PluginClassesFree
1831{
1832 void operator() (LilvPluginClasses* ptr) const noexcept { lilv_plugin_classes_free (ptr); }
1833};
1834
1835using OwningPluginClasses = PluginClassesImpl<OwningPtrTraits<LilvPluginClasses, PluginClassesFree>>;
1836using NonOwningPluginClasses = PluginClassesImpl<NonOwningPtrTraits<LilvPluginClasses>>;
1837
1838class World
1839{
1840public:
1841 World() : world (lilv_world_new()) {}
1842
1843 void loadAll() { lilv_world_load_all (world.get()); }
1844 void loadBundle (const NodeUri& uri) { lilv_world_load_bundle (world.get(), uri.get()); }
1845 void unloadBundle (const NodeUri& uri) { lilv_world_unload_bundle (world.get(), uri.get()); }
1846
1847 void loadResource (const NodeUri& uri) { lilv_world_load_resource (world.get(), uri.get()); }
1848 void unloadResource (const NodeUri& uri) { lilv_world_unload_resource (world.get(), uri.get()); }
1849
1850 void loadSpecifications() { lilv_world_load_specifications (world.get()); }
1851 void loadPluginClasses() { lilv_world_load_plugin_classes (world.get()); }
1852
1853 Plugins getAllPlugins() const { return Plugins { lilv_world_get_all_plugins (world.get()) }; }
1854 NonOwningPluginClasses getPluginClasses() const { return NonOwningPluginClasses { lilv_world_get_plugin_classes (world.get()) }; }
1855
1856 NodeUri newUri (const char* uri) { return NodeUri { world.get(), uri }; }
1857 NodeUri newFileUri (const char* host, const char* path) { return NodeUri { world.get(), host, path }; }
1858 NodeString newString (const char* str) { return NodeString { world.get(), str }; }
1859
1860 bool ask (const LilvNode* subject, const LilvNode* predicate, const LilvNode* object) const
1861 {
1862 return lilv_world_ask (world.get(), subject, predicate, object);
1863 }
1864
1865 OwningNode get (const LilvNode* subject, const LilvNode* predicate, const LilvNode* object) const
1866 {
1867 return OwningNode { lilv_world_get (world.get(), subject, predicate, object) };
1868 }
1869
1870 OwningNodes findNodes (const LilvNode* subject, const LilvNode* predicate, const LilvNode* object) const
1871 {
1872 return OwningNodes { lilv_world_find_nodes (world.get(), subject, predicate, object) };
1873 }
1874
1875 LilvWorld* get() const { return world.get(); }
1876
1877private:
1878 struct Free
1879 {
1880 void operator() (LilvWorld* ptr) const noexcept { lilv_world_free (ptr); }
1881 };
1882
1883 std::unique_ptr<LilvWorld, Free> world;
1884};
1885
1886class Ports
1887{
1888public:
1889 static constexpr auto sequenceSize = 8192;
1890
1891 template <typename Callback>
1892 void forEachPort (Callback&& callback) const
1893 {
1894 for (const auto& port : controlPorts)
1895 callback (port.header);
1896
1897 for (const auto& port : cvPorts)
1898 callback (port.header);
1899
1900 for (const auto& port : audioPorts)
1901 callback (port.header);
1902
1903 for (const auto& port : atomPorts)
1904 callback (port.header);
1905 }
1906
1907 auto getControlPorts() { return makeSimpleSpan (controlPorts); }
1908 auto getControlPorts() const { return makeSimpleSpan (controlPorts); }
1909 auto getCvPorts() { return makeSimpleSpan (cvPorts); }
1910 auto getCvPorts() const { return makeSimpleSpan (cvPorts); }
1911 auto getAudioPorts() { return makeSimpleSpan (audioPorts); }
1912 auto getAudioPorts() const { return makeSimpleSpan (audioPorts); }
1913 auto getAtomPorts() { return makeSimpleSpan (atomPorts); }
1914 auto getAtomPorts() const { return makeSimpleSpan (atomPorts); }
1915
1916 static Optional<Ports> getPorts (World& world, const UsefulUris& uris, const Plugin& plugin, SymbolMap& symap)
1917 {
1918 Ports value;
1919 bool successful = true;
1920
1921 const auto numPorts = plugin.getNumPorts();
1922 const auto timeNode = world.newUri (LV2_TIME__Position);
1923
1924 for (uint32_t i = 0; i != numPorts; ++i)
1925 {
1926 const auto port = plugin.getPortByIndex (i);
1927
1928 const PortHeader header { String::fromUTF8 (port.getName().getTyped()),
1929 String::fromUTF8 (port.getSymbol().getTyped()),
1930 i,
1931 port.getDirection (uris) };
1932
1933 switch (port.getKind (uris))
1934 {
1935 case Port::Kind::control:
1936 {
1937 value.controlPorts.push_back ({ header, ParameterInfo::getInfoForPort (uris, port) });
1938 break;
1939 }
1940
1941 case Port::Kind::cv:
1942 value.cvPorts.push_back ({ header });
1943 break;
1944
1945 case Port::Kind::audio:
1946 {
1947 value.audioPorts.push_back ({ header });
1948 break;
1949 }
1950
1951 case Port::Kind::atom:
1952 {
1953 const auto supportsTime = port.supportsEvent (timeNode.get());
1954 value.atomPorts.push_back ({ header,
1955 (size_t) Ports::sequenceSize,
1956 symap,
1957 supportsTime ? SupportsTime::yes : SupportsTime::no });
1958 break;
1959 }
1960
1961 case Port::Kind::unknown:
1962 successful = false;
1963 break;
1964 }
1965 }
1966
1967 for (auto& atomPort : value.atomPorts)
1968 {
1969 const auto port = plugin.getPortByIndex (atomPort.header.index);
1970 const auto minSize = port.get (uris.mLV2_RESIZE_PORT__minimumSize.get());
1971
1972 if (minSize != nullptr)
1973 atomPort.ensureSizeInBytes ((size_t) lilv_node_as_int (minSize.get()));
1974 }
1975
1976 return successful ? makeOptional (std::move (value)) : nullopt;
1977 }
1978
1979private:
1980 std::vector<ControlPort> controlPorts;
1981 std::vector<CVPort> cvPorts;
1982 std::vector<AudioPort> audioPorts;
1983 std::vector<AtomPort> atomPorts;
1984};
1985
1986class InstanceWithSupports : private FeaturesDataListener,
1987 private HandleHolder
1988{
1989public:
1990 InstanceWithSupports (World& world,
1991 std::unique_ptr<SymbolMap>&& symapIn,
1992 const Plugin& plugin,
1993 Ports portsIn,
1994 int32_t initialBufferSize,
1995 double sampleRate)
1996 : symap (std::move (symapIn)),
1997 ports (std::move (portsIn)),
1998 features (*this, *this, initialBufferSize, lv2_host::Ports::sequenceSize, &urids),
1999 instance (plugin, sampleRate, features.getFeatureArray()),
2000 workerInterface (instance.getExtensionData<LV2_Worker_Interface> (world.newUri (LV2_WORKER__interface)))
2001 {
2002 if (instance == nullptr)
2003 return;
2004
2005 for (auto& port : ports.getControlPorts())
2006 instance.connectPort (port.header.index, &port.currentValue);
2007
2008 for (auto& port : ports.getAtomPorts())
2009 instance.connectPort (port.header.index, port.data());
2010
2011 for (auto& port : ports.getCvPorts())
2012 instance.connectPort (port.header.index, nullptr);
2013
2014 for (auto& port : ports.getAudioPorts())
2015 instance.connectPort (port.header.index, nullptr);
2016
2017 features.registerHandle (instance.getHandle());
2018 }
2019
2020 ~InstanceWithSupports() override
2021 {
2022 if (instance != nullptr)
2023 features.deregisterHandle (instance.getHandle());
2024 }
2025
2026 std::unique_ptr<SymbolMap> symap;
2027 const UsefulUrids urids { *symap };
2028 Ports ports;
2029 FeaturesData features;
2030 Instance instance;
2031 Messages<MessageHeader, RealtimeReadTrait> uiToProcessor;
2032 SharedResourcePointer<ProcessorToUi> processorToUi;
2033
2034private:
2035 LV2_Handle handle = instance == nullptr ? nullptr : instance.getHandle();
2036 OptionalExtension<LV2_Worker_Interface> workerInterface;
2037
2038 LV2_Handle getHandle() const override { return handle; }
2039 const LV2_Worker_Interface* getWorkerInterface() const override { return workerInterface.valid ? &workerInterface.extension : nullptr; }
2040
2041 LV2_Resize_Port_Status resizeCallback (uint32_t index, size_t size) override
2042 {
2043 if (ports.getAtomPorts().size() <= index)
2045
2046 auto& port = ports.getAtomPorts()[index];
2047
2048 if (port.header.direction != Port::Direction::output)
2050
2051 port.ensureSizeInBytes (size);
2052 instance.connectPort (port.header.index, port.data());
2053
2055 }
2056
2057 JUCE_DECLARE_NON_COPYABLE (InstanceWithSupports)
2058 JUCE_DECLARE_NON_MOVEABLE (InstanceWithSupports)
2059 JUCE_LEAK_DETECTOR (InstanceWithSupports)
2060};
2061
2062struct PortState
2063{
2064 const void* data;
2065 uint32_t size;
2066 uint32_t kind;
2067};
2068
2069class PortMap
2070{
2071public:
2072 explicit PortMap (Ports& ports)
2073 {
2074 for (auto& port : ports.getControlPorts())
2075 symbolToControlPortMap.emplace (port.header.symbol, &port);
2076 }
2077
2078 PortState getState (const String& symbol, const StatefulPortUrids& urids)
2079 {
2080 if (auto* port = getControlPortForSymbol (symbol))
2081 return { &port->currentValue, sizeof (float), urids.Float };
2082
2083 // At time of writing, lilv_state_new_from_instance did not attempt to store
2084 // the state of non-control ports. Perhaps that has changed?
2086 return { nullptr, 0, 0 };
2087 }
2088
2089 void restoreState (const String& symbol, const StatefulPortUrids& urids, PortState ps)
2090 {
2091 if (auto* port = getControlPortForSymbol (symbol))
2092 {
2093 port->currentValue = [&]() -> float
2094 {
2095 if (ps.kind == urids.Float)
2096 return getValueFrom<float> (ps.data, ps.size);
2097
2098 if (ps.kind == urids.Double)
2099 return getValueFrom<double> (ps.data, ps.size);
2100
2101 if (ps.kind == urids.Int)
2102 return getValueFrom<int32_t> (ps.data, ps.size);
2103
2104 if (ps.kind == urids.Long)
2105 return getValueFrom<int64_t> (ps.data, ps.size);
2106
2108 return {};
2109 }();
2110 }
2111 else
2112 jassertfalse; // Restoring state for non-control ports is not currently supported.
2113 }
2114
2115private:
2116 template <typename Value>
2117 static float getValueFrom (const void* data, uint32_t size)
2118 {
2119 jassertquiet (size == sizeof (Value));
2120 return (float) readUnaligned<Value> (data);
2121 }
2122
2123 ControlPort* getControlPortForSymbol (const String& symbol) const
2124 {
2125 const auto iter = symbolToControlPortMap.find (symbol);
2126 return iter != symbolToControlPortMap.cend() ? iter->second : nullptr;
2127 }
2128
2129 std::map<String, ControlPort*> symbolToControlPortMap;
2130 JUCE_LEAK_DETECTOR (PortMap)
2131};
2132
2133struct FreeString { void operator() (void* ptr) const noexcept { lilv_free (ptr); } };
2134
2135class PluginState
2136{
2137public:
2138 PluginState() = default;
2139
2140 explicit PluginState (LilvState* ptr)
2141 : state (ptr) {}
2142
2143 const LilvState* get() const noexcept { return state.get(); }
2144
2145 void restore (InstanceWithSupports& instance, PortMap& portMap) const
2146 {
2147 if (state != nullptr)
2148 SaveRestoreHandle { instance, portMap }.restore (state.get());
2149 }
2150
2151 std::string toString (LilvWorld* world, LV2_URID_Map* map, LV2_URID_Unmap* unmap, const char* uri) const
2152 {
2153 std::unique_ptr<char, FreeString> result { lilv_state_to_string (world,
2154 map,
2155 unmap,
2156 state.get(),
2157 uri,
2158 nullptr) };
2159 return std::string { result.get() };
2160 }
2161
2162 String getLabel() const
2163 {
2164 return String::fromUTF8 (lilv_state_get_label (state.get()));
2165 }
2166
2167 void setLabel (const String& label)
2168 {
2169 lilv_state_set_label (state.get(), label.toRawUTF8());
2170 }
2171
2172 class SaveRestoreHandle
2173 {
2174 public:
2175 explicit SaveRestoreHandle (InstanceWithSupports& instanceIn, PortMap& portMap)
2176 : instance (instanceIn.instance.get()),
2177 features (instanceIn.features.getFeatureArray()),
2178 urids (*instanceIn.symap),
2179 map (portMap)
2180 {}
2181
2182 PluginState save (const LilvPlugin* plugin, LV2_URID_Map* mapFeature)
2183 {
2184 return PluginState { lilv_state_new_from_instance (plugin,
2185 instance,
2186 mapFeature,
2187 nullptr,
2188 nullptr,
2189 nullptr,
2190 nullptr,
2191 getPortValue,
2192 this,
2194 features ) };
2195 }
2196
2197 void restore (const LilvState* stateIn)
2198 {
2199 lilv_state_restore (stateIn,
2200 instance,
2201 setPortValue,
2202 this,
2203 0,
2204 features);
2205 }
2206
2207 private:
2208 static const void* getPortValue (const char* portSymbol,
2209 void* userData,
2210 uint32_t* size,
2211 uint32_t* type)
2212 {
2213 auto& handle = *static_cast<SaveRestoreHandle*> (userData);
2214
2215 const auto state = handle.map.getState (portSymbol, handle.urids);
2216 *size = state.size;
2217 *type = state.kind;
2218 return state.data;
2219 }
2220
2221 static void setPortValue (const char* portSymbol,
2222 void* userData,
2223 const void* value,
2224 uint32_t size,
2225 uint32_t type)
2226 {
2227 const auto& handle = *static_cast<const SaveRestoreHandle*> (userData);
2228 handle.map.restoreState (portSymbol, handle.urids, { static_cast<const char*> (value), size, type });
2229 }
2230
2231 LilvInstance* instance = nullptr;
2232 const LV2_Feature* const* features = nullptr;
2233 const StatefulPortUrids urids;
2234 PortMap& map;
2235 };
2236
2237private:
2238 struct Free
2239 {
2240 void operator() (LilvState* ptr) const noexcept { lilv_state_free (ptr); }
2241 };
2242
2243 std::unique_ptr<LilvState, Free> state;
2244
2245 JUCE_LEAK_DETECTOR (PluginState)
2246};
2247
2248/*
2249 Wraps an LV2 UI bundle, providing access to the descriptor (if available).
2250*/
2251struct UiDescriptorLibrary
2252{
2253 using GetDescriptor = LV2UI_Descriptor* (*) (uint32_t);
2254
2255 UiDescriptorLibrary() = default;
2256
2257 explicit UiDescriptorLibrary (const String& libraryPath)
2258 : library (std::make_unique<DynamicLibrary> (libraryPath)),
2259 getDescriptor (lv2_shared::wordCast<GetDescriptor> (library->getFunction ("lv2ui_descriptor"))) {}
2260
2261 std::unique_ptr<DynamicLibrary> library;
2262 GetDescriptor getDescriptor = nullptr;
2263};
2264
2265class UiDescriptorArgs
2266{
2267public:
2268 String libraryPath;
2269 String uiUri;
2270
2271 auto withLibraryPath (String v) const noexcept { return with (&UiDescriptorArgs::libraryPath, v); }
2272 auto withUiUri (String v) const noexcept { return with (&UiDescriptorArgs::uiUri, v); }
2273
2274private:
2275 UiDescriptorArgs with (String UiDescriptorArgs::* member, String value) const noexcept
2276 {
2277 return juce::lv2_host::with (*this, member, std::move (value));
2278 }
2279};
2280
2281/*
2282 Stores a pointer to the descriptor for a specific UI bundle and UI URI.
2283*/
2284class UiDescriptor
2285{
2286public:
2287 UiDescriptor() = default;
2288
2289 explicit UiDescriptor (const UiDescriptorArgs& args)
2290 : library (args.libraryPath),
2291 descriptor (extractUiDescriptor (library, args.uiUri.toRawUTF8()))
2292 {}
2293
2294 void portEvent (LV2UI_Handle ui,
2295 uint32_t portIndex,
2296 uint32_t bufferSize,
2298 const void* buffer) const
2299 {
2301
2302 if (auto* lv2Descriptor = get())
2303 if (auto* callback = lv2Descriptor->port_event)
2304 callback (ui, portIndex, bufferSize, format, buffer);
2305 }
2306
2307 bool hasExtensionData (World& world, const char* uid) const
2308 {
2309 return world.ask (world.newUri (descriptor->URI).get(),
2310 world.newUri (LV2_CORE__extensionData).get(),
2311 world.newUri (uid).get());
2312 }
2313
2314 template <typename Extension>
2315 OptionalExtension<Extension> getExtensionData (World& world, const char* uid) const
2316 {
2317 if (! hasExtensionData (world, uid))
2318 return {};
2319
2320 if (auto* lv2Descriptor = get())
2321 if (auto* extension = lv2Descriptor->extension_data)
2322 return OptionalExtension<Extension> (readUnaligned<Extension> (extension (uid)));
2323
2324 return {};
2325 }
2326
2327 const LV2UI_Descriptor* get() const noexcept { return descriptor; }
2328
2329private:
2330 static const LV2UI_Descriptor* extractUiDescriptor (const UiDescriptorLibrary& lib, const char* uiUri)
2331 {
2332 if (lib.getDescriptor == nullptr)
2333 return nullptr;
2334
2335 for (uint32_t i = 0;; ++i)
2336 {
2337 const auto* descriptor = lib.getDescriptor (i);
2338
2339 if (descriptor == nullptr)
2340 return nullptr;
2341
2342 if (strcmp (uiUri, descriptor->URI) == 0)
2343 return descriptor;
2344 }
2345 }
2346
2347 UiDescriptorLibrary library;
2348 const LV2UI_Descriptor* descriptor = nullptr;
2349
2350 JUCE_LEAK_DETECTOR (UiDescriptor)
2351};
2352
2353enum class Update { no, yes };
2354
2355/* A bit like the FlaggedFloatCache used by the VST3 host/client.
2356
2357 While the FlaggedFloatCache always clears all set flags during the ifSet() call,
2358 this class stores the "value changed" flags for the processor and UI separately,
2359 so that they can be read at different rates.
2360*/
2361class ParameterValuesAndFlags
2362{
2363public:
2364 ParameterValuesAndFlags() = default;
2365
2366 explicit ParameterValuesAndFlags (size_t sizeIn)
2367 : values (sizeIn),
2368 needsUiUpdate (sizeIn),
2369 needsProcessorUpdate (sizeIn)
2370 {
2371 std::fill (values.begin(), values.end(), 0.0f);
2372 }
2373
2374 size_t size() const noexcept { return values.size(); }
2375
2376 void set (size_t index, float value, Update update)
2377 {
2378 jassert (index < size());
2379 values[index].store (value, std::memory_order_relaxed);
2380 needsUiUpdate .set (index, update == Update::yes ? 1 : 0);
2381 needsProcessorUpdate.set (index, update == Update::yes ? 1 : 0);
2382 }
2383
2384 float get (size_t index) const noexcept
2385 {
2386 jassert (index < size());
2387 return values[index].load (std::memory_order_relaxed);
2388 }
2389
2390 template <typename Callback>
2391 void ifProcessorValuesChanged (Callback&& callback)
2392 {
2393 ifChanged (needsProcessorUpdate, std::forward<Callback> (callback));
2394 }
2395
2396 template <typename Callback>
2397 void ifUiValuesChanged (Callback&& callback)
2398 {
2399 ifChanged (needsUiUpdate, std::forward<Callback> (callback));
2400 }
2401
2402 void clearUiFlags() { needsUiUpdate.clear(); }
2403
2404private:
2405 template <typename Callback>
2406 void ifChanged (FlagCache<1>& flags, Callback&& callback)
2407 {
2408 flags.ifSet ([this, &callback] (size_t groupIndex, uint32_t)
2409 {
2410 callback (groupIndex, values[groupIndex].load (std::memory_order_relaxed));
2411 });
2412 }
2413
2414 std::vector<std::atomic<float>> values;
2415 FlagCache<1> needsUiUpdate;
2416 FlagCache<1> needsProcessorUpdate;
2417
2418 JUCE_LEAK_DETECTOR (ParameterValuesAndFlags)
2419};
2420
2421class LV2Parameter : public AudioPluginInstance::HostedParameter
2422{
2423public:
2424 LV2Parameter (const String& nameIn,
2425 const ParameterInfo& infoIn,
2426 ParameterValuesAndFlags& floatCache)
2427 : cache (floatCache),
2428 info (infoIn),
2429 range (info.min, info.max),
2430 name (nameIn),
2431 normalisedDefault (range.convertTo0to1 (infoIn.defaultValue))
2432 {}
2433
2434 float getValue() const noexcept override
2435 {
2436 return range.convertTo0to1 (getDenormalisedValue());
2437 }
2438
2439 void setValue (float f) override
2440 {
2441 cache.set ((size_t) getParameterIndex(), range.convertFrom0to1 (f), Update::yes);
2442 }
2443
2444 void setDenormalisedValue (float denormalised)
2445 {
2446 cache.set ((size_t) getParameterIndex(), denormalised, Update::yes);
2447 sendValueChangedMessageToListeners (range.convertTo0to1 (denormalised));
2448 }
2449
2450 void setDenormalisedValueWithoutTriggeringUpdate (float denormalised)
2451 {
2452 cache.set ((size_t) getParameterIndex(), denormalised, Update::no);
2453 sendValueChangedMessageToListeners (range.convertTo0to1 (denormalised));
2454 }
2455
2456 float getDenormalisedValue() const noexcept
2457 {
2458 return cache.get ((size_t) getParameterIndex());
2459 }
2460
2461 float getDefaultValue() const override { return normalisedDefault; }
2462 float getDenormalisedDefaultValue() const { return info.defaultValue; }
2463
2464 float getValueForText (const String& text) const override
2465 {
2466 if (! info.isEnum)
2467 return range.convertTo0to1 (text.getFloatValue());
2468
2469 const auto it = std::find_if (info.scalePoints.begin(),
2470 info.scalePoints.end(),
2471 [&] (const StoredScalePoint& stored) { return stored.label == text; });
2472 return it != info.scalePoints.end() ? range.convertTo0to1 (it->value) : normalisedDefault;
2473 }
2474
2475 int getNumSteps() const override
2476 {
2477 if (info.isToggle)
2478 return 2;
2479
2480 if (info.isEnum)
2481 return static_cast<int> (info.scalePoints.size());
2482
2483 if (info.isInteger)
2484 return static_cast<int> (range.getRange().getLength()) + 1;
2485
2486 return AudioProcessorParameter::getNumSteps();
2487 }
2488
2489 bool isDiscrete() const override { return info.isEnum || info.isInteger || info.isToggle; }
2490 bool isBoolean() const override { return info.isToggle; }
2491
2492 StringArray getAllValueStrings() const override
2493 {
2494 if (! info.isEnum)
2495 return {};
2496
2497 return AudioProcessorParameter::getAllValueStrings();
2498 }
2499
2500 String getText (float normalisedValue, int) const override
2501 {
2502 const auto denormalised = range.convertFrom0to1 (normalisedValue);
2503
2504 if (info.isEnum && ! info.scalePoints.empty())
2505 {
2506 // The normalised value might not correspond to the exact value of a scale point.
2507 // In this case, we find the closest label by searching the midpoints of the scale
2508 // point values.
2509 const auto index = std::distance (midPoints.begin(),
2510 std::lower_bound (midPoints.begin(), midPoints.end(), normalisedValue));
2511 jassert (isPositiveAndBelow (index, info.scalePoints.size()));
2512 return info.scalePoints[(size_t) index].label;
2513 }
2514
2515 return getFallbackParameterString (denormalised);
2516 }
2517
2518 String getParameterID() const override
2519 {
2520 return info.identifier;
2521 }
2522
2523 String getName (int maxLength) const override
2524 {
2525 return name.substring (0, maxLength);
2526 }
2527
2528 String getLabel() const override
2529 {
2530 // TODO
2531 return {};
2532 }
2533
2534private:
2535 String getFallbackParameterString (float denormalised) const
2536 {
2537 if (info.isToggle)
2538 return denormalised > 0.0f ? "On" : "Off";
2539
2540 if (info.isInteger)
2541 return String { static_cast<int> (denormalised) };
2542
2543 return String { denormalised };
2544 }
2545
2546 static std::vector<float> findScalePointMidPoints (const SafeSortedSet<StoredScalePoint>& set)
2547 {
2548 if (set.size() < 2)
2549 return {};
2550
2551 std::vector<float> result;
2552
2553 for (auto it = std::next (set.begin()); it != set.end(); ++it)
2554 result.push_back ((std::prev (it)->value + it->value) * 0.5f);
2555
2556 jassert (std::is_sorted (result.begin(), result.end()));
2557 jassert (result.size() + 1 == set.size());
2558 return result;
2559 }
2560
2561 ParameterValuesAndFlags& cache;
2562 const ParameterInfo info;
2563 const std::vector<float> midPoints = findScalePointMidPoints (info.scalePoints);
2564 const NormalisableRange<float> range;
2565 const String name;
2566 const float normalisedDefault;
2567
2568 JUCE_LEAK_DETECTOR (LV2Parameter)
2569};
2570
2571class UiInstanceArgs
2572{
2573public:
2574 File bundlePath;
2575 URL pluginUri;
2576
2577 auto withBundlePath (File v) const noexcept { return with (&UiInstanceArgs::bundlePath, std::move (v)); }
2578 auto withPluginUri (URL v) const noexcept { return with (&UiInstanceArgs::pluginUri, std::move (v)); }
2579
2580private:
2581 template <typename Member>
2582 UiInstanceArgs with (Member UiInstanceArgs::* member, Member value) const noexcept
2583 {
2584 return juce::lv2_host::with (*this, member, std::move (value));
2585 }
2586};
2587
2588static File bundlePathFromUri (const char* uri)
2589{
2590 return File { std::unique_ptr<char, FreeString> { lilv_file_uri_parse (uri, nullptr) }.get() };
2591}
2592
2593/*
2594 Creates and holds a UI instance for a plugin with a specific URI, using the provided descriptor.
2595*/
2596class UiInstance
2597{
2598public:
2599 UiInstance (World& world,
2600 const UiDescriptor* descriptorIn,
2601 const UiInstanceArgs& args,
2602 const LV2_Feature* const* features,
2603 MessageBufferInterface<MessageHeader>& messagesIn,
2604 SymbolMap& map,
2605 PhysicalResizeListener& rl)
2606 : descriptor (descriptorIn),
2607 resizeListener (rl),
2608 uiToProcessor (messagesIn),
2609 mLV2_UI__floatProtocol (map.map (LV2_UI__floatProtocol)),
2610 mLV2_ATOM__atomTransfer (map.map (LV2_ATOM__atomTransfer)),
2611 mLV2_ATOM__eventTransfer (map.map (LV2_ATOM__eventTransfer)),
2612 instance (makeInstance (args.pluginUri, args.bundlePath, features)),
2613 idleCallback (getExtensionData<LV2UI_Idle_Interface> (world, LV2_UI__idleInterface))
2614 {
2615 jassert (descriptor != nullptr);
2616 jassert (widget != nullptr);
2617
2618 ignoreUnused (resizeListener);
2619 }
2620
2621 LV2UI_Handle getHandle() const noexcept { return instance.get(); }
2622
2623 void pushMessage (MessageHeader header, uint32_t size, const void* buffer)
2624 {
2625 descriptor->portEvent (getHandle(), header.portIndex, size, header.protocol, buffer);
2626 }
2627
2628 int idle()
2629 {
2630 if (idleCallback.valid && idleCallback.extension.idle != nullptr)
2631 return idleCallback.extension.idle (getHandle());
2632
2633 return 0;
2634 }
2635
2636 template <typename Extension>
2637 OptionalExtension<Extension> getExtensionData (World& world, const char* uid) const
2638 {
2639 return descriptor->getExtensionData<Extension> (world, uid);
2640 }
2641
2642 Rectangle<int> getDetectedViewBounds() const
2643 {
2644 #if JUCE_MAC
2645 const auto frame = [(NSView*) widget frame];
2646 return { (int) frame.size.width, (int) frame.size.height };
2647 #elif JUCE_LINUX || JUCE_BSD
2648 Window root = 0;
2649 int wx = 0, wy = 0;
2650 unsigned int ww = 0, wh = 0, bw = 0, bitDepth = 0;
2651
2652 XWindowSystemUtilities::ScopedXLock xLock;
2653 auto* display = XWindowSystem::getInstance()->getDisplay();
2654 X11Symbols::getInstance()->xGetGeometry (display,
2655 (::Drawable) widget,
2656 &root,
2657 &wx,
2658 &wy,
2659 &ww,
2660 &wh,
2661 &bw,
2662 &bitDepth);
2663
2664 return { (int) ww, (int) wh };
2665 #elif JUCE_WINDOWS
2666 RECT rect;
2667 GetWindowRect ((HWND) widget, &rect);
2668 return { rect.right - rect.left, rect.bottom - rect.top };
2669 #else
2670 return {};
2671 #endif
2672 }
2673
2674 const UiDescriptor* descriptor = nullptr;
2675
2676private:
2677 using Instance = std::unique_ptr<void, void (*) (LV2UI_Handle)>;
2678 using Idle = int (*) (LV2UI_Handle);
2679
2680 Instance makeInstance (const URL& pluginUri, const File& bundlePath, const LV2_Feature* const* features)
2681 {
2682 if (descriptor->get() == nullptr)
2683 return { nullptr, [] (LV2UI_Handle) {} };
2684
2685 return Instance { descriptor->get()->instantiate (descriptor->get(),
2686 pluginUri.toString (false).toRawUTF8(),
2688 writeFunction,
2689 this,
2690 &widget,
2691 features),
2692 descriptor->get()->cleanup };
2693 }
2694
2695 void write (uint32_t portIndex, uint32_t bufferSize, uint32_t protocol, const void* buffer)
2696 {
2697 const LV2_URID protocols[] { 0, mLV2_UI__floatProtocol, mLV2_ATOM__atomTransfer, mLV2_ATOM__eventTransfer };
2698 const auto it = std::find (std::begin (protocols), std::end (protocols), protocol);
2699
2700 if (it != std::end (protocols))
2701 {
2702 uiToProcessor.pushMessage ({ portIndex, protocol }, bufferSize, buffer);
2703 }
2704 }
2705
2706 static void writeFunction (LV2UI_Controller controller,
2707 uint32_t portIndex,
2708 uint32_t bufferSize,
2709 uint32_t portProtocol,
2710 const void* buffer)
2711 {
2712 jassert (controller != nullptr);
2713 static_cast<UiInstance*> (controller)->write (portIndex, bufferSize, portProtocol, buffer);
2714 }
2715
2716 PhysicalResizeListener& resizeListener;
2717 MessageBufferInterface<MessageHeader>& uiToProcessor;
2718 LV2UI_Widget widget = nullptr;
2719 const LV2_URID mLV2_UI__floatProtocol;
2720 const LV2_URID mLV2_ATOM__atomTransfer;
2721 const LV2_URID mLV2_ATOM__eventTransfer;
2722 Instance instance;
2723 OptionalExtension<LV2UI_Idle_Interface> idleCallback;
2724
2725 #if JUCE_MAC
2726 NSViewFrameWatcher frameWatcher { (NSView*) widget, [this]
2727 {
2728 const auto bounds = getDetectedViewBounds();
2729 resizeListener.viewRequestedResizeInPhysicalPixels (bounds.getWidth(), bounds.getHeight());
2730 } };
2731 #elif JUCE_WINDOWS
2732 WindowSizeChangeListener frameWatcher { (HWND) widget, resizeListener };
2733 #endif
2734
2735 JUCE_LEAK_DETECTOR (UiInstance)
2736};
2737
2738struct TouchListener
2739{
2740 virtual ~TouchListener() = default;
2741 virtual void controlGrabbed (uint32_t port, bool grabbed) = 0;
2742};
2743
2744class AsyncFn : public AsyncUpdater
2745{
2746public:
2747 explicit AsyncFn (std::function<void()> callbackIn)
2748 : callback (std::move (callbackIn)) {}
2749
2750 ~AsyncFn() override { cancelPendingUpdate(); }
2751
2752 void handleAsyncUpdate() override { callback(); }
2753
2754private:
2755 std::function<void()> callback;
2756};
2757
2758class UiFeaturesDataOptions
2759{
2760public:
2761 float initialScaleFactor = 0.0f, sampleRate = 0.0f;
2762
2763 auto withInitialScaleFactor (float v) const { return with (&UiFeaturesDataOptions::initialScaleFactor, v); }
2764 auto withSampleRate (float v) const { return with (&UiFeaturesDataOptions::sampleRate, v); }
2765
2766private:
2767 UiFeaturesDataOptions with (float UiFeaturesDataOptions::* member, float value) const
2768 {
2769 return juce::lv2_host::with (*this, member, value);
2770 }
2771};
2772
2773class UiFeaturesData
2774{
2775public:
2776 UiFeaturesData (PhysicalResizeListener& rl,
2777 TouchListener& tl,
2778 LV2_Handle instanceIn,
2779 LV2UI_Widget parentIn,
2780 Instance::GetExtensionData getExtensionData,
2781 const Ports& ports,
2782 SymbolMap& symapIn,
2783 const UiFeaturesDataOptions& optIn)
2784 : opts (optIn),
2785 resizeListener (rl),
2786 touchListener (tl),
2787 instance (instanceIn),
2788 parent (parentIn),
2789 symap (symapIn),
2790 dataAccess { getExtensionData },
2791 portIndices (makePortIndices (ports))
2792 {
2793 }
2794
2795 const LV2_Feature* const* getFeatureArray() const noexcept { return features.pointers.data(); }
2796
2797 static std::vector<String> getFeatureUris()
2798 {
2799 return Features::getUris (makeFeatures ({}, {}, {}, {}, {}, {}, {}, {}, {}, {}));
2800 }
2801
2802 Rectangle<int> getLastRequestedBounds() const { return { lastRequestedWidth, lastRequestedHeight }; }
2803
2804private:
2805 static std::vector<LV2_Feature> makeFeatures (LV2UI_Resize* resize,
2807 LV2_Handle handle,
2808 LV2_Extension_Data_Feature* data,
2809 LV2_URID_Map* map,
2811 LV2UI_Port_Map* portMap,
2812 LV2UI_Touch* touch,
2813 LV2_Options_Option* options,
2814 LV2_Log_Log* log)
2815 {
2816 return { LV2_Feature { LV2_UI__resize, resize },
2821 LV2_Feature { LV2_URID__map, map },
2823 LV2_Feature { LV2_UI__portMap, portMap },
2824 LV2_Feature { LV2_UI__touch, touch },
2825 LV2_Feature { LV2_OPTIONS__options, options },
2826 LV2_Feature { LV2_LOG__log, log } };
2827 }
2828
2829 int resizeCallback (int width, int height)
2830 {
2831 lastRequestedWidth = width;
2832 lastRequestedHeight = height;
2833 resizeListener.viewRequestedResizeInPhysicalPixels (width, height);
2834 return 0;
2835 }
2836
2837 static int resizeCallback (LV2UI_Feature_Handle handle, int width, int height)
2838 {
2839 return static_cast<UiFeaturesData*> (handle)->resizeCallback (width, height);
2840 }
2841
2842 uint32_t portIndexCallback (const char* symbol) const
2843 {
2844 const auto it = portIndices.find (symbol);
2845 return it != portIndices.cend() ? it->second : LV2UI_INVALID_PORT_INDEX;
2846 }
2847
2848 static uint32_t portIndexCallback (LV2UI_Feature_Handle handle, const char* symbol)
2849 {
2850 return static_cast<const UiFeaturesData*> (handle)->portIndexCallback (symbol);
2851 }
2852
2853 void touchCallback (uint32_t portIndex, bool grabbed) const
2854 {
2855 touchListener.controlGrabbed (portIndex, grabbed);
2856 }
2857
2858 static void touchCallback (LV2UI_Feature_Handle handle, uint32_t index, bool b)
2859 {
2860 return static_cast<const UiFeaturesData*> (handle)->touchCallback (index, b);
2861 }
2862
2863 static std::map<String, uint32_t> makePortIndices (const Ports& ports)
2864 {
2865 std::map<String, uint32_t> result;
2866
2867 ports.forEachPort ([&] (const PortHeader& header)
2868 {
2869 const auto emplaced = result.emplace (header.symbol, header.index);
2870
2871 // This will complain if there are duplicate port symbols.
2872 jassert (emplaced.second);
2873 ignoreUnused (emplaced);
2874 });
2875
2876 return result;
2877 }
2878
2879 const UiFeaturesDataOptions opts;
2880 PhysicalResizeListener& resizeListener;
2881 TouchListener& touchListener;
2882 LV2_Handle instance{};
2884 SymbolMap& symap;
2885 const UsefulUrids urids { symap };
2886 Log log { &urids };
2887 int lastRequestedWidth = 0, lastRequestedHeight = 0;
2888 std::vector<LV2_Options_Option> options { { LV2_OPTIONS_INSTANCE,
2889 0,
2890 symap.map (LV2_UI__scaleFactor),
2891 sizeof (float),
2892 symap.map (LV2_ATOM__Float),
2893 &opts.initialScaleFactor },
2895 0,
2896 symap.map (LV2_PARAMETERS__sampleRate),
2897 sizeof (float),
2898 symap.map (LV2_ATOM__Float),
2899 &opts.sampleRate },
2900 { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, nullptr } }; // The final entry must be nulled out
2901 LV2UI_Resize resize { this, resizeCallback };
2902 LV2_URID_Map map = symap.getMapFeature();
2903 LV2_URID_Unmap unmap = symap.getUnmapFeature();
2904 LV2UI_Port_Map portMap { this, portIndexCallback };
2905 LV2UI_Touch touch { this, touchCallback };
2906 LV2_Extension_Data_Feature dataAccess;
2907 std::map<String, uint32_t> portIndices;
2908 Features features { makeFeatures (&resize,
2909 parent,
2910 instance,
2911 &dataAccess,
2912 &map,
2913 &unmap,
2914 &portMap,
2915 &touch,
2916 options.data(),
2917 log.getLogFeature()) };
2918
2919 JUCE_LEAK_DETECTOR (UiFeaturesData)
2920};
2921
2922class UiInstanceWithSupports
2923{
2924public:
2925 UiInstanceWithSupports (World& world,
2926 PhysicalResizeListener& resizeListener,
2927 TouchListener& touchListener,
2928 const UiDescriptor* descriptor,
2929 const UiInstanceArgs& args,
2931 InstanceWithSupports& engineInstance,
2932 const UiFeaturesDataOptions& opts)
2933 : features (resizeListener,
2934 touchListener,
2935 engineInstance.instance.getHandle(),
2936 parent,
2937 engineInstance.instance.getExtensionDataCallback(),
2938 engineInstance.ports,
2939 *engineInstance.symap,
2940 opts),
2941 instance (world,
2942 descriptor,
2943 args,
2944 features.getFeatureArray(),
2945 engineInstance.uiToProcessor,
2946 *engineInstance.symap,
2947 resizeListener)
2948 {}
2949
2950 UiFeaturesData features;
2951 UiInstance instance;
2952
2953 JUCE_LEAK_DETECTOR (UiInstanceWithSupports)
2954};
2955
2956struct RequiredFeatures
2957{
2958 explicit RequiredFeatures (OwningNodes nodes)
2959 : values (std::move (nodes)) {}
2960
2961 OwningNodes values;
2962};
2963
2964struct OptionalFeatures
2965{
2966 explicit OptionalFeatures (OwningNodes nodes)
2967 : values (std::move (nodes)) {}
2968
2969 OwningNodes values;
2970};
2971
2972template <typename Range, typename Predicate>
2973static bool noneOf (Range&& range, Predicate&& pred)
2974{
2975 // Not a mistake, this is for ADL
2976 using std::begin;
2977 using std::end;
2978 return std::none_of (begin (range), end (range), std::forward<Predicate> (pred));
2979}
2980
2981class PeerChangedListener : private ComponentMovementWatcher
2982{
2983public:
2984 PeerChangedListener (Component& c, std::function<void()> peerChangedIn)
2985 : ComponentMovementWatcher (&c), peerChanged (std::move (peerChangedIn))
2986 {
2987 }
2988
2989 void componentMovedOrResized (bool, bool) override {}
2990 void componentPeerChanged() override { NullCheckedInvocation::invoke (peerChanged); }
2991 void componentVisibilityChanged() override {}
2992
2993 using ComponentMovementWatcher::componentVisibilityChanged;
2994 using ComponentMovementWatcher::componentMovedOrResized;
2995
2996private:
2997 std::function<void()> peerChanged;
2998};
2999
3000struct ViewSizeListener : private ComponentMovementWatcher
3001{
3002 ViewSizeListener (Component& c, PhysicalResizeListener& l)
3003 : ComponentMovementWatcher (&c), listener (l)
3004 {
3005 }
3006
3007 void componentMovedOrResized (bool, bool wasResized) override
3008 {
3009 if (wasResized)
3010 {
3011 const auto physicalSize = Desktop::getInstance().getDisplays()
3012 .logicalToPhysical (getComponent()->localAreaToGlobal (getComponent()->getLocalBounds()));
3013 const auto width = physicalSize.getWidth();
3014 const auto height = physicalSize.getHeight();
3015
3016 if (width > 10 && height > 10)
3017 listener.viewRequestedResizeInPhysicalPixels (width, height);
3018 }
3019 }
3020
3021 void componentPeerChanged() override {}
3022 void componentVisibilityChanged() override {}
3023
3024 using ComponentMovementWatcher::componentVisibilityChanged;
3025 using ComponentMovementWatcher::componentMovedOrResized;
3026
3027 PhysicalResizeListener& listener;
3028};
3029
3030class ConfiguredEditorComponent : public Component,
3031 private PhysicalResizeListener
3032{
3033public:
3034 ConfiguredEditorComponent (World& world,
3035 InstanceWithSupports& instance,
3036 UiDescriptor& uiDescriptor,
3037 LogicalResizeListener& resizeListenerIn,
3038 TouchListener& touchListener,
3039 const String& uiBundleUri,
3040 const UiFeaturesDataOptions& opts)
3041 : resizeListener (resizeListenerIn),
3042 floatUrid (instance.symap->map (LV2_ATOM__Float)),
3043 scaleFactorUrid (instance.symap->map (LV2_UI__scaleFactor)),
3044 uiInstance (new UiInstanceWithSupports (world,
3045 *this,
3046 touchListener,
3047 &uiDescriptor,
3048 UiInstanceArgs{}.withBundlePath (bundlePathFromUri (uiBundleUri.toRawUTF8()))
3049 .withPluginUri (URL (instance.instance.getUri())),
3050 viewComponent.getWidget(),
3051 instance,
3052 opts)),
3053 resizeClient (uiInstance->instance.getExtensionData<LV2UI_Resize> (world, LV2_UI__resize)),
3054 optionsInterface (uiInstance->instance.getExtensionData<LV2_Options_Interface> (world, LV2_OPTIONS__interface))
3055 {
3056 jassert (uiInstance != nullptr);
3057
3058 setOpaque (true);
3059 addAndMakeVisible (viewComponent);
3060
3061 const auto boundsToUse = [&]
3062 {
3063 const auto requested = uiInstance->features.getLastRequestedBounds();
3064
3065 if (requested.getWidth() > 10 && requested.getHeight() > 10)
3066 return requested;
3067
3068 return uiInstance->instance.getDetectedViewBounds();
3069 }();
3070
3071 const auto scaled = lv2ToComponentRect (boundsToUse);
3072 lastWidth = scaled.getWidth();
3073 lastHeight = scaled.getHeight();
3074 setSize (lastWidth, lastHeight);
3075 }
3076
3077 ~ConfiguredEditorComponent() override
3078 {
3079 viewComponent.prepareForDestruction();
3080 }
3081
3082 void paint (Graphics& g) override
3083 {
3084 g.fillAll (Colours::black);
3085 }
3086
3087 void resized() override
3088 {
3089 viewComponent.setBounds (getLocalBounds());
3090 }
3091
3092 void updateViewBounds()
3093 {
3094 // If the editor changed size as a result of a request from the client,
3095 // we shouldn't send a notification back to the client.
3096 if (uiInstance != nullptr)
3097 {
3098 if (resizeClient.valid && resizeClient.extension.ui_resize != nullptr)
3099 {
3100 const auto physicalSize = componentToLv2Rect (getLocalBounds());
3101
3102 resizeClient.extension.ui_resize (uiInstance->instance.getHandle(),
3103 physicalSize.getWidth(),
3104 physicalSize.getHeight());
3105 }
3106 }
3107 }
3108
3109 void pushMessage (MessageHeader header, uint32_t size, const void* buffer)
3110 {
3111 if (uiInstance != nullptr)
3112 uiInstance->instance.pushMessage (header, size, buffer);
3113 }
3114
3115 int idle()
3116 {
3117 if (uiInstance != nullptr)
3118 return uiInstance->instance.idle();
3119
3120 return 0;
3121 }
3122
3123 void childBoundsChanged (Component* c) override
3124 {
3125 if (c == nullptr)
3126 resizeToFitView();
3127 }
3128
3129 void setUserScaleFactor (float userScale) { userScaleFactor = userScale; }
3130
3131 void sendScaleFactorToPlugin()
3132 {
3133 const auto factor = getEffectiveScale();
3134
3135 const LV2_Options_Option options[]
3136 {
3137 { LV2_OPTIONS_INSTANCE, 0, scaleFactorUrid, sizeof (float), floatUrid, &factor },
3138 { {}, {}, {}, {}, {}, {} }
3139 };
3140
3141 if (optionsInterface.valid)
3142 optionsInterface.extension.set (uiInstance->instance.getHandle(), options);
3143
3144 applyLastRequestedPhysicalSize();
3145 }
3146
3147private:
3148 void viewRequestedResizeInPhysicalPixels (int width, int height) override
3149 {
3150 lastWidth = width;
3151 lastHeight = height;
3152 const auto logical = lv2ToComponentRect ({ width, height });
3153 resizeListener.viewRequestedResizeInLogicalPixels (logical.getWidth(), logical.getHeight());
3154 }
3155
3156 void resizeToFitView()
3157 {
3158 viewComponent.fitToView();
3159 resizeListener.viewRequestedResizeInLogicalPixels (viewComponent.getWidth(), viewComponent.getHeight());
3160 }
3161
3162 void applyLastRequestedPhysicalSize()
3163 {
3164 viewRequestedResizeInPhysicalPixels (lastWidth, lastHeight);
3165 viewComponent.forceViewToSize();
3166 }
3167
3168 /* Convert from the component's coordinate system to the hosted LV2's coordinate system. */
3169 Rectangle<int> componentToLv2Rect (Rectangle<int> r) const
3170 {
3171 return localAreaToGlobal (r) * nativeScaleFactor * getDesktopScaleFactor();
3172 }
3173
3174 /* Convert from the hosted LV2's coordinate system to the component's coordinate system. */
3175 Rectangle<int> lv2ToComponentRect (Rectangle<int> vr) const
3176 {
3177 return getLocalArea (nullptr, vr / (nativeScaleFactor * getDesktopScaleFactor()));
3178 }
3179
3180 float getEffectiveScale() const { return nativeScaleFactor * userScaleFactor; }
3181
3182 // If possible, try to keep platform-specific handing restricted to the implementation of
3183 // ViewComponent. Keep the interface of ViewComponent consistent on all platforms.
3184 #if JUCE_LINUX || JUCE_BSD
3185 struct InnerHolder
3186 {
3187 struct Inner : public XEmbedComponent
3188 {
3189 Inner() : XEmbedComponent (true, true)
3190 {
3191 setOpaque (true);
3192 addToDesktop (0);
3193 }
3194 };
3195
3196 Inner inner;
3197 };
3198
3199 struct ViewComponent : public InnerHolder,
3200 public XEmbedComponent
3201 {
3202 explicit ViewComponent (PhysicalResizeListener& l)
3203 : XEmbedComponent ((unsigned long) inner.getPeer()->getNativeHandle(), true, false),
3204 listener (inner, l)
3205 {
3206 setOpaque (true);
3207 }
3208
3209 ~ViewComponent()
3210 {
3211 removeClient();
3212 }
3213
3214 void prepareForDestruction()
3215 {
3216 inner.removeClient();
3217 }
3218
3219 LV2UI_Widget getWidget() { return lv2_shared::wordCast<LV2UI_Widget> (inner.getHostWindowID()); }
3220 void forceViewToSize() {}
3221 void fitToView() {}
3222
3223 ViewSizeListener listener;
3224 };
3225 #elif JUCE_MAC
3226 struct ViewComponent : public NSViewComponentWithParent
3227 {
3228 explicit ViewComponent (PhysicalResizeListener&)
3229 : NSViewComponentWithParent (WantsNudge::no) {}
3230 LV2UI_Widget getWidget() { return getView(); }
3231 void forceViewToSize() {}
3232 void fitToView() { resizeToFitView(); }
3233 void prepareForDestruction() {}
3234 };
3235 #elif JUCE_WINDOWS
3236 struct ViewComponent : public HWNDComponent
3237 {
3238 explicit ViewComponent (PhysicalResizeListener&)
3239 {
3240 setOpaque (true);
3241 inner.addToDesktop (0);
3242
3243 if (auto* peer = inner.getPeer())
3244 setHWND (peer->getNativeHandle());
3245 }
3246
3247 void paint (Graphics& g) override { g.fillAll (Colours::black); }
3248
3249 LV2UI_Widget getWidget() { return getHWND(); }
3250
3251 void forceViewToSize() { updateHWNDBounds(); }
3252 void fitToView() { resizeToFit(); }
3253
3254 void prepareForDestruction() {}
3255
3256 private:
3257 struct Inner : public Component
3258 {
3259 Inner() { setOpaque (true); }
3260 void paint (Graphics& g) override { g.fillAll (Colours::black); }
3261 };
3262
3263 Inner inner;
3264 };
3265 #else
3266 struct ViewComponent : public Component
3267 {
3268 explicit ViewComponent (PhysicalResizeListener&) {}
3269 void* getWidget() { return nullptr; }
3270 void forceViewToSize() {}
3271 void fitToView() {}
3272 void prepareForDestruction() {}
3273 };
3274 #endif
3275
3276 struct ScaleNotifierCallback
3277 {
3278 ConfiguredEditorComponent& window;
3279
3280 void operator() (float platformScale) const
3281 {
3282 MessageManager::callAsync ([ref = Component::SafePointer<ConfiguredEditorComponent> (&window), platformScale]
3283 {
3284 if (auto* r = ref.getComponent())
3285 {
3286 if (std::exchange (r->nativeScaleFactor, platformScale) == platformScale)
3287 return;
3288
3289 r->nativeScaleFactor = platformScale;
3290 r->sendScaleFactorToPlugin();
3291 }
3292 });
3293 }
3294 };
3295
3296 LogicalResizeListener& resizeListener;
3297 int lastWidth = 0, lastHeight = 0;
3298 float nativeScaleFactor = 1.0f, userScaleFactor = 1.0f;
3299 NativeScaleFactorNotifier scaleNotifier { this, ScaleNotifierCallback { *this } };
3300 ViewComponent viewComponent { *this };
3301 LV2_URID floatUrid, scaleFactorUrid;
3302 std::unique_ptr<UiInstanceWithSupports> uiInstance;
3303 OptionalExtension<LV2UI_Resize> resizeClient;
3304 OptionalExtension<LV2_Options_Interface> optionsInterface;
3305 PeerChangedListener peerListener { *this, [this]
3306 {
3307 applyLastRequestedPhysicalSize();
3308 } };
3309
3310 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConfiguredEditorComponent)
3311};
3312
3313//==============================================================================
3314/* Interface to receive notifications when the Editor changes. */
3315struct EditorListener
3316{
3317 virtual ~EditorListener() = default;
3318
3319 /* The editor needs to be recreated in a few different scenarios, such as:
3320 - When the scale factor of the window changes, because we can only provide the
3321 scale factor to the view during construction
3322 - When the sample rate changes, because the processor also needs to be destroyed
3323 and recreated in this case
3324
3325 This function will be called whenever the editor has been recreated, in order to
3326 allow the processor (or other listeners) to respond, e.g. by sending all of the
3327 current port/parameter values to the view.
3328 */
3329 virtual void viewCreated (UiEventListener* newListener) = 0;
3330
3331 virtual void notifyEditorBeingDeleted() = 0;
3332};
3333
3334/* We can't pass the InstanceWithSupports directly to the editor, because
3335 it might be destroyed and reconstructed if the sample rate changes.
3336*/
3337struct InstanceProvider
3338{
3339 virtual ~InstanceProvider() noexcept = default;
3340
3341 virtual InstanceWithSupports* getInstanceWithSupports() const = 0;
3342};
3343
3344class Editor : public AudioProcessorEditor,
3345 public UiEventListener,
3346 private LogicalResizeListener
3347{
3348public:
3349 Editor (World& worldIn,
3350 AudioPluginInstance& p,
3351 InstanceProvider& instanceProviderIn,
3352 UiDescriptor& uiDescriptorIn,
3353 TouchListener& touchListenerIn,
3354 EditorListener& listenerIn,
3355 const String& uiBundleUriIn,
3356 RequiredFeatures requiredIn,
3357 OptionalFeatures optionalIn)
3358 : AudioProcessorEditor (p),
3359 world (worldIn),
3360 instanceProvider (&instanceProviderIn),
3361 uiDescriptor (&uiDescriptorIn),
3362 touchListener (&touchListenerIn),
3363 listener (&listenerIn),
3364 uiBundleUri (uiBundleUriIn),
3365 required (std::move (requiredIn)),
3366 optional (std::move (optionalIn))
3367 {
3368 setResizable (isResizable (required, optional), false);
3369 setSize (10, 10);
3370 setOpaque (true);
3371
3372 createView();
3373
3374 instanceProvider->getInstanceWithSupports()->processorToUi->addUi (*this);
3375 }
3376
3377 ~Editor() noexcept override
3378 {
3379 instanceProvider->getInstanceWithSupports()->processorToUi->removeUi (*this);
3380
3381 listener->notifyEditorBeingDeleted();
3382 }
3383
3384 void createView()
3385 {
3386 const auto initialScale = userScaleFactor * (float) [&]
3387 {
3388 if (auto* p = getPeer())
3389 return p->getPlatformScaleFactor();
3390
3391 return 1.0;
3392 }();
3393
3394 const auto opts = UiFeaturesDataOptions{}.withInitialScaleFactor (initialScale)
3395 .withSampleRate ((float) processor.getSampleRate());
3396 configuredEditor = nullptr;
3397 configuredEditor = rawToUniquePtr (new ConfiguredEditorComponent (world,
3398 *instanceProvider->getInstanceWithSupports(),
3399 *uiDescriptor,
3400 *this,
3401 *touchListener,
3402 uiBundleUri,
3403 opts));
3404 parentHierarchyChanged();
3405 const auto initialSize = configuredEditor->getBounds();
3406 setSize (initialSize.getWidth(), initialSize.getHeight());
3407
3408 listener->viewCreated (this);
3409 }
3410
3411 void destroyView()
3412 {
3413 configuredEditor = nullptr;
3414 }
3415
3416 void paint (Graphics& g) override
3417 {
3418 g.fillAll (Colours::black);
3419 }
3420
3421 void resized() override
3422 {
3423 const ScopedValueSetter<bool> scope (resizeFromHost, true);
3424
3425 if (auto* inner = configuredEditor.get())
3426 {
3427 inner->setBounds (getLocalBounds());
3428 inner->updateViewBounds();
3429 }
3430 }
3431
3432 void parentHierarchyChanged() override
3433 {
3434 if (auto* comp = configuredEditor.get())
3435 {
3436 if (isShowing())
3437 addAndMakeVisible (comp);
3438 else
3439 removeChildComponent (comp);
3440 }
3441 }
3442
3443 void pushMessage (MessageHeader header, uint32_t size, const void* buffer) override
3444 {
3445 if (auto* comp = configuredEditor.get())
3446 comp->pushMessage (header, size, buffer);
3447 }
3448
3449 int idle() override
3450 {
3451 if (auto* comp = configuredEditor.get())
3452 return comp->idle();
3453
3454 return 0;
3455 }
3456
3457 void setScaleFactor (float newScale) override
3458 {
3459 userScaleFactor = newScale;
3460
3461 if (configuredEditor != nullptr)
3462 {
3463 configuredEditor->setUserScaleFactor (userScaleFactor);
3464 configuredEditor->sendScaleFactorToPlugin();
3465 }
3466 }
3467
3468private:
3469 bool isResizable (const RequiredFeatures& requiredFeatures,
3470 const OptionalFeatures& optionalFeatures) const
3471 {
3472 const auto uriMatches = [] (const LilvNode* node)
3473 {
3474 const auto* uri = lilv_node_as_uri (node);
3475 return std::strcmp (uri, LV2_UI__noUserResize) == 0;
3476 };
3477
3478 return uiDescriptor->hasExtensionData (world, LV2_UI__resize)
3479 && ! uiDescriptor->hasExtensionData (world, LV2_UI__noUserResize)
3480 && noneOf (requiredFeatures.values, uriMatches)
3481 && noneOf (optionalFeatures.values, uriMatches);
3482 }
3483
3484 bool isScalable() const
3485 {
3486 return uiDescriptor->hasExtensionData (world, LV2_OPTIONS__interface);
3487 }
3488
3489 void viewRequestedResizeInLogicalPixels (int width, int height) override
3490 {
3491 if (! resizeFromHost)
3492 setSize (width, height);
3493 }
3494
3495 World& world;
3496 InstanceProvider* instanceProvider;
3497 UiDescriptor* uiDescriptor;
3498 TouchListener* touchListener;
3499 EditorListener* listener;
3500 String uiBundleUri;
3501 const RequiredFeatures required;
3502 const OptionalFeatures optional;
3503 std::unique_ptr<ConfiguredEditorComponent> configuredEditor;
3504 float userScaleFactor = 1.0f;
3505 bool resizeFromHost = false;
3506
3508};
3509
3510class Uis
3511{
3512public:
3513 explicit Uis (const LilvPlugin* plugin) noexcept : uis (lilv_plugin_get_uis (plugin)) {}
3514
3515 unsigned size() const noexcept { return lilv_uis_size (uis.get()); }
3516
3517 UisIterator begin() const noexcept { return UisIterator { uis.get() }; }
3518 UisIterator end() const noexcept { return UisIterator{}; }
3519
3520 const LilvUI* getByUri (const NodeUri& uri) const
3521 {
3522 return lilv_uis_get_by_uri (uis.get(), uri.get());
3523 }
3524
3525private:
3526 struct Free
3527 {
3528 void operator() (LilvUIs* ptr) const noexcept { lilv_uis_free (ptr); }
3529 };
3530
3531 std::unique_ptr<LilvUIs, Free> uis;
3532};
3533
3534//==============================================================================
3535class PluginClass
3536{
3537public:
3538 explicit PluginClass (const LilvPluginClass* c) : pluginClass (c) {}
3539
3540 NodeUri getParentUri() const noexcept { return NodeUri::copy (lilv_plugin_class_get_parent_uri (pluginClass)); }
3541 NodeUri getUri() const noexcept { return NodeUri::copy (lilv_plugin_class_get_uri (pluginClass)); }
3542 NodeString getLabel() const noexcept { return NodeString::copy (lilv_plugin_class_get_label (pluginClass)); }
3543 OwningPluginClasses getChildren() const noexcept
3544 {
3545 return OwningPluginClasses { OwningPluginClasses::type { lilv_plugin_class_get_children (pluginClass) } };
3546 }
3547
3548private:
3549 const LilvPluginClass* pluginClass = nullptr;
3550};
3551
3552using FloatWriter = void (*) (LV2_Atom_Forge*, float);
3553
3554struct ParameterWriterUrids
3555{
3556 LV2_URID mLV2_PATCH__Set;
3557 LV2_URID mLV2_PATCH__property;
3558 LV2_URID mLV2_PATCH__value;
3559 LV2_URID mLV2_ATOM__eventTransfer;
3560};
3561
3562struct MessageHeaderAndSize
3563{
3564 MessageHeader header;
3565 uint32_t size;
3566};
3567
3568class ParameterWriter
3569{
3570public:
3571 ParameterWriter (ControlPort* p)
3572 : data (PortBacking { p }), kind (Kind::port) {}
3573
3574 ParameterWriter (FloatWriter write, LV2_URID urid, uint32_t controlPortIndex)
3575 : data (PatchBacking { write, urid, controlPortIndex }), kind (Kind::patch) {}
3576
3577 void writeToProcessor (const ParameterWriterUrids urids, LV2_Atom_Forge* forge, float value) const
3578 {
3579 switch (kind)
3580 {
3581 case Kind::patch:
3582 {
3583 if (forge != nullptr)
3584 {
3585 lv2_atom_forge_frame_time (forge, 0);
3586 writeSetToForge (urids, *forge, value);
3587 }
3588
3589 break;
3590 }
3591
3592 case Kind::port:
3593 data.port.port->currentValue = value;
3594 break;
3595 }
3596 }
3597
3598 MessageHeaderAndSize writeToUi (const ParameterWriterUrids urids, LV2_Atom_Forge& forge, float value) const
3599 {
3600 const auto getWrittenBytes = [&]() -> uint32_t
3601 {
3602 if (const auto* atom = convertToAtomPtr (forge.buf, forge.size))
3603 return (uint32_t) (atom->size + sizeof (LV2_Atom));
3604
3606 return 0;
3607 };
3608
3609 switch (kind)
3610 {
3611 case Kind::patch:
3612 writeSetToForge (urids, forge, value);
3613 return { { data.patch.controlPortIndex, urids.mLV2_ATOM__eventTransfer }, getWrittenBytes() };
3614
3615 case Kind::port:
3616 lv2_atom_forge_raw (&forge, &value, sizeof (value));
3617 return { { data.port.port->header.index, 0 }, sizeof (value) };
3618 }
3619
3620 return { { 0, 0 }, 0 };
3621 }
3622
3623 const LV2_URID* getUrid() const
3624 {
3625 return kind == Kind::patch ? &data.patch.urid : nullptr;
3626 }
3627
3628 const uint32_t* getPortIndex() const
3629 {
3630 return kind == Kind::port ? &data.port.port->header.index : nullptr;
3631 }
3632
3633private:
3634 void writeSetToForge (const ParameterWriterUrids urids, LV2_Atom_Forge& forge, float value) const
3635 {
3636 lv2_shared::ObjectFrame object { &forge, (uint32_t) 0, urids.mLV2_PATCH__Set };
3637
3638 lv2_atom_forge_key (&forge, urids.mLV2_PATCH__property);
3639 lv2_atom_forge_urid (&forge, data.patch.urid);
3640
3641 lv2_atom_forge_key (&forge, urids.mLV2_PATCH__value);
3642 data.patch.write (&forge, value);
3643 }
3644
3645 struct PortBacking
3646 {
3647 ControlPort* port;
3648 };
3649
3650 struct PatchBacking
3651 {
3652 FloatWriter write;
3653 LV2_URID urid;
3654 uint32_t controlPortIndex;
3655 };
3656
3657 union Data
3658 {
3659 static_assert (std::is_trivial<PortBacking>::value, "PortBacking must be trivial");
3660 static_assert (std::is_trivial<PatchBacking>::value, "PatchBacking must be trivial");
3661
3662 explicit Data (PortBacking p) : port (p) {}
3663 explicit Data (PatchBacking p) : patch (p) {}
3664
3665 PortBacking port;
3666 PatchBacking patch;
3667 };
3668
3669 enum class Kind { port, patch };
3670
3671 Data data;
3672 Kind kind;
3673
3674 JUCE_LEAK_DETECTOR (ParameterWriter)
3675};
3676
3677static String lilvNodeToUriString (const LilvNode* node)
3678{
3679 return node != nullptr ? String::fromUTF8 (lilv_node_as_uri (node)) : String{};
3680}
3681
3682static String lilvNodeToString (const LilvNode* node)
3683{
3684 return node != nullptr ? String::fromUTF8 (lilv_node_as_string (node)) : String{};
3685}
3686
3687/* This holds all of the discovered groups in the plugin's manifest, and allows us to
3688 add parameters to these groups as we discover them.
3689
3690 Once all the parameters have been added with addParameter(), you can call
3691 getTree() to convert this class' contents (which are optimised for fast lookup
3692 and modification) into a plain old AudioProcessorParameterGroup.
3693*/
3694class IntermediateParameterTree
3695{
3696public:
3697 explicit IntermediateParameterTree (World& worldIn)
3698 : world (worldIn)
3699 {
3700 const auto groups = getGroups (world);
3701 const auto symbolNode = world.newUri (LV2_CORE__symbol);
3702 const auto nameNode = world.newUri (LV2_CORE__name);
3703
3704 for (const auto& group : groups)
3705 {
3706 const auto symbol = lilvNodeToString (world.get (group.get(), symbolNode.get(), nullptr).get());
3707 const auto name = lilvNodeToString (world.get (group.get(), nameNode .get(), nullptr).get());
3708 owning.emplace (lilvNodeToUriString (group.get()),
3709 std::make_unique<AudioProcessorParameterGroup> (symbol, name, "|"));
3710 }
3711 }
3712
3713 void addParameter (StringRef group, std::unique_ptr<LV2Parameter> param)
3714 {
3715 if (param == nullptr)
3716 return;
3717
3718 const auto it = owning.find (group);
3719 (it != owning.cend() ? *it->second : topLevel).addChild (std::move (param));
3720 }
3721
3722 static AudioProcessorParameterGroup getTree (IntermediateParameterTree tree)
3723 {
3724 std::map<String, AudioProcessorParameterGroup*> nonowning;
3725
3726 for (const auto& pair : tree.owning)
3727 nonowning.emplace (pair.first, pair.second.get());
3728
3729 const auto groups = getGroups (tree.world);
3730 const auto subgroupNode = tree.world.newUri (LV2_PORT_GROUPS__subGroupOf);
3731
3732 for (const auto& group : groups)
3733 {
3734 const auto innerIt = tree.owning.find (lilvNodeToUriString (group.get()));
3735
3736 if (innerIt == tree.owning.cend())
3737 continue;
3738
3739 const auto outer = lilvNodeToUriString (tree.world.get (group.get(), subgroupNode.get(), nullptr).get());
3740 const auto outerIt = nonowning.find (outer);
3741
3742 if (outerIt != nonowning.cend() && containsParameters (outerIt->second))
3743 outerIt->second->addChild (std::move (innerIt->second));
3744 }
3745
3746 for (auto& subgroup : tree.owning)
3747 if (containsParameters (subgroup.second.get()))
3748 tree.topLevel.addChild (std::move (subgroup.second));
3749
3750 return std::move (tree.topLevel);
3751 }
3752
3753private:
3754 static std::vector<OwningNode> getGroups (World& world)
3755 {
3756 std::vector<OwningNode> names;
3757
3759 for (const auto* group : world.findNodes (nullptr, world.newUri (LILV_NS_RDF "type").get(), world.newUri (uri).get()))
3760 names.push_back (OwningNode { lilv_node_duplicate (group) });
3761
3762 return names;
3763 }
3764
3765 static bool containsParameters (const AudioProcessorParameterGroup* g)
3766 {
3767 if (g == nullptr)
3768 return false;
3769
3770 for (auto* node : *g)
3771 {
3772 if (node->getParameter() != nullptr)
3773 return true;
3774
3775 if (auto* group = node->getGroup())
3776 if (containsParameters (group))
3777 return true;
3778 }
3779
3780 return false;
3781 }
3782
3783 World& world;
3784 AudioProcessorParameterGroup topLevel;
3785 std::map<String, std::unique_ptr<AudioProcessorParameterGroup>> owning;
3786
3787 JUCE_LEAK_DETECTOR (IntermediateParameterTree)
3788};
3789
3790struct BypassParameter : public LV2Parameter
3791{
3792 BypassParameter (const ParameterInfo& parameterInfo, ParameterValuesAndFlags& cacheIn)
3793 : LV2Parameter ("Bypass", parameterInfo, cacheIn) {}
3794
3795 float getValue() const noexcept override
3796 {
3797 return LV2Parameter::getValue() > 0.0f ? 0.0f : 1.0f;
3798 }
3799
3800 void setValue (float newValue) override
3801 {
3802 LV2Parameter::setValue (newValue > 0.0f ? 0.0f : 1.0f);
3803 }
3804
3805 float getDefaultValue() const override { return 0.0f; }
3806 bool isAutomatable() const override { return true; }
3807 bool isDiscrete() const override { return true; }
3808 bool isBoolean() const override { return true; }
3809 int getNumSteps() const override { return 2; }
3810 StringArray getAllValueStrings() const override { return { TRANS ("Off"), TRANS ("On") }; }
3811};
3812
3813struct ParameterData
3814{
3815 ParameterInfo info;
3816 ParameterWriter writer;
3817 String group;
3818 String name;
3819};
3820
3821template <typename T>
3822static auto getPortPointers (SimpleSpan<T> range)
3823{
3824 using std::begin;
3825 std::vector<decltype (&(*begin (range)))> result;
3826
3827 for (auto& port : range)
3828 {
3829 result.resize (std::max ((size_t) (port.header.index + 1), result.size()), nullptr);
3830 result[port.header.index] = &port;
3831 }
3832
3833 return result;
3834}
3835
3836static std::unique_ptr<LV2Parameter> makeParameter (const uint32_t* enabledPortIndex,
3837 const ParameterData& data,
3838 ParameterValuesAndFlags& cache)
3839{
3840 // The bypass parameter is a bit special, in that JUCE expects the parameter to be a bypass
3841 // (where 0 is active, 1 is inactive), but the LV2 version is called "enabled" and has
3842 // different semantics (0 is inactive, 1 is active).
3843 // To work around this, we wrap the LV2 parameter in a special inverting JUCE parameter.
3844
3845 if (enabledPortIndex != nullptr)
3846 if (auto* index = data.writer.getPortIndex())
3847 if (*index == *enabledPortIndex)
3848 return std::make_unique<BypassParameter> (data.info, cache);
3849
3850 return std::make_unique<LV2Parameter> (data.name, data.info, cache);
3851}
3852
3853class ControlPortAccelerationStructure
3854{
3855public:
3856 ControlPortAccelerationStructure (SimpleSpan<ControlPort> controlPorts)
3857 : indexedControlPorts (getPortPointers (controlPorts))
3858 {
3859 for (const auto& port : controlPorts)
3860 if (port.header.direction == Port::Direction::output)
3861 outputPorts.push_back (&port);
3862 }
3863
3864 const std::vector<ControlPort*>& getIndexedControlPorts() { return indexedControlPorts; }
3865
3866 ControlPort* getControlPortByIndex (uint32_t index) const
3867 {
3868 if (isPositiveAndBelow (index, indexedControlPorts.size()))
3869 return indexedControlPorts[index];
3870
3871 return nullptr;
3872 }
3873
3874 void writeOutputPorts (UiEventListener* target, MessageBufferInterface<UiMessageHeader>& uiMessages) const
3875 {
3876 if (target == nullptr)
3877 return;
3878
3879 for (const auto* port : outputPorts)
3880 {
3881 const auto chars = toChars (port->currentValue);
3882 uiMessages.pushMessage ({ target, { port->header.index, 0 } }, (uint32_t) chars.size(), chars.data());
3883 }
3884 }
3885
3886private:
3887 std::vector<ControlPort*> indexedControlPorts;
3888 std::vector<const ControlPort*> outputPorts;
3889};
3890
3891class ParameterValueCache
3892{
3893public:
3894 /* This takes some information about all the parameters that this plugin wants to expose,
3895 then builds and installs the actual parameters.
3896 */
3897 ParameterValueCache (AudioPluginInstance& processor,
3898 World& world,
3899 LV2_URID_Map mapFeature,
3900 const std::vector<ParameterData>& data,
3901 ControlPort* enabledPort)
3902 : uiForge (mapFeature),
3903 cache (data.size())
3904 {
3905 // Parameter indices are unknown until we add the parameters to the processor.
3906 // This map lets us keep track of which ParameterWriter corresponds to each parameter.
3907 // After the parameters have been added to the processor, we'll convert this
3908 // to a simple vector that stores each ParameterWriter at the same index
3909 // as the corresponding parameter.
3910 std::map<AudioProcessorParameter*, ParameterWriter> writerForParameter;
3911
3912 IntermediateParameterTree tree { world };
3913
3914 const auto* enabledPortIndex = enabledPort != nullptr ? &enabledPort->header.index
3915 : nullptr;
3916
3917 for (const auto& item : data)
3918 {
3919 auto param = makeParameter (enabledPortIndex, item, cache);
3920
3921 if (auto* urid = item.writer.getUrid())
3922 urids.emplace (*urid, param.get());
3923
3924 if (auto* index = item.writer.getPortIndex())
3925 portIndices.emplace (*index, param.get());
3926
3927 writerForParameter.emplace (param.get(), item.writer);
3928
3929 tree.addParameter (item.group, std::move (param));
3930 }
3931
3932 processor.setHostedParameterTree (IntermediateParameterTree::getTree (std::move (tree)));
3933
3934 // Build the vector of writers
3935 writers.reserve (data.size());
3936
3937 for (auto* param : processor.getParameters())
3938 {
3939 const auto it = writerForParameter.find (param);
3940 jassert (it != writerForParameter.end());
3941 writers.push_back (it->second); // The writer must exist at the same index as the parameter!
3942 }
3943
3944 // Duplicate port indices or urids?
3945 jassert (processor.getParameters().size() == (int) (urids.size() + portIndices.size()));
3946
3947 // Set parameters to default values
3948 const auto setToDefault = [] (auto& container)
3949 {
3950 for (auto& item : container)
3951 item.second->setDenormalisedValueWithoutTriggeringUpdate (item.second->getDenormalisedDefaultValue());
3952 };
3953
3954 setToDefault (urids);
3955 setToDefault (portIndices);
3956 }
3957
3958 void postChangedParametersToProcessor (const ParameterWriterUrids helperUrids,
3959 LV2_Atom_Forge* forge)
3960 {
3961 cache.ifProcessorValuesChanged ([&] (size_t index, float value)
3962 {
3963 writers[index].writeToProcessor (helperUrids, forge, value);
3964 });
3965 }
3966
3967 void postChangedParametersToUi (UiEventListener* target,
3968 const ParameterWriterUrids helperUrids,
3969 MessageBufferInterface<UiMessageHeader>& uiMessages)
3970 {
3971 if (target == nullptr)
3972 return;
3973
3974 cache.ifUiValuesChanged ([&] (size_t index, float value)
3975 {
3976 writeParameterToUi (target, writers[index], value, helperUrids, uiMessages);
3977 });
3978 }
3979
3980 void postAllParametersToUi (UiEventListener* target,
3981 const ParameterWriterUrids helperUrids,
3982 MessageBufferInterface<UiMessageHeader>& uiMessages)
3983 {
3984 if (target == nullptr)
3985 return;
3986
3987 const auto numWriters = writers.size();
3988
3989 for (size_t i = 0; i < numWriters; ++i)
3990 writeParameterToUi (target, writers[i], cache.get (i), helperUrids, uiMessages);
3991
3992 cache.clearUiFlags();
3993 }
3994
3995 LV2Parameter* getParamByUrid (LV2_URID urid) const
3996 {
3997 const auto it = urids.find (urid);
3998 return it != urids.end() ? it->second : nullptr;
3999 }
4000
4001 LV2Parameter* getParamByPortIndex (uint32_t portIndex) const
4002 {
4003 const auto it = portIndices.find (portIndex);
4004 return it != portIndices.end() ? it->second : nullptr;
4005 }
4006
4007 void updateFromControlPorts (const ControlPortAccelerationStructure& ports) const
4008 {
4009 for (const auto& pair : portIndices)
4010 if (auto* port = ports.getControlPortByIndex (pair.first))
4011 if (auto* param = pair.second)
4012 param->setDenormalisedValueWithoutTriggeringUpdate (port->currentValue);
4013 }
4014
4015private:
4016 void writeParameterToUi (UiEventListener* target,
4017 const ParameterWriter& writer,
4018 float value,
4019 const ParameterWriterUrids helperUrids,
4020 MessageBufferInterface<UiMessageHeader>& uiMessages)
4021 {
4023
4024 uiForge.setBuffer (forgeStorage.data(), forgeStorage.size());
4025 const auto messageHeader = writer.writeToUi (helperUrids, *uiForge.get(), value);
4026 uiMessages.pushMessage ({ target, messageHeader.header }, messageHeader.size, forgeStorage.data());
4027 }
4028
4029 SingleSizeAlignedStorage<8> forgeStorage { 256 };
4030 lv2_shared::AtomForge uiForge;
4031
4032 ParameterValuesAndFlags cache;
4033 std::vector<ParameterWriter> writers;
4034 std::map<LV2_URID, LV2Parameter*> urids;
4035 std::map<uint32_t, LV2Parameter*> portIndices;
4036
4037 JUCE_LEAK_DETECTOR (ParameterValueCache)
4038};
4039
4040struct PatchSetCallback
4041{
4042 explicit PatchSetCallback (ParameterValueCache& x) : cache (x) {}
4043
4044 // If we receive a patch set from the processor, we can assume that the UI will
4045 // put itself into the correct state when it receives the message.
4046 void setParameter (LV2_URID property, float value) const noexcept
4047 {
4048 if (auto* param = cache.getParamByUrid (property))
4049 param->setDenormalisedValueWithoutTriggeringUpdate (value);
4050 }
4051
4052 // TODO gesture support will probably go here, once it's part of the LV2 spec
4053
4054 ParameterValueCache& cache;
4055};
4056
4057struct SupportedParameter
4058{
4059 ParameterInfo info;
4060 bool supported;
4061 LV2_URID type;
4062};
4063
4064static SupportedParameter getInfoForPatchParameter (World& worldIn,
4065 const UsefulUrids& urids,
4066 const NodeUri& property)
4067{
4068 const auto rangeUri = worldIn.newUri (LILV_NS_RDFS "range");
4069 const auto type = worldIn.get (property.get(), rangeUri.get(), nullptr);
4070
4071 if (type == nullptr)
4072 return { {}, false, {} };
4073
4074 const auto typeUrid = urids.symap.map (lilv_node_as_uri (type.get()));
4075
4076 const LV2_URID types[] { urids.mLV2_ATOM__Int,
4077 urids.mLV2_ATOM__Long,
4078 urids.mLV2_ATOM__Float,
4079 urids.mLV2_ATOM__Double,
4080 urids.mLV2_ATOM__Bool };
4081
4082 if (std::find (std::begin (types), std::end (types), typeUrid) == std::end (types))
4083 return { {}, false, {} };
4084
4085 const auto getValue = [&] (const char* uri, float fallback)
4086 {
4087 return Port::getFloatValue (worldIn.get (property.get(), worldIn.newUri (uri).get(), nullptr).get(), fallback);
4088 };
4089
4090 const auto hasPortProperty = [&] (const char* uri)
4091 {
4092 return worldIn.ask (property.get(),
4093 worldIn.newUri (LV2_CORE__portProperty).get(),
4094 worldIn.newUri (uri).get());
4095 };
4096
4097 const auto metadataScalePoints = worldIn.findNodes (property.get(),
4098 worldIn.newUri (LV2_CORE__scalePoint).get(),
4099 nullptr);
4100 SafeSortedSet<StoredScalePoint> parsedScalePoints;
4101
4102 for (const auto* scalePoint : metadataScalePoints)
4103 {
4104 const auto label = worldIn.get (scalePoint, worldIn.newUri (LILV_NS_RDFS "label").get(), nullptr);
4105 const auto value = worldIn.get (scalePoint, worldIn.newUri (LILV_NS_RDF "value").get(), nullptr);
4106
4107 if (label != nullptr && value != nullptr)
4108 parsedScalePoints.insert ({ lilv_node_as_string (label.get()), lilv_node_as_float (value.get()) });
4109 else
4110 jassertfalse; // A ScalePoint must have both a rdfs:label and a rdf:value
4111 }
4112
4113 const auto minimum = getValue (LV2_CORE__minimum, 0.0f);
4114 const auto maximum = getValue (LV2_CORE__maximum, 1.0f);
4115
4116 return { { std::move (parsedScalePoints),
4117 "des:" + String::fromUTF8 (property.getTyped()),
4118 getValue (LV2_CORE__default, (minimum + maximum) * 0.5f),
4119 minimum,
4120 maximum,
4121 typeUrid == urids.mLV2_ATOM__Bool || hasPortProperty (LV2_CORE__toggled),
4122 typeUrid == urids.mLV2_ATOM__Int || typeUrid == urids.mLV2_ATOM__Long,
4123 hasPortProperty (LV2_CORE__enumeration) },
4124 true,
4125 typeUrid };
4126}
4127
4128static std::vector<ParameterData> getPortBasedParameters (World& world,
4129 const Plugin& plugin,
4130 std::initializer_list<const ControlPort*> hiddenPorts,
4131 SimpleSpan<ControlPort> controlPorts)
4132{
4133 std::vector<ParameterData> result;
4134
4135 const auto groupNode = world.newUri (LV2_PORT_GROUPS__group);
4136
4137 for (auto& port : controlPorts)
4138 {
4139 if (port.header.direction != Port::Direction::input)
4140 continue;
4141
4142 if (std::find (std::begin (hiddenPorts), std::end (hiddenPorts), &port) != std::end (hiddenPorts))
4143 continue;
4144
4145 const auto lilvPort = plugin.getPortByIndex (port.header.index);
4146 const auto group = lilvNodeToUriString (lilvPort.get (groupNode.get()).get());
4147
4148 result.push_back ({ port.info, ParameterWriter { &port }, group, port.header.name });
4149 }
4150
4151 return result;
4152}
4153
4154static void writeFloatToForge (LV2_Atom_Forge* forge, float value) { lv2_atom_forge_float (forge, value); }
4155static void writeDoubleToForge (LV2_Atom_Forge* forge, float value) { lv2_atom_forge_double (forge, (double) value); }
4156static void writeIntToForge (LV2_Atom_Forge* forge, float value) { lv2_atom_forge_int (forge, (int32_t) value); }
4157static void writeLongToForge (LV2_Atom_Forge* forge, float value) { lv2_atom_forge_long (forge, (int64_t) value); }
4158static void writeBoolToForge (LV2_Atom_Forge* forge, float value) { lv2_atom_forge_bool (forge, value > 0.5f); }
4159
4160static std::vector<ParameterData> getPatchBasedParameters (World& world,
4161 const Plugin& plugin,
4162 const UsefulUrids& urids,
4163 uint32_t controlPortIndex)
4164{
4165 // This returns our writable parameters in an indeterminate order.
4166 // We want our parameters to be in a consistent order between runs, so
4167 // we'll create all the parameters in one pass, sort them, and then
4168 // add them in a separate pass.
4169 const auto writableControls = world.findNodes (plugin.getUri().get(),
4170 world.newUri (LV2_PATCH__writable).get(),
4171 nullptr);
4172
4173 struct DataAndUri
4174 {
4175 ParameterData data;
4176 String uri;
4177 };
4178
4179 std::vector<DataAndUri> resultWithUris;
4180
4181 const auto groupNode = world.newUri (LV2_PORT_GROUPS__group);
4182
4183 for (auto* ctrl : writableControls)
4184 {
4185 const auto labelString = [&]
4186 {
4187 if (auto label = world.get (ctrl, world.newUri (LILV_NS_RDFS "label").get(), nullptr))
4189
4190 return String();
4191 }();
4192
4193 const auto uri = String::fromUTF8 (lilv_node_as_uri (ctrl));
4194 const auto info = getInfoForPatchParameter (world, urids, world.newUri (uri.toRawUTF8()));
4195
4196 if (! info.supported)
4197 continue;
4198
4199 const auto write = [&]
4200 {
4201 if (info.type == urids.mLV2_ATOM__Int)
4202 return writeIntToForge;
4203
4204 if (info.type == urids.mLV2_ATOM__Long)
4205 return writeLongToForge;
4206
4207 if (info.type == urids.mLV2_ATOM__Double)
4208 return writeDoubleToForge;
4209
4210 if (info.type == urids.mLV2_ATOM__Bool)
4211 return writeBoolToForge;
4212
4213 return writeFloatToForge;
4214 }();
4215
4216 const auto group = lilvNodeToUriString (world.get (ctrl, groupNode.get(), nullptr).get());
4217 resultWithUris.push_back ({ { info.info,
4218 ParameterWriter { write, urids.symap.map (uri.toRawUTF8()), controlPortIndex },
4219 group,
4220 labelString },
4221 uri });
4222 }
4223
4224 const auto compareUris = [] (const DataAndUri& a, const DataAndUri& b) { return a.uri < b.uri; };
4225 std::sort (resultWithUris.begin(), resultWithUris.end(), compareUris);
4226
4227 std::vector<ParameterData> result;
4228
4229 for (const auto& item : resultWithUris)
4230 result.push_back (item.data);
4231
4232 return result;
4233}
4234
4235static std::vector<ParameterData> getJuceParameterInfo (World& world,
4236 const Plugin& plugin,
4237 const UsefulUrids& urids,
4238 std::initializer_list<const ControlPort*> hiddenPorts,
4239 SimpleSpan<ControlPort> controlPorts,
4240 uint32_t controlPortIndex)
4241{
4242 auto port = getPortBasedParameters (world, plugin, hiddenPorts, controlPorts);
4243 auto patch = getPatchBasedParameters (world, plugin, urids, controlPortIndex);
4244
4245 port.insert (port.end(), patch.begin(), patch.end());
4246 return port;
4247}
4248
4249// Rather than sprinkle #ifdef everywhere, risking the wrath of the entire C++
4250// standards committee, we put all of our conditionally-compiled stuff into a
4251// specialised template that compiles away to nothing when editor support is
4252// not available.
4253#if JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD
4254 constexpr auto editorFunctionalityEnabled = true;
4255#else
4256 constexpr auto editorFunctionalityEnabled = false;
4257#endif
4258
4259template <bool editorEnabled = editorFunctionalityEnabled> class OptionalEditor;
4260
4261template <>
4262class OptionalEditor<true>
4263{
4264public:
4265 OptionalEditor (String uiBundleUriIn, UiDescriptor uiDescriptorIn, std::function<void()> timerCallback)
4266 : uiBundleUri (std::move (uiBundleUriIn)),
4267 uiDescriptor (std::move (uiDescriptorIn)),
4268 changedParameterFlusher (std::move (timerCallback)) {}
4269
4270 void createView()
4271 {
4272 if (auto* editor = editorPointer.getComponent())
4273 editor->createView();
4274 }
4275
4276 void destroyView()
4277 {
4278 if (auto* editor = editorPointer.getComponent())
4279 editor->destroyView();
4280 }
4281
4282 std::unique_ptr<AudioProcessorEditor> createEditor (World& world,
4283 AudioPluginInstance& p,
4284 InstanceProvider& instanceProviderIn,
4285 TouchListener& touchListenerIn,
4286 EditorListener& listenerIn)
4287 {
4288 if (! hasEditor())
4289 return nullptr;
4290
4291 const auto queryFeatures = [this, &world] (const char* kind)
4292 {
4293 return world.findNodes (world.newUri (uiDescriptor.get()->URI).get(),
4294 world.newUri (kind).get(),
4295 nullptr);
4296 };
4297
4298 auto newEditor = std::make_unique<Editor> (world,
4299 p,
4300 instanceProviderIn,
4301 uiDescriptor,
4302 touchListenerIn,
4303 listenerIn,
4304 uiBundleUri,
4305 RequiredFeatures { queryFeatures (LV2_CORE__requiredFeature) },
4306 OptionalFeatures { queryFeatures (LV2_CORE__optionalFeature) });
4307
4308 editorPointer = newEditor.get();
4309
4310 changedParameterFlusher.startTimerHz (60);
4311
4312 return newEditor;
4313 }
4314
4315 bool hasEditor() const
4316 {
4317 return uiDescriptor.get() != nullptr;
4318 }
4319
4320 void prepareToDestroyEditor()
4321 {
4322 changedParameterFlusher.stopTimer();
4323 }
4324
4325private:
4326 Component::SafePointer<Editor> editorPointer = nullptr;
4327 String uiBundleUri;
4328 UiDescriptor uiDescriptor;
4329 LambdaTimer changedParameterFlusher;
4330};
4331
4332template <>
4333class OptionalEditor<false>
4334{
4335public:
4336 OptionalEditor (String, UiDescriptor, std::function<void()>) {}
4337
4338 void createView() {}
4339 void destroyView() {}
4340
4341 std::unique_ptr<AudioProcessorEditor> createEditor(World&,
4342 AudioPluginInstance&,
4343 InstanceProvider&,
4344 TouchListener&,
4345 EditorListener&)
4346 {
4347 return nullptr;
4348 }
4349
4350 bool hasEditor() const { return false; }
4351 void prepareToDestroyEditor() {}
4352};
4353
4354//==============================================================================
4355class LV2AudioPluginInstance : public AudioPluginInstance,
4356 private TouchListener,
4357 private EditorListener,
4358 private InstanceProvider
4359{
4360public:
4361 LV2AudioPluginInstance (std::shared_ptr<World> worldIn,
4362 const Plugin& pluginIn,
4363 const UsefulUris& uris,
4364 std::unique_ptr<InstanceWithSupports>&& in,
4365 PluginDescription&& desc,
4366 std::vector<String> knownPresetUris,
4367 PluginState stateToApply,
4368 String uiBundleUriIn,
4369 UiDescriptor uiDescriptorIn)
4370 : LV2AudioPluginInstance (worldIn,
4371 pluginIn,
4372 std::move (in),
4373 std::move (desc),
4374 std::move (knownPresetUris),
4375 std::move (stateToApply),
4376 std::move (uiBundleUriIn),
4377 std::move (uiDescriptorIn),
4378 getParsedBuses (*worldIn, pluginIn, uris)) {}
4379
4380 void fillInPluginDescription (PluginDescription& d) const override { d = description; }
4381
4382 const String getName() const override { return description.name; }
4383
4384 void prepareToPlay (double sampleRate, int numSamples) override
4385 {
4386 // In REAPER, changing the sample rate will deactivate the plugin,
4387 // save its state, destroy it, create a new instance, restore the
4388 // state, and then activate the new instance.
4389 // We'll do the same, because there's no way to retroactively change the
4390 // plugin sample rate.
4391 // This is a bit expensive, so try to avoid changing the sample rate too
4392 // frequently.
4393
4394 // In addition to the above, we also need to destroy the custom view,
4395 // and recreate it after creating the new plugin instance.
4396 // Ideally this should all happen in the same Component.
4397
4398 deactivate();
4399 destroyView();
4400
4401 MemoryBlock mb;
4402 getStateInformation (mb);
4403
4404 instance = std::make_unique<InstanceWithSupports> (*world,
4405 std::move (instance->symap),
4406 plugin,
4407 std::move (instance->ports),
4408 numSamples,
4409 sampleRate);
4410
4411 // prepareToPlay is *guaranteed* not to be called concurrently with processBlock
4412 setStateInformationImpl (mb.getData(), (int) mb.getSize(), ConcurrentWithAudioCallback::no);
4413
4414 jassert (numSamples == instance->features.getMaxBlockSize());
4415
4416 optionalEditor.createView();
4417 activate();
4418 }
4419
4420 void releaseResources() override { deactivate(); }
4421
4422 using AudioPluginInstance::processBlock;
4423 using AudioPluginInstance::processBlockBypassed;
4424
4425 void processBlock (AudioBuffer<float>& audio, MidiBuffer& midi) override
4426 {
4427 processBlockImpl (audio, midi);
4428 }
4429
4430 void processBlockBypassed (AudioBuffer<float>& audio, MidiBuffer& midi) override
4431 {
4432 if (bypassParam != nullptr)
4433 processBlockImpl (audio, midi);
4434 else
4435 AudioPluginInstance::processBlockBypassed (audio, midi);
4436 }
4437
4438 double getTailLengthSeconds() const override { return {}; } // TODO
4439
4440 bool acceptsMidi() const override
4441 {
4442 if (instance == nullptr)
4443 return false;
4444
4445 auto ports = instance->ports.getAtomPorts();
4446
4447 return std::any_of (ports.begin(), ports.end(), [&] (const AtomPort& a)
4448 {
4449 if (a.header.direction != Port::Direction::input)
4450 return false;
4451
4452 return portAtIndexSupportsMidi (a.header.index);
4453 });
4454 }
4455
4456 bool producesMidi() const override
4457 {
4458 if (instance == nullptr)
4459 return false;
4460
4461 auto ports = instance->ports.getAtomPorts();
4462
4463 return std::any_of (ports.begin(), ports.end(), [&] (const AtomPort& a)
4464 {
4465 if (a.header.direction != Port::Direction::output)
4466 return false;
4467
4468 return portAtIndexSupportsMidi (a.header.index);
4469 });
4470 }
4471
4472 AudioProcessorEditor* createEditor() override
4473 {
4474 return optionalEditor.createEditor (*world, *this, *this, *this, *this).release();
4475 }
4476
4477 bool hasEditor() const override
4478 {
4479 return optionalEditor.hasEditor();
4480 }
4481
4482 int getNumPrograms() override { return (int) presetUris.size(); }
4483
4484 int getCurrentProgram() override
4485 {
4486 return lastAppliedPreset;
4487 }
4488
4489 void setCurrentProgram (int newProgram) override
4490 {
4492
4493 if (! isPositiveAndBelow (newProgram, presetUris.size()))
4494 return;
4495
4496 lastAppliedPreset = newProgram;
4497 applyStateWithAppropriateLocking (loadStateWithUri (presetUris[(size_t) newProgram]),
4498 ConcurrentWithAudioCallback::yes);
4499 }
4500
4501 const String getProgramName (int program) override
4502 {
4504
4505 if (isPositiveAndBelow (program, presetUris.size()))
4506 return loadStateWithUri (presetUris[(size_t) program]).getLabel();
4507
4508 return {};
4509 }
4510
4511 void changeProgramName (int program, const String& label) override
4512 {
4514
4515 if (isPositiveAndBelow (program, presetUris.size()))
4516 loadStateWithUri (presetUris[(size_t) program]).setLabel (label);
4517 }
4518
4519 void getStateInformation (MemoryBlock& block) override
4520 {
4522
4523 // TODO where should the state URI come from?
4524 PortMap portStateManager (instance->ports);
4525 const auto stateUri = String::fromUTF8 (instance->instance.getUri()) + "/savedState";
4526 auto mapFeature = instance->symap->getMapFeature();
4527 auto unmapFeature = instance->symap->getUnmapFeature();
4528 const auto state = PluginState::SaveRestoreHandle (*instance, portStateManager).save (plugin.get(), &mapFeature);
4529 const auto string = state.toString (world->get(), &mapFeature, &unmapFeature, stateUri.toRawUTF8());
4530 block.replaceAll (string.data(), string.size());
4531 }
4532
4533 void setStateInformation (const void* data, int size) override
4534 {
4535 setStateInformationImpl (data, size, ConcurrentWithAudioCallback::yes);
4536 }
4537
4538 void setNonRealtime (bool newValue) noexcept override
4539 {
4541
4542 AudioPluginInstance::setNonRealtime (newValue);
4543 instance->features.setNonRealtime (newValue);
4544 }
4545
4546 bool isBusesLayoutSupported (const BusesLayout& layout) const override
4547 {
4548 for (const auto& pair : { std::make_tuple (&layout.inputBuses, &declaredBusLayout.inputs),
4549 std::make_tuple (&layout.outputBuses, &declaredBusLayout.outputs) })
4550 {
4551 const auto& requested = *std::get<0> (pair);
4552 const auto& allowed = *std::get<1> (pair);
4553
4554 if ((size_t) requested.size() != allowed.size())
4555 return false;
4556
4557 for (size_t busIndex = 0; busIndex < allowed.size(); ++busIndex)
4558 {
4559 const auto& requestedBus = requested[(int) busIndex];
4560 const auto& allowedBus = allowed[busIndex];
4561
4562 if (! allowedBus.isCompatible (requestedBus))
4563 return false;
4564 }
4565 }
4566
4567 return true;
4568 }
4569
4570 void processorLayoutsChanged() override { ioMap = lv2_shared::PortToAudioBufferMap { getBusesLayout(), declaredBusLayout }; }
4571
4572 AudioProcessorParameter* getBypassParameter() const override { return bypassParam; }
4573
4574private:
4575 enum class ConcurrentWithAudioCallback { no, yes };
4576
4577 LV2AudioPluginInstance (std::shared_ptr<World> worldIn,
4578 const Plugin& pluginIn,
4579 std::unique_ptr<InstanceWithSupports>&& in,
4580 PluginDescription&& desc,
4581 std::vector<String> knownPresetUris,
4582 PluginState stateToApply,
4583 String uiBundleUriIn,
4584 UiDescriptor uiDescriptorIn,
4585 const lv2_shared::ParsedBuses& parsedBuses)
4586 : AudioPluginInstance (getBusesProperties (parsedBuses, *worldIn)),
4587 declaredBusLayout (parsedBuses),
4588 world (std::move (worldIn)),
4589 plugin (pluginIn),
4590 description (std::move (desc)),
4591 presetUris (std::move (knownPresetUris)),
4592 instance (std::move (in)),
4593 optionalEditor (std::move (uiBundleUriIn),
4594 std::move (uiDescriptorIn),
4595 [this] { postChangedParametersToUi(); })
4596 {
4597 applyStateWithAppropriateLocking (std::move (stateToApply), ConcurrentWithAudioCallback::no);
4598 }
4599
4600 void setStateInformationImpl (const void* data, int size, ConcurrentWithAudioCallback concurrent)
4601 {
4603
4604 if (data == nullptr || size == 0)
4605 return;
4606
4607 auto begin = static_cast<const char*> (data);
4608 std::vector<char> copy (begin, begin + size);
4609 copy.push_back (0);
4610 auto mapFeature = instance->symap->getMapFeature();
4611 applyStateWithAppropriateLocking (PluginState { lilv_state_new_from_string (world->get(), &mapFeature, copy.data()) },
4612 concurrent);
4613 }
4614
4615 // This does *not* destroy the editor component.
4616 // If we destroy the processor, the view must also be destroyed to avoid dangling pointers.
4617 // However, JUCE clients expect their editors to remain valid for the duration of the
4618 // AudioProcessor's lifetime.
4619 // As a compromise, this will create a new LV2 view into an existing editor component.
4620 void destroyView()
4621 {
4622 optionalEditor.destroyView();
4623 }
4624
4625 void activate()
4626 {
4627 if (! active)
4628 instance->instance.activate();
4629
4630 active = true;
4631 }
4632
4633 void deactivate()
4634 {
4635 if (active)
4636 instance->instance.deactivate();
4637
4638 active = false;
4639 }
4640
4641 void processBlockImpl (AudioBuffer<float>& audio, MidiBuffer& midi)
4642 {
4643 preparePortsForRun (audio, midi);
4644
4645 instance->instance.run (static_cast<uint32_t> (audio.getNumSamples()));
4646 instance->features.processResponses();
4647
4648 processPortsAfterRun (midi);
4649 }
4650
4651 bool portAtIndexSupportsMidi (uint32_t index) const noexcept
4652 {
4653 const auto port = plugin.getPortByIndex (index);
4654
4655 if (! port.isValid())
4656 return false;
4657
4658 return port.supportsEvent (world->newUri (LV2_MIDI__MidiEvent).get());
4659 }
4660
4661 void controlGrabbed (uint32_t port, bool grabbed) override
4662 {
4663 if (auto* param = parameterValues.getParamByPortIndex (port))
4664 {
4665 if (grabbed)
4666 param->beginChangeGesture();
4667 else
4668 param->endChangeGesture();
4669 }
4670 }
4671
4672 void viewCreated (UiEventListener* newListener) override
4673 {
4674 uiEventListener = newListener;
4675 postAllParametersToUi();
4676 }
4677
4678 ParameterWriterUrids getParameterWriterUrids() const
4679 {
4680 return { instance->urids.mLV2_PATCH__Set,
4681 instance->urids.mLV2_PATCH__property,
4682 instance->urids.mLV2_PATCH__value,
4683 instance->urids.mLV2_ATOM__eventTransfer };
4684 }
4685
4686 void postAllParametersToUi()
4687 {
4688 parameterValues.postAllParametersToUi (uiEventListener, getParameterWriterUrids(), *instance->processorToUi);
4689 controlPortStructure.writeOutputPorts (uiEventListener, *instance->processorToUi);
4690 }
4691
4692 void postChangedParametersToUi()
4693 {
4694 parameterValues.postChangedParametersToUi (uiEventListener, getParameterWriterUrids(), *instance->processorToUi);
4695 controlPortStructure.writeOutputPorts (uiEventListener, *instance->processorToUi);
4696 }
4697
4698 void notifyEditorBeingDeleted() override
4699 {
4700 optionalEditor.prepareToDestroyEditor();
4701 uiEventListener = nullptr;
4702 editorBeingDeleted (getActiveEditor());
4703 }
4704
4705 InstanceWithSupports* getInstanceWithSupports() const override
4706 {
4707 return instance.get();
4708 }
4709
4710 void applyStateWithAppropriateLocking (PluginState&& state, ConcurrentWithAudioCallback concurrent)
4711 {
4712 PortMap portStateManager (instance->ports);
4713
4714 // If a plugin supports threadSafeRestore, its restore method is thread-safe
4715 // and may be called concurrently with audio class functions.
4716 if (hasThreadSafeRestore || concurrent == ConcurrentWithAudioCallback::no)
4717 {
4718 state.restore (*instance, portStateManager);
4719 }
4720 else
4721 {
4722 const ScopedLock lock (getCallbackLock());
4723 state.restore (*instance, portStateManager);
4724 }
4725
4726 parameterValues.updateFromControlPorts (controlPortStructure);
4727 asyncFullUiParameterUpdate.triggerAsyncUpdate();
4728 }
4729
4730 PluginState loadStateWithUri (const String& str)
4731 {
4732 auto mapFeature = instance->symap->getMapFeature();
4733 const auto presetUri = world->newUri (str.toRawUTF8());
4734 lilv_world_load_resource (world->get(), presetUri.get());
4735 return PluginState { lilv_state_new_from_world (world->get(), &mapFeature, presetUri.get()) };
4736 }
4737
4738 void connectPorts (AudioBuffer<float>& audio)
4739 {
4740 // Plugins that cannot process in-place will require the feature "inPlaceBroken".
4741 // We don't support that feature, so if we made it to this point we can assume that
4742 // in-place processing works.
4743 for (const auto& port : instance->ports.getAudioPorts())
4744 {
4745 const auto channel = ioMap.getChannelForPort (port.header.index);
4746 auto* ptr = isPositiveAndBelow (channel, audio.getNumChannels()) ? audio.getWritePointer (channel)
4747 : nullptr;
4748 instance->instance.connectPort (port.header.index, ptr);
4749 }
4750
4751 for (const auto& port : instance->ports.getCvPorts())
4752 instance->instance.connectPort (port.header.index, nullptr);
4753
4754 for (auto& port : instance->ports.getAtomPorts())
4755 instance->instance.connectPort (port.header.index, port.data());
4756 }
4757
4758 void writeTimeInfoToPort (AtomPort& port)
4759 {
4760 if (port.header.direction != Port::Direction::input || ! port.getSupportsTime())
4761 return;
4762
4763 auto* forge = port.getForge().get();
4764 auto* playhead = getPlayHead();
4765
4766 if (playhead == nullptr)
4767 return;
4768
4769 // Write timing info to the control port
4770 const auto info = playhead->getPosition();
4771
4772 if (! info.hasValue())
4773 return;
4774
4775 const auto& urids = instance->urids;
4776
4777 lv2_atom_forge_frame_time (forge, 0);
4778
4779 lv2_shared::ObjectFrame object { forge, (uint32_t) 0, urids.mLV2_TIME__Position };
4780
4781 lv2_atom_forge_key (forge, urids.mLV2_TIME__speed);
4782 lv2_atom_forge_float (forge, info->getIsPlaying() ? 1.0f : 0.0f);
4783
4784 if (const auto samples = info->getTimeInSamples())
4785 {
4786 lv2_atom_forge_key (forge, urids.mLV2_TIME__frame);
4787 lv2_atom_forge_long (forge, *samples);
4788 }
4789
4790 if (const auto bar = info->getBarCount())
4791 {
4792 lv2_atom_forge_key (forge, urids.mLV2_TIME__bar);
4793 lv2_atom_forge_long (forge, *bar);
4794 }
4795
4796 if (const auto beat = info->getPpqPosition())
4797 {
4798 if (const auto barStart = info->getPpqPositionOfLastBarStart())
4799 {
4800 lv2_atom_forge_key (forge, urids.mLV2_TIME__barBeat);
4801 lv2_atom_forge_float (forge, (float) (*beat - *barStart));
4802 }
4803
4804 lv2_atom_forge_key (forge, urids.mLV2_TIME__beat);
4805 lv2_atom_forge_double (forge, *beat);
4806 }
4807
4808 if (const auto sig = info->getTimeSignature())
4809 {
4810 lv2_atom_forge_key (forge, urids.mLV2_TIME__beatUnit);
4811 lv2_atom_forge_int (forge, sig->denominator);
4812
4813 lv2_atom_forge_key (forge, urids.mLV2_TIME__beatsPerBar);
4814 lv2_atom_forge_float (forge, (float) sig->numerator);
4815 }
4816
4817 if (const auto bpm = info->getBpm())
4818 {
4819 lv2_atom_forge_key (forge, urids.mLV2_TIME__beatsPerMinute);
4820 lv2_atom_forge_float (forge, (float) *bpm);
4821 }
4822 }
4823
4824 void preparePortsForRun (AudioBuffer<float>& audio, MidiBuffer& midiBuffer)
4825 {
4826 connectPorts (audio);
4827
4828 for (auto& port : instance->ports.getAtomPorts())
4829 {
4830 switch (port.header.direction)
4831 {
4832 case Port::Direction::input:
4833 port.beginSequence();
4834 break;
4835
4836 case Port::Direction::output:
4837 port.replaceWithChunk();
4838 break;
4839
4840 case Port::Direction::unknown:
4842 break;
4843 }
4844 }
4845
4846 for (auto& port : instance->ports.getAtomPorts())
4847 writeTimeInfoToPort (port);
4848
4849 const auto controlPortForge = controlPort != nullptr ? controlPort->getForge().get()
4850 : nullptr;
4851
4852 parameterValues.postChangedParametersToProcessor (getParameterWriterUrids(), controlPortForge);
4853
4854 instance->uiToProcessor.readAllAndClear ([this] (MessageHeader header, uint32_t size, const void* buffer)
4855 {
4856 pushMessage (header, size, buffer);
4857 });
4858
4859 for (auto& port : instance->ports.getAtomPorts())
4860 {
4861 if (port.header.direction == Port::Direction::input)
4862 {
4863 for (const auto meta : midiBuffer)
4864 {
4865 port.addEventToSequence (meta.samplePosition,
4866 instance->urids.mLV2_MIDI__MidiEvent,
4867 static_cast<uint32_t> (meta.numBytes),
4868 meta.data);
4869 }
4870
4871 port.endSequence();
4872 }
4873 }
4874
4875 if (freeWheelingPort != nullptr)
4876 freeWheelingPort->currentValue = isNonRealtime() ? freeWheelingPort->info.max
4877 : freeWheelingPort->info.min;
4878 }
4879
4880 void pushMessage (MessageHeader header, uint32_t size, const void* data)
4881 {
4883
4884 if (header.protocol == 0 || header.protocol == instance->urids.mLV2_UI__floatProtocol)
4885 {
4886 const auto value = readUnaligned<float> (data);
4887
4888 if (auto* param = parameterValues.getParamByPortIndex (header.portIndex))
4889 {
4890 param->setDenormalisedValue (value);
4891 }
4892 else if (auto* port = controlPortStructure.getControlPortByIndex (header.portIndex))
4893 {
4894 // No parameter corresponds to this port, write to the port directly
4895 port->currentValue = value;
4896 }
4897 }
4898 else if (auto* atomPort = header.portIndex < atomPorts.size() ? atomPorts[header.portIndex] : nullptr)
4899 {
4900 if (header.protocol == instance->urids.mLV2_ATOM__eventTransfer)
4901 {
4902 if (const auto* atom = convertToAtomPtr (data, (size_t) size))
4903 {
4904 atomPort->addAtomToSequence (0, atom);
4905
4906 // Not UB; LV2_Atom_Object has LV2_Atom as its first member
4907 if (atom->type == instance->urids.mLV2_ATOM__Object)
4908 patchSetHelper.processPatchSet (reinterpret_cast<const LV2_Atom_Object*> (data), PatchSetCallback { parameterValues });
4909 }
4910 }
4911 else if (header.protocol == instance->urids.mLV2_ATOM__atomTransfer)
4912 {
4913 if (const auto* atom = convertToAtomPtr (data, (size_t) size))
4914 atomPort->replaceBufferWithAtom (atom);
4915 }
4916 }
4917 }
4918
4919 void processPortsAfterRun (MidiBuffer& midi)
4920 {
4921 midi.clear();
4922
4923 for (auto& port : instance->ports.getAtomPorts())
4924 processAtomPort (port, midi);
4925
4926 if (latencyPort != nullptr)
4927 setLatencySamples ((int) latencyPort->currentValue);
4928 }
4929
4930 void processAtomPort (const AtomPort& port, MidiBuffer& midi)
4931 {
4932 if (port.header.direction != Port::Direction::output)
4933 return;
4934
4935 // The port holds an Atom, by definition
4936 const auto* atom = reinterpret_cast<const LV2_Atom*> (port.data());
4937
4938 if (atom->type != instance->urids.mLV2_ATOM__Sequence)
4939 return;
4940
4941 // The Atom said that it was of sequence type, so this isn't UB
4942 const auto* sequence = reinterpret_cast<const LV2_Atom_Sequence*> (port.data());
4943
4944 // http://lv2plug.in/ns/ext/atom#Sequence - run() stamps are always audio frames
4945 jassert (sequence->body.unit == 0 || sequence->body.unit == instance->urids.mLV2_UNITS__frame);
4946
4947 for (const auto* event : lv2_shared::SequenceIterator { lv2_shared::SequenceWithSize { sequence } })
4948 {
4949 // At the moment, we forward all outgoing events to the UI.
4950 instance->processorToUi->pushMessage ({ uiEventListener, { port.header.index, instance->urids.mLV2_ATOM__eventTransfer } },
4951 (uint32_t) (event->body.size + sizeof (LV2_Atom)),
4952 &event->body);
4953
4954 if (event->body.type == instance->urids.mLV2_MIDI__MidiEvent)
4955 midi.addEvent (event + 1, static_cast<int> (event->body.size), static_cast<int> (event->time.frames));
4956
4957 if (lv2_atom_forge_is_object_type (port.getForge().get(), event->body.type))
4958 if (reinterpret_cast<const LV2_Atom_Object_Body*> (event + 1)->otype == instance->urids.mLV2_STATE__StateChanged)
4959 updateHostDisplay (ChangeDetails{}.withNonParameterStateChanged (true));
4960
4961 patchSetHelper.processPatchSet (event, PatchSetCallback { parameterValues });
4962 }
4963 }
4964
4965 // Check for duplicate channel designations, and convert the set to a discrete channel layout
4966 // if any designations are duplicated.
4967 static std::set<lv2_shared::SinglePortInfo> validateAndRedesignatePorts (std::set<lv2_shared::SinglePortInfo> info)
4968 {
4969 const auto channelSet = lv2_shared::ParsedGroup::getEquivalentSet (info);
4970
4971 if ((int) info.size() == channelSet.size())
4972 return info;
4973
4974 std::set<lv2_shared::SinglePortInfo> result;
4975 auto designation = (int) AudioChannelSet::discreteChannel0;
4976
4977 for (auto& item : info)
4978 {
4979 auto copy = item;
4980 copy.designation = (AudioChannelSet::ChannelType) designation++;
4981 result.insert (copy);
4982 }
4983
4984 return result;
4985 }
4986
4987 static AudioChannelSet::ChannelType getPortDesignation (World& world, const Port& port, size_t indexInGroup)
4988 {
4989 const auto defaultResult = (AudioChannelSet::ChannelType) (AudioChannelSet::discreteChannel0 + indexInGroup);
4990 const auto node = port.get (world.newUri (LV2_CORE__designation).get());
4991
4992 if (node == nullptr)
4993 return defaultResult;
4994
4995 const auto it = lv2_shared::channelDesignationMap.find (lilvNodeToUriString (node.get()));
4996
4997 if (it == lv2_shared::channelDesignationMap.end())
4998 return defaultResult;
4999
5000 return it->second;
5001 }
5002
5003 static lv2_shared::ParsedBuses getParsedBuses (World& world, const Plugin& p, const UsefulUris& uris)
5004 {
5005 const auto groupPropertyUri = world.newUri (LV2_PORT_GROUPS__group);
5006 const auto optionalUri = world.newUri (LV2_CORE__connectionOptional);
5007
5008 std::map<String, std::set<lv2_shared::SinglePortInfo>> inputGroups, outputGroups;
5009 std::set<lv2_shared::SinglePortInfo> ungroupedInputs, ungroupedOutputs;
5010
5011 for (uint32_t i = 0, numPorts = p.getNumPorts(); i < numPorts; ++i)
5012 {
5013 const auto port = p.getPortByIndex (i);
5014
5015 if (port.getKind (uris) != Port::Kind::audio)
5016 continue;
5017
5018 const auto groupUri = lilvNodeToUriString (port.get (groupPropertyUri.get()).get());
5019
5020 auto& set = [&]() -> auto&
5021 {
5022 if (groupUri.isEmpty())
5023 return port.getDirection (uris) == Port::Direction::input ? ungroupedInputs : ungroupedOutputs;
5024
5025 auto& group = port.getDirection (uris) == Port::Direction::input ? inputGroups : outputGroups;
5026 return group[groupUri];
5027 }();
5028
5029 set.insert ({ port.getIndex(), getPortDesignation (world, port, set.size()), port.hasProperty (optionalUri) });
5030 }
5031
5032 for (auto* groups : { &inputGroups, &outputGroups })
5033 for (auto& pair : *groups)
5034 pair.second = validateAndRedesignatePorts (std::move (pair.second));
5035
5037 const auto getMainGroupName = [&] (const char* propertyName)
5038 {
5039 for (const auto* item : p.getValue (world.newUri (propertyName).get()))
5040 return lilvNodeToUriString (item);
5041
5042 return String{};
5043 };
5045
5046 return { findStableBusOrder (getMainGroupName (LV2_PORT_GROUPS__mainInput), inputGroups, ungroupedInputs),
5047 findStableBusOrder (getMainGroupName (LV2_PORT_GROUPS__mainOutput), outputGroups, ungroupedOutputs) };
5048 }
5049
5050 static String getNameForUri (World& world, StringRef uri)
5051 {
5052 if (uri.isEmpty())
5053 return String();
5054
5055 const auto node = world.get (world.newUri (uri).get(),
5056 world.newUri (LV2_CORE__name).get(),
5057 nullptr);
5058
5059 if (node == nullptr)
5060 return String();
5061
5062 return String::fromUTF8 (lilv_node_as_string (node.get()));
5063 }
5064
5065 static BusesProperties getBusesProperties (const lv2_shared::ParsedBuses& parsedBuses, World& world)
5066 {
5067 BusesProperties result;
5068
5069 for (const auto& pair : { std::make_tuple (&parsedBuses.inputs, &result.inputLayouts),
5070 std::make_tuple (&parsedBuses.outputs, &result.outputLayouts) })
5071 {
5072 const auto& buses = *std::get<0> (pair);
5073 auto& layout = *std::get<1> (pair);
5074
5075 for (const auto& bus : buses)
5076 {
5077 layout.add (AudioProcessor::BusProperties { getNameForUri (world, bus.uid),
5078 bus.getEquivalentSet(),
5079 bus.isRequired() });
5080 }
5081 }
5082
5083 return result;
5084 }
5085
5086 LV2_URID map (const char* str) const
5087 {
5088 return instance != nullptr ? instance->symap->map (str)
5089 : LV2_URID();
5090 }
5091
5092 ControlPort* findControlPortWithIndex (uint32_t index) const
5093 {
5094 auto ports = instance->ports.getControlPorts();
5095 const auto indexMatches = [&] (const ControlPort& p) { return p.header.index == index; };
5096 const auto it = std::find_if (ports.begin(), ports.end(), indexMatches);
5097
5098 return it != ports.end() ? &(*it) : nullptr;
5099 }
5100
5101 const lv2_shared::ParsedBuses declaredBusLayout;
5102 lv2_shared::PortToAudioBufferMap ioMap { getBusesLayout(), declaredBusLayout };
5103 std::shared_ptr<World> world;
5104 Plugin plugin;
5105 PluginDescription description;
5106 std::vector<String> presetUris;
5107 std::unique_ptr<InstanceWithSupports> instance;
5108 AsyncFn asyncFullUiParameterUpdate { [this] { postAllParametersToUi(); } };
5109
5110 std::vector<AtomPort*> atomPorts = getPortPointers (instance->ports.getAtomPorts());
5111
5112 AtomPort* const controlPort = [&]() -> AtomPort*
5113 {
5114 const auto port = plugin.getPortByDesignation (world->newUri (LV2_CORE__InputPort).get(),
5115 world->newUri (LV2_CORE__control).get());
5116
5117 if (! port.isValid())
5118 return nullptr;
5119
5120 const auto index = port.getIndex();
5121
5122 if (! isPositiveAndBelow (index, atomPorts.size()))
5123 return nullptr;
5124
5125 return atomPorts[index];
5126 }();
5127
5128 ControlPort* const latencyPort = [&]() -> ControlPort*
5129 {
5130 if (! plugin.hasLatency())
5131 return nullptr;
5132
5133 return findControlPortWithIndex (plugin.getLatencyPortIndex());
5134 }();
5135
5136 ControlPort* const freeWheelingPort = [&]() -> ControlPort*
5137 {
5138 const auto port = plugin.getPortByDesignation (world->newUri (LV2_CORE__InputPort).get(),
5139 world->newUri (LV2_CORE__freeWheeling).get());
5140
5141 if (! port.isValid())
5142 return nullptr;
5143
5144 return findControlPortWithIndex (port.getIndex());
5145 }();
5146
5147 ControlPort* const enabledPort = [&]() -> ControlPort*
5148 {
5149 const auto port = plugin.getPortByDesignation (world->newUri (LV2_CORE__InputPort).get(),
5150 world->newUri (LV2_CORE_PREFIX "enabled").get());
5151
5152 if (! port.isValid())
5153 return nullptr;
5154
5155 return findControlPortWithIndex (port.getIndex());
5156 }();
5157
5158 lv2_shared::PatchSetHelper patchSetHelper { instance->symap->getMapFeature(), plugin.getUri().getTyped() };
5159 ControlPortAccelerationStructure controlPortStructure { instance->ports.getControlPorts() };
5160 ParameterValueCache parameterValues { *this,
5161 *world,
5162 instance->symap->getMapFeature(),
5163 getJuceParameterInfo (*world,
5164 plugin,
5165 instance->urids,
5166 { latencyPort, freeWheelingPort },
5167 instance->ports.getControlPorts(),
5168 controlPort != nullptr ? controlPort->header.index : 0),
5169 enabledPort };
5170 LV2Parameter* bypassParam = enabledPort != nullptr ? parameterValues.getParamByPortIndex (enabledPort->header.index)
5171 : nullptr;
5172
5173 std::atomic<UiEventListener*> uiEventListener { nullptr };
5174 OptionalEditor<> optionalEditor;
5175 int lastAppliedPreset = 0;
5176 bool hasThreadSafeRestore = plugin.hasExtensionData (world->newUri (LV2_STATE__threadSafeRestore));
5177 bool active { false };
5178
5179 JUCE_LEAK_DETECTOR (LV2AudioPluginInstance)
5180};
5181
5182} // namespace lv2
5183
5184//==============================================================================
5185class LV2PluginFormat::Pimpl
5186{
5187public:
5188 Pimpl()
5189 {
5190 world->loadAll();
5191
5192 const auto tempFile = lv2ResourceFolder.getFile();
5193
5194 if (tempFile.createDirectory())
5195 {
5196 for (const auto& bundle : lv2::Bundle::getAllBundles())
5197 {
5198 const auto pathToBundle = tempFile.getChildFile (bundle.name + String (".lv2"));
5199
5200 if (! pathToBundle.createDirectory())
5201 continue;
5202
5203 for (const auto& resource : bundle.contents)
5204 pathToBundle.getChildFile (resource.name).replaceWithText (resource.contents);
5205
5206 const auto pathString = File::addTrailingSeparator (pathToBundle.getFullPathName());
5207 world->loadBundle (world->newFileUri (nullptr, pathString.toRawUTF8()));
5208 }
5209 }
5210 }
5211
5212 ~Pimpl()
5213 {
5214 lv2ResourceFolder.getFile().deleteRecursively();
5215 }
5216
5217 void findAllTypesForFile (OwnedArray<PluginDescription>& result,
5218 const String& identifier)
5219 {
5220 auto desc = getDescription (findPluginByUri (identifier));
5221
5222 if (desc.fileOrIdentifier.isNotEmpty())
5223 result.add (std::make_unique<PluginDescription> (desc));
5224 }
5225
5226 bool fileMightContainThisPluginType (const String& file) const
5227 {
5228 // If the string looks like a URI, then it could be a valid LV2 identifier
5229 const auto* data = file.toRawUTF8();
5230 const auto numBytes = file.getNumBytesAsUTF8();
5231 std::vector<uint8_t> vec (numBytes + 1, 0);
5232 std::copy (data, data + numBytes, vec.begin());
5233 return serd_uri_string_has_scheme (vec.data());
5234 }
5235
5236 String getNameOfPluginFromIdentifier (const String& identifier)
5237 {
5238 // We would have to actually load the bundle to get its name,
5239 // and the bundle may contain multiple plugins
5240 return identifier;
5241 }
5242
5243 bool pluginNeedsRescanning (const PluginDescription&)
5244 {
5245 return true;
5246 }
5247
5248 bool doesPluginStillExist (const PluginDescription& description)
5249 {
5250 return findPluginByUri (description.fileOrIdentifier) != nullptr;
5251 }
5252
5253 StringArray searchPathsForPlugins (const FileSearchPath&, bool, bool)
5254 {
5255 world->loadAll();
5256
5257 StringArray result;
5258
5259 for (const auto* plugin : world->getAllPlugins())
5260 result.add (lv2_host::Plugin { plugin }.getUri().getTyped());
5261
5262 return result;
5263 }
5264
5265 FileSearchPath getDefaultLocationsToSearch() { return {}; }
5266
5267 const LilvUI* findEmbeddableUi (const lv2_host::Uis* pluginUis, std::true_type)
5268 {
5269 if (pluginUis == nullptr)
5270 return nullptr;
5271
5272 const std::vector<const LilvUI*> allUis (pluginUis->begin(), pluginUis->end());
5273
5274 if (allUis.empty())
5275 return nullptr;
5276
5277 constexpr const char* rawUri =
5278 #if JUCE_MAC
5280 #elif JUCE_WINDOWS
5282 #elif JUCE_LINUX || JUCE_BSD
5284 #else
5285 nullptr;
5286 #endif
5287
5288 jassert (rawUri != nullptr);
5289 const auto nativeUiUri = world->newUri (rawUri);
5290
5291 struct UiWithSuitability
5292 {
5293 const LilvUI* ui;
5294 unsigned suitability;
5295
5296 bool operator< (const UiWithSuitability& other) const noexcept
5297 {
5298 return suitability < other.suitability;
5299 }
5300
5301 static unsigned uiIsSupported (const char* hostUri, const char* pluginUri)
5302 {
5303 if (strcmp (hostUri, pluginUri) == 0)
5304 return 1;
5305
5306 return 0;
5307 }
5308 };
5309
5310 std::vector<UiWithSuitability> uisWithSuitability;
5311 uisWithSuitability.reserve (allUis.size());
5312
5313 std::transform (allUis.cbegin(), allUis.cend(), std::back_inserter (uisWithSuitability), [&] (const LilvUI* ui)
5314 {
5315 const LilvNode* type = nullptr;
5316 return UiWithSuitability { ui, lilv_ui_is_supported (ui, UiWithSuitability::uiIsSupported, nativeUiUri.get(), &type) };
5317 });
5318
5319 std::sort (uisWithSuitability.begin(), uisWithSuitability.end());
5320
5321 if (uisWithSuitability.back().suitability != 0)
5322 return uisWithSuitability.back().ui;
5323
5324 return nullptr;
5325 }
5326
5327 const LilvUI* findEmbeddableUi (const lv2_host::Uis*, std::false_type)
5328 {
5329 return nullptr;
5330 }
5331
5332 const LilvUI* findEmbeddableUi (const lv2_host::Uis* pluginUis)
5333 {
5334 return findEmbeddableUi (pluginUis, std::integral_constant<bool, lv2_host::editorFunctionalityEnabled>{});
5335 }
5336
5337 static lv2_host::UiDescriptor getUiDescriptor (const LilvUI* ui)
5338 {
5339 if (ui == nullptr)
5340 return {};
5341
5342 const auto libraryFile = StringPtr { lilv_file_uri_parse (lilv_node_as_uri (lilv_ui_get_binary_uri (ui)), nullptr) };
5343
5344 return lv2_host::UiDescriptor { lv2_host::UiDescriptorArgs{}.withLibraryPath (libraryFile.get())
5345 .withUiUri (lilv_node_as_uri (lilv_ui_get_uri (ui))) };
5346 }
5347
5348 // Returns the name of a missing feature, if any.
5349 template <typename RequiredFeatures, typename AvailableFeatures>
5350 static std::vector<String> findMissingFeatures (RequiredFeatures&& required,
5351 AvailableFeatures&& available)
5352 {
5353 std::vector<String> result;
5354
5355 for (const auto* node : required)
5356 {
5357 const auto nodeString = String::fromUTF8 (lilv_node_as_uri (node));
5358
5359 if (std::find (std::begin (available), std::end (available), nodeString) == std::end (available))
5360 result.push_back (nodeString);
5361 }
5362
5363 return result;
5364 }
5365
5366 void createPluginInstance (const PluginDescription& desc,
5367 double initialSampleRate,
5368 int initialBufferSize,
5369 PluginCreationCallback callback)
5370 {
5371 const auto* pluginPtr = findPluginByUri (desc.fileOrIdentifier);
5372
5373 if (pluginPtr == nullptr)
5374 return callback (nullptr, "Unable to locate plugin with the requested URI");
5375
5376 const lv2_host::Plugin plugin { pluginPtr };
5377
5378 auto symap = std::make_unique<lv2_host::SymbolMap>();
5379
5380 const auto missingFeatures = findMissingFeatures (plugin.getRequiredFeatures(),
5381 lv2_host::FeaturesData::getFeatureUris());
5382
5383 if (! missingFeatures.empty())
5384 {
5385 const auto missingFeaturesString = StringArray (missingFeatures.data(), (int) missingFeatures.size()).joinIntoString (", ");
5386
5387 return callback (nullptr, "plugin requires missing features: " + missingFeaturesString);
5388 }
5389
5390 auto stateToApply = [&]
5391 {
5392 if (! plugin.hasFeature (world->newUri (LV2_STATE__loadDefaultState)))
5393 return lv2_host::PluginState{};
5394
5395 auto map = symap->getMapFeature();
5396 return lv2_host::PluginState { lilv_state_new_from_world (world->get(), &map, plugin.getUri().get()) };
5397 }();
5398
5399 auto ports = lv2_host::Ports::getPorts (*world, uris, plugin, *symap);
5400
5401 if (! ports.hasValue())
5402 return callback (nullptr, "Plugin has ports of an unsupported type");
5403
5404 auto instance = std::make_unique<lv2_host::InstanceWithSupports> (*world,
5405 std::move (symap),
5406 plugin,
5407 std::move (*ports),
5408 (int32_t) initialBufferSize,
5409 initialSampleRate);
5410
5411 if (instance->instance == nullptr)
5412 return callback (nullptr, "Plugin was located, but could not be opened");
5413
5414 auto potentialPresets = world->findNodes (nullptr,
5415 world->newUri (LV2_CORE__appliesTo).get(),
5416 plugin.getUri().get());
5417
5418 const lv2_host::Uis pluginUis { plugin.get() };
5419
5420 const auto uiToUse = [&]() -> const LilvUI*
5421 {
5422 const auto bestMatch = findEmbeddableUi (&pluginUis);
5423
5424 if (bestMatch == nullptr)
5425 return bestMatch;
5426
5427 const auto uiUri = lilv_ui_get_uri (bestMatch);
5428 lilv_world_load_resource (world->get(), uiUri);
5429
5430 const auto queryUi = [&] (const char* featureUri)
5431 {
5432 const auto featureUriNode = world->newUri (featureUri);
5433 return world->findNodes (uiUri, featureUriNode.get(), nullptr);
5434 };
5435
5436 const auto missingUiFeatures = findMissingFeatures (queryUi (LV2_CORE__requiredFeature),
5437 lv2_host::UiFeaturesData::getFeatureUris());
5438
5439 return missingUiFeatures.empty() ? bestMatch : nullptr;
5440 }();
5441
5442 auto uiBundleUri = uiToUse != nullptr ? String::fromUTF8 (lilv_node_as_uri (lilv_ui_get_bundle_uri (uiToUse)))
5443 : String();
5444
5445 auto wrapped = std::make_unique<lv2_host::LV2AudioPluginInstance> (world,
5446 plugin,
5447 uris,
5448 std::move (instance),
5449 getDescription (pluginPtr),
5450 findPresetUrisForPlugin (plugin.get()),
5451 std::move (stateToApply),
5452 std::move (uiBundleUri),
5453 getUiDescriptor (uiToUse));
5454 callback (std::move (wrapped), {});
5455 }
5456
5457private:
5458 struct Free { void operator() (char* ptr) const noexcept { free (ptr); } };
5459 using StringPtr = std::unique_ptr<char, Free>;
5460
5461 const LilvPlugin* findPluginByUri (const String& s)
5462 {
5463 return world->getAllPlugins().getByUri (world->newUri (s.toRawUTF8()));
5464 }
5465
5466 template <typename Fn>
5467 void visitParentClasses (const LilvPluginClass* c, Fn&& fn) const
5468 {
5469 if (c == nullptr)
5470 return;
5471
5472 const lv2_host::PluginClass wrapped { c };
5473 fn (wrapped);
5474
5475 const auto parentUri = wrapped.getParentUri();
5476
5477 if (parentUri.get() != nullptr)
5478 visitParentClasses (world->getPluginClasses().getByUri (parentUri), fn);
5479 }
5480
5481 std::vector<lv2_host::NodeUri> collectPluginClassUris (const LilvPluginClass* c) const
5482 {
5483 std::vector<lv2_host::NodeUri> results;
5484
5485 visitParentClasses (c, [&results] (const lv2_host::PluginClass& wrapped)
5486 {
5487 results.emplace_back (wrapped.getUri());
5488 });
5489
5490 return results;
5491 }
5492
5493 PluginDescription getDescription (const LilvPlugin* plugin)
5494 {
5495 if (plugin == nullptr)
5496 return {};
5497
5498 const auto wrapped = lv2_host::Plugin { plugin };
5499 const auto bundle = wrapped.getBundleUri().getTyped();
5500 const auto bundleFile = File { StringPtr { lilv_file_uri_parse (bundle, nullptr) }.get() };
5501
5502 const auto numInputs = wrapped.getNumPortsOfClass (uris.mLV2_CORE__AudioPort, uris.mLV2_CORE__InputPort);
5503 const auto numOutputs = wrapped.getNumPortsOfClass (uris.mLV2_CORE__AudioPort, uris.mLV2_CORE__OutputPort);
5504
5505 PluginDescription result;
5506 result.name = wrapped.getName().getTyped();
5507 result.descriptiveName = wrapped.getName().getTyped();
5508 result.lastFileModTime = bundleFile.getLastModificationTime();
5509 result.lastInfoUpdateTime = Time::getCurrentTime();
5510 result.manufacturerName = wrapped.getAuthorName().getTyped();
5511 result.pluginFormatName = LV2PluginFormat::getFormatName();
5512 result.numInputChannels = static_cast<int> (numInputs);
5513 result.numOutputChannels = static_cast<int> (numOutputs);
5514
5515 const auto classPtr = wrapped.getClass();
5516 const auto classes = collectPluginClassUris (classPtr);
5517 const auto isInstrument = std::any_of (classes.cbegin(),
5518 classes.cend(),
5519 [this] (const lv2_host::NodeUri& uri)
5520 {
5521 return uri.equals (uris.mLV2_CORE__GeneratorPlugin);
5522 });
5523
5524 result.category = lv2_host::PluginClass { classPtr }.getLabel().getTyped();
5525 result.isInstrument = isInstrument;
5526
5527 // The plugin URI is required to be globally unique, so a hash of it should be too
5528 result.fileOrIdentifier = wrapped.getUri().getTyped();
5529
5530 const auto uid = DefaultHashFunctions::generateHash (result.fileOrIdentifier, std::numeric_limits<int>::max());;
5531 result.deprecatedUid = result.uniqueId = uid;
5532 return result;
5533 }
5534
5535 std::vector<String> findPresetUrisForPlugin (const LilvPlugin* plugin)
5536 {
5537 std::vector<String> presetUris;
5538
5539 lv2_host::OwningNodes potentialPresets { lilv_plugin_get_related (plugin, world->newUri (LV2_PRESETS__Preset).get()) };
5540
5541 for (const auto* potentialPreset : potentialPresets)
5542 presetUris.push_back (lilv_node_as_string (potentialPreset));
5543
5544 return presetUris;
5545 }
5546
5547 TemporaryFile lv2ResourceFolder;
5548 std::shared_ptr<lv2_host::World> world = std::make_shared<lv2_host::World>();
5549 lv2_host::UsefulUris uris { world->get() };
5550};
5551
5552//==============================================================================
5553LV2PluginFormat::LV2PluginFormat()
5554 : pimpl (std::make_unique<Pimpl>()) {}
5555
5556LV2PluginFormat::~LV2PluginFormat() = default;
5557
5558void LV2PluginFormat::findAllTypesForFile (OwnedArray<PluginDescription>& results,
5559 const String& fileOrIdentifier)
5560{
5561 pimpl->findAllTypesForFile (results, fileOrIdentifier);
5562}
5563
5564bool LV2PluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier)
5565{
5566 return pimpl->fileMightContainThisPluginType (fileOrIdentifier);
5567}
5568
5569String LV2PluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier)
5570{
5571 return pimpl->getNameOfPluginFromIdentifier (fileOrIdentifier);
5572}
5573
5574bool LV2PluginFormat::pluginNeedsRescanning (const PluginDescription& desc)
5575{
5576 return pimpl->pluginNeedsRescanning (desc);
5577}
5578
5579bool LV2PluginFormat::doesPluginStillExist (const PluginDescription& desc)
5580{
5581 return pimpl->doesPluginStillExist (desc);
5582}
5583
5584bool LV2PluginFormat::canScanForPlugins() const { return true; }
5585bool LV2PluginFormat::isTrivialToScan() const { return true; }
5586
5587StringArray LV2PluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch,
5588 bool recursive,
5589 bool allowAsync)
5590{
5591 return pimpl->searchPathsForPlugins (directoriesToSearch, recursive, allowAsync);
5592}
5593
5594FileSearchPath LV2PluginFormat::getDefaultLocationsToSearch()
5595{
5596 return pimpl->getDefaultLocationsToSearch();
5597}
5598
5599bool LV2PluginFormat::requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const
5600{
5601 return false;
5602}
5603
5604void LV2PluginFormat::createPluginInstance (const PluginDescription& desc,
5605 double sampleRate,
5606 int bufferSize,
5607 PluginCreationCallback callback)
5608{
5609 pimpl->createPluginInstance (desc, sampleRate, bufferSize, std::move (callback));
5610}
5611
5612} // namespace juce
5613
5614#endif
#define juce
Definition AppConfig.h:25
#define copy(x)
Definition ADnoteParameters.cpp:1011
#define constexpr
Definition CarlaDefines.h:94
Type jmax(const Type a, const Type b)
Definition MathsFunctions.h:48
Direction
Definition CarlaStyle.cpp:109
class MasterUI * ui
Definition Connection.cpp:39
Controller controller
Definition main.C:5
pthread_mutex_t mutex
Definition Controller.C:6
#define noexcept
Definition DistrhoDefines.h:72
#define nullptr
Definition DistrhoDefines.h:75
static Audio_Scope * scope
Definition player.cpp:26
uint8_t a
Definition Spc_Cpu.h:141
CAdPlugDatabase::CRecord::RecordType type
Definition adplugdb.cpp:93
static void message(int level, const char *fmt,...)
Definition adplugdb.cpp:120
event_queue * push(event_queue *queue, event_queue *event)
Definition allegrosmfwr.cpp:114
static char ** uris
Definition atom-test-utils.c:27
static uint8_t unmap(const uint8_t in)
Definition base64.c:94
static const LV2_Descriptor descriptor
Definition bindings_test_plugin.c:165
static void run(LV2_Handle instance, uint32_t n_samples)
Definition bindings_test_plugin.c:112
static void deactivate(LV2_Handle instance)
Definition bindings_test_plugin.c:128
static HINSTANCE getCurrentModuleInstanceHandle() noexcept
Definition carla-vst-export-bridged.cpp:35
#define pluginPtr
Definition carla-vst.cpp:950
static String addTrailingSeparator(const String &path)
Definition File.cpp:225
const String & getFullPathName() const noexcept
Definition File.h:152
size_t getSize() const noexcept
Definition MemoryBlock.h:102
void * getData() const noexcept
Definition MemoryBlock.h:91
void addEvent(const MidiMessage &midiMessage, int sampleNumber)
Definition MidiBuffer.cpp:118
void clear() noexcept
Definition MidiBuffer.cpp:106
Definition StringArray.h:41
Definition String.h:48
const char * toRawUTF8() const
Definition String.cpp:1925
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
Definition String.cpp:1961
const char * toRawUTF8() const
Definition String.cpp:1925
* e
Definition inflate.c:1404
int * l
Definition inflate.c:1579
unsigned * m
Definition inflate.c:1559
struct huft * t
Definition inflate.c:943
unsigned v[N_MAX]
Definition inflate.c:1584
unsigned d
Definition inflate.c:940
int g
Definition inflate.c:1573
struct huft * u[BMAX]
Definition inflate.c:1583
register unsigned i
Definition inflate.c:1575
unsigned s
Definition inflate.c:1555
unsigned x[BMAX+1]
Definition inflate.c:1586
unsigned f
Definition inflate.c:1572
static Wave_Writer * ww
Definition Wave_Writer.cpp:166
#define LV2_ATOM__Object
http://lv2plug.in/ns/ext/atom#Object
Definition atom.h:47
#define LV2_ATOM__AtomPort
http://lv2plug.in/ns/ext/atom#AtomPort
Definition atom.h:36
#define LV2_ATOM__Float
http://lv2plug.in/ns/ext/atom#Float
Definition atom.h:42
#define LV2_ATOM__beatTime
http://lv2plug.in/ns/ext/atom#beatTime
Definition atom.h:59
#define LV2_ATOM__Int
http://lv2plug.in/ns/ext/atom#Int
Definition atom.h:43
#define LV2_ATOM__atomTransfer
http://lv2plug.in/ns/ext/atom#atomTransfer
Definition atom.h:58
#define LV2_ATOM__frameTime
http://lv2plug.in/ns/ext/atom#frameTime
Definition atom.h:63
#define LV2_ATOM__Double
http://lv2plug.in/ns/ext/atom#Double
Definition atom.h:40
#define LV2_ATOM__Bool
http://lv2plug.in/ns/ext/atom#Bool
Definition atom.h:38
#define LV2_ATOM__Sequence
http://lv2plug.in/ns/ext/atom#Sequence
Definition atom.h:51
#define LV2_ATOM__Long
http://lv2plug.in/ns/ext/atom#Long
Definition atom.h:45
#define LV2_ATOM__eventTransfer
http://lv2plug.in/ns/ext/atom#eventTransfer
Definition atom.h:62
#define LV2_BUF_SIZE__minBlockLength
http://lv2plug.in/ns/ext/buf-size#minBlockLength
Definition buf-size.h:35
#define LV2_BUF_SIZE__sequenceSize
http://lv2plug.in/ns/ext/buf-size#sequenceSize
Definition buf-size.h:38
#define LV2_BUF_SIZE__maxBlockLength
http://lv2plug.in/ns/ext/buf-size#maxBlockLength
Definition buf-size.h:34
#define LV2_BUF_SIZE__boundedBlockLength
http://lv2plug.in/ns/ext/buf-size#boundedBlockLength
Definition buf-size.h:32
#define LV2_DATA_ACCESS_URI
http://lv2plug.in/ns/ext/data-access
Definition data-access.h:30
static PuglViewHint int value
Definition pugl.h:1708
static const char * name
Definition pugl.h:1582
static int int height
Definition pugl.h:1594
static int width
Definition pugl.h:1593
static uintptr_t parent
Definition pugl.h:1644
static LV2_Atom_Forge_Ref lv2_atom_forge_int(LV2_Atom_Forge *forge, int32_t val)
Definition atom-forge.h:374
static LV2_Atom_Forge_Ref lv2_atom_forge_bool(LV2_Atom_Forge *forge, bool val)
Definition atom-forge.h:406
static LV2_Atom_Forge_Ref lv2_atom_forge_long(LV2_Atom_Forge *forge, int64_t val)
Definition atom-forge.h:382
static LV2_Atom_Forge_Ref lv2_atom_forge_sequence_head(LV2_Atom_Forge *forge, LV2_Atom_Forge_Frame *frame, uint32_t unit)
Definition atom-forge.h:661
static bool lv2_atom_forge_is_object_type(const LV2_Atom_Forge *forge, uint32_t type)
Definition atom-forge.h:217
static LV2_Atom_Forge_Ref lv2_atom_forge_raw(LV2_Atom_Forge *forge, const void *data, uint32_t size)
Definition atom-forge.h:293
static LV2_Atom_Forge_Ref lv2_atom_forge_urid(LV2_Atom_Forge *forge, LV2_URID id)
Definition atom-forge.h:414
static LV2_Atom_Forge_Ref lv2_atom_forge_write(LV2_Atom_Forge *forge, const void *data, uint32_t size)
Definition atom-forge.h:324
static LV2_Atom_Forge_Ref lv2_atom_forge_double(LV2_Atom_Forge *forge, double val)
Definition atom-forge.h:398
static void lv2_atom_forge_pop(LV2_Atom_Forge *forge, LV2_Atom_Forge_Frame *frame)
Definition atom-forge.h:201
struct _LV2_Atom_Forge_Frame LV2_Atom_Forge_Frame
static LV2_Atom_Forge_Ref lv2_atom_forge_key(LV2_Atom_Forge *forge, LV2_URID key)
Definition atom-forge.h:635
static LV2_Atom_Forge_Ref lv2_atom_forge_atom(LV2_Atom_Forge *forge, uint32_t size, uint32_t type)
Definition atom-forge.h:354
static LV2_Atom_Forge_Ref lv2_atom_forge_frame_time(LV2_Atom_Forge *forge, int64_t frames)
Definition atom-forge.h:679
static LV2_Atom_Forge_Ref lv2_atom_forge_float(LV2_Atom_Forge *forge, float val)
Definition atom-forge.h:390
#define LV2_INSTANCE_ACCESS_URI
http://lv2plug.in/ns/ext/instance-access
Definition instance-access.h:30
LILV_API const LilvNode * lilv_plugin_class_get_label(const LilvPluginClass *plugin_class)
Definition pluginclass.c:67
LILV_API const LilvNode * lilv_plugin_class_get_uri(const LilvPluginClass *plugin_class)
Definition pluginclass.c:61
LILV_API LilvPluginClasses * lilv_plugin_class_get_children(const LilvPluginClass *plugin_class)
Definition pluginclass.c:73
LILV_API const LilvNode * lilv_plugin_class_get_parent_uri(const LilvPluginClass *plugin_class)
Definition pluginclass.c:55
LILV_API unsigned lilv_plugin_classes_size(const LilvPluginClasses *collection)
LILV_API const LilvUI * lilv_uis_get(const LilvUIs *collection, LilvIter *i)
LILV_API LilvIter * lilv_scale_points_next(const LilvScalePoints *collection, LilvIter *i)
LILV_API LilvIter * lilv_plugin_classes_begin(const LilvPluginClasses *collection)
LILV_API const LilvPluginClass * lilv_plugin_classes_get_by_uri(const LilvPluginClasses *classes, const LilvNode *uri)
Definition collections.c:115
LILV_API unsigned lilv_uis_size(const LilvUIs *collection)
LILV_API void lilv_plugin_classes_free(LilvPluginClasses *collection)
Definition collections.c:212
LILV_API bool lilv_scale_points_is_end(const LilvScalePoints *collection, LilvIter *i)
LILV_API LilvIter * lilv_uis_begin(const LilvUIs *collection)
LILV_API LilvIter * lilv_nodes_begin(const LilvNodes *collection)
LILV_API LilvIter * lilv_uis_next(const LilvUIs *collection, LilvIter *i)
LILV_API const LilvPlugin * lilv_plugins_get_by_uri(const LilvPlugins *plugins, const LilvNode *uri)
Definition collections.c:137
LILV_API bool lilv_plugin_classes_is_end(const LilvPluginClasses *collection, LilvIter *i)
LILV_API LilvIter * lilv_plugins_next(const LilvPlugins *collection, LilvIter *i)
LILV_API const LilvPlugin * lilv_plugins_get(const LilvPlugins *collection, LilvIter *i)
LILV_API LilvIter * lilv_plugin_classes_next(const LilvPluginClasses *collection, LilvIter *i)
LILV_API void lilv_uis_free(LilvUIs *collection)
Definition collections.c:224
LILV_API unsigned lilv_plugins_size(const LilvPlugins *collection)
LILV_API const LilvScalePoint * lilv_scale_points_get(const LilvScalePoints *collection, LilvIter *i)
LILV_API const LilvUI * lilv_uis_get_by_uri(const LilvUIs *uis, const LilvNode *uri)
Definition collections.c:123
LILV_API bool lilv_uis_is_end(const LilvUIs *collection, LilvIter *i)
LILV_API bool lilv_plugins_is_end(const LilvPlugins *collection, LilvIter *i)
LILV_API bool lilv_nodes_is_end(const LilvNodes *collection, LilvIter *i)
LILV_API LilvIter * lilv_nodes_next(const LilvNodes *collection, LilvIter *i)
LILV_API void lilv_nodes_free(LilvNodes *collection)
Definition collections.c:230
LILV_API const LilvNode * lilv_nodes_get(const LilvNodes *collection, LilvIter *i)
LILV_API const LilvPluginClass * lilv_plugin_classes_get(const LilvPluginClasses *collection, LilvIter *i)
LILV_API LilvIter * lilv_scale_points_begin(const LilvScalePoints *collection)
LILV_API unsigned lilv_nodes_size(const LilvNodes *collection)
LILV_API LilvIter * lilv_plugins_begin(const LilvPlugins *collection)
static void lilv_instance_connect_port(LilvInstance *instance, uint32_t port_index, void *data_location)
Definition lilv.h:1916
static const char * lilv_instance_get_uri(const LilvInstance *instance)
Definition lilv.h:1904
static const void * lilv_instance_get_extension_data(const LilvInstance *instance, const char *uri)
Definition lilv.h:1972
LILV_API LilvInstance * lilv_plugin_instantiate(const LilvPlugin *plugin, double sample_rate, const LV2_Feature *const *features)
Definition instance.c:30
static void lilv_instance_run(LilvInstance *instance, uint32_t sample_count)
Definition lilv.h:1946
static void lilv_instance_deactivate(LilvInstance *instance)
Definition lilv.h:1958
static LV2_Handle lilv_instance_get_handle(const LilvInstance *instance)
Definition lilv.h:2004
LILV_API void lilv_instance_free(LilvInstance *instance)
Definition instance.c:104
static void lilv_instance_activate(LilvInstance *instance)
Definition lilv.h:1932
LILV_API bool lilv_node_is_string(const LilvNode *value)
Definition node.c:351
LILV_API LilvNode * lilv_new_uri(LilvWorld *world, const char *uri)
Definition node.c:155
LILV_API LilvNode * lilv_new_file_uri(LilvWorld *world, const char *host, const char *path)
Definition node.c:161
LILV_API int lilv_node_as_int(const LilvNode *value)
Definition node.c:369
LILV_API bool lilv_node_equals(const LilvNode *value, const LilvNode *other)
Definition node.c:238
LILV_API LilvNode * lilv_new_string(LilvWorld *world, const char *str)
Definition node.c:174
LILV_API bool lilv_node_is_float(const LilvNode *value)
Definition node.c:375
LILV_API const char * lilv_node_as_string(const LilvNode *value)
Definition node.c:357
LILV_API float lilv_node_as_float(const LilvNode *value)
Definition node.c:381
LILV_API const char * lilv_node_as_uri(const LilvNode *value)
Definition node.c:311
LILV_API char * lilv_file_uri_parse(const char *uri, char **hostname)
Definition util.c:106
LILV_API LilvNode * lilv_node_duplicate(const LilvNode *val)
Definition node.c:214
LILV_API bool lilv_node_is_int(const LilvNode *value)
Definition node.c:363
LILV_API void lilv_node_free(LilvNode *val)
Definition node.c:229
LILV_API bool lilv_node_is_uri(const LilvNode *value)
Definition node.c:305
LILV_API LilvNodes * lilv_plugin_get_value(const LilvPlugin *plugin, const LilvNode *predicate)
Definition plugin.c:521
LILV_API LilvNodes * lilv_plugin_get_required_features(const LilvPlugin *plugin)
Definition plugin.c:787
LILV_API const LilvNode * lilv_plugin_get_bundle_uri(const LilvPlugin *plugin)
Definition plugin.c:379
LILV_API LilvNode * lilv_plugin_get_author_name(const LilvPlugin *plugin)
Definition plugin.c:922
LILV_API bool lilv_plugin_has_latency(const LilvPlugin *plugin)
Definition plugin.c:637
LILV_API const LilvNode * lilv_plugin_get_uri(const LilvPlugin *plugin)
Definition plugin.c:373
LILV_API bool lilv_plugin_has_feature(const LilvPlugin *plugin, const LilvNode *feature)
Definition plugin.c:749
LILV_API const LilvPort * lilv_plugin_get_port_by_designation(const LilvPlugin *plugin, const LilvNode *port_class, const LilvNode *designation)
Definition plugin.c:699
LILV_API bool lilv_plugin_has_extension_data(const LilvPlugin *plugin, const LilvNode *uri)
Definition plugin.c:797
LILV_API const LilvPluginClass * lilv_plugin_get_class(const LilvPlugin *plugin)
Definition plugin.c:418
LILV_API uint32_t lilv_plugin_get_num_ports_of_class(const LilvPlugin *plugin, const LilvNode *class_1,...)
Definition plugin.c:623
LILV_API bool lilv_plugin_verify(const LilvPlugin *plugin)
Definition plugin.c:464
LILV_API uint32_t lilv_plugin_get_num_ports(const LilvPlugin *plugin)
Definition plugin.c:529
LILV_API const LilvNode * lilv_plugin_get_library_uri(const LilvPlugin *plugin)
Definition plugin.c:385
LILV_API LilvNodes * lilv_plugin_get_optional_features(const LilvPlugin *plugin)
Definition plugin.c:777
LILV_API LilvNodes * lilv_plugin_get_related(const LilvPlugin *plugin, const LilvNode *type)
Definition plugin.c:997
LILV_API LilvNode * lilv_plugin_get_name(const LilvPlugin *plugin)
Definition plugin.c:498
LILV_API uint32_t lilv_plugin_get_latency_port_index(const LilvPlugin *plugin)
Definition plugin.c:724
LILV_API const LilvPort * lilv_plugin_get_port_by_index(const LilvPlugin *plugin, uint32_t index)
Definition plugin.c:820
LILV_API const LilvNode * lilv_port_get_symbol(const LilvPlugin *plugin, const LilvPort *port)
Definition port.c:161
LILV_API LilvNodes * lilv_port_get_properties(const LilvPlugin *plugin, const LilvPort *port)
Definition port.c:265
LILV_API uint32_t lilv_port_get_index(const LilvPlugin *plugin, const LilvPort *port)
Definition port.c:153
LILV_API void lilv_port_get_range(const LilvPlugin *plugin, const LilvPort *port, LilvNode **def, LilvNode **min, LilvNode **max)
Definition port.c:200
LILV_API bool lilv_port_has_property(const LilvPlugin *plugin, const LilvPort *port, const LilvNode *property)
Definition port.c:77
LILV_API bool lilv_port_is_a(const LilvPlugin *plugin, const LilvPort *port, const LilvNode *port_class)
Definition port.c:61
LILV_API LilvNode * lilv_port_get_name(const LilvPlugin *plugin, const LilvPort *port)
Definition port.c:169
LILV_API LilvScalePoints * lilv_port_get_scale_points(const LilvPlugin *plugin, const LilvPort *port)
Definition port.c:232
LILV_API bool lilv_port_supports_event(const LilvPlugin *plugin, const LilvPort *port, const LilvNode *event_type)
Definition port.c:88
LILV_API LilvNode * lilv_port_get(const LilvPlugin *plugin, const LilvPort *port, const LilvNode *predicate)
Definition port.c:139
LILV_API const LilvNodes * lilv_port_get_classes(const LilvPlugin *plugin, const LilvPort *port)
Definition port.c:192
LILV_API const LilvNode * lilv_scale_point_get_label(const LilvScalePoint *point)
Definition scalepoint.c:50
LILV_API const LilvNode * lilv_scale_point_get_value(const LilvScalePoint *point)
Definition scalepoint.c:44
LILV_API LilvState * lilv_state_new_from_world(LilvWorld *world, LV2_URID_Map *map, const LilvNode *node)
Definition state.c:720
LILV_API LilvState * lilv_state_new_from_string(LilvWorld *world, LV2_URID_Map *map, const char *str)
Definition state.c:788
LILV_API LilvState * lilv_state_new_from_instance(const LilvPlugin *plugin, LilvInstance *instance, LV2_URID_Map *map, const char *scratch_dir, const char *copy_dir, const char *link_dir, const char *save_dir, LilvGetPortValueFunc get_value, void *user_data, uint32_t flags, const LV2_Feature *const *features)
Definition state.c:429
LILV_API void lilv_state_free(LilvState *state)
Definition state.c:1436
LILV_API const char * lilv_state_get_label(const LilvState *state)
Definition state.c:1518
LILV_API void lilv_state_set_label(LilvState *state, const char *label)
Definition state.c:1524
LILV_API void lilv_state_restore(const LilvState *state, LilvInstance *instance, LilvSetPortValueFunc set_value, void *user_data, uint32_t flags, const LV2_Feature *const *features)
Definition state.c:522
LILV_API char * lilv_state_to_string(LilvWorld *world, LV2_URID_Map *map, LV2_URID_Unmap *unmap, const LilvState *state, const char *uri, const char *base_uri)
Definition state.c:1280
LILV_API const LilvNode * lilv_ui_get_bundle_uri(const LilvUI *ui)
Definition ui.c:106
LILV_API const LilvNode * lilv_ui_get_binary_uri(const LilvUI *ui)
Definition ui.c:112
LILV_API LilvUIs * lilv_plugin_get_uis(const LilvPlugin *plugin)
Definition plugin.c:946
LILV_API const LilvNode * lilv_ui_get_uri(const LilvUI *ui)
Definition ui.c:66
LILV_API LilvNode * lilv_world_get(LilvWorld *world, const LilvNode *subject, const LilvNode *predicate, const LilvNode *object)
Definition world.c:242
LILV_API LilvNodes * lilv_world_find_nodes(LilvWorld *world, const LilvNode *subject, const LilvNode *predicate, const LilvNode *object)
Definition world.c:208
LILV_API void lilv_world_load_bundle(LilvWorld *world, const LilvNode *bundle_uri)
Definition world.c:741
LILV_API LilvWorld * lilv_world_new(void)
Definition world.c:45
LILV_API int lilv_world_load_resource(LilvWorld *world, const LilvNode *resource)
Definition world.c:1123
LILV_API void lilv_world_load_specifications(LilvWorld *world)
Definition world.c:1008
LILV_API void lilv_world_load_all(LilvWorld *world)
Definition world.c:1058
LILV_API void lilv_world_load_plugin_classes(LilvWorld *world)
Definition world.c:1019
LILV_API const LilvPluginClasses * lilv_world_get_plugin_classes(const LilvWorld *world)
Definition world.c:1192
LILV_API const LilvPlugins * lilv_world_get_all_plugins(const LilvWorld *world)
Definition world.c:1198
LILV_API void lilv_world_free(LilvWorld *world)
Definition world.c:129
LILV_API int lilv_world_unload_bundle(LilvWorld *world, const LilvNode *bundle_uri)
Definition world.c:898
LILV_API int lilv_world_unload_resource(LilvWorld *world, const LilvNode *resource)
Definition world.c:1154
LILV_API bool lilv_world_ask(LilvWorld *world, const LilvNode *subject, const LilvNode *predicate, const LilvNode *object)
Definition world.c:296
void LilvScalePoints
Definition lilv.h:95
struct LilvStateImpl LilvState
Definition lilv.h:90
void LilvPluginClasses
Definition lilv.h:93
void LilvNodes
Definition lilv.h:97
struct LilvUIImpl LilvUI
Definition lilv.h:86
LILV_API void lilv_free(void *ptr)
Definition util.c:37
struct LilvScalePointImpl LilvScalePoint
Definition lilv.h:85
struct LilvPortImpl LilvPort
Definition lilv.h:84
struct LilvWorldImpl LilvWorld
Definition lilv.h:88
struct LilvPluginClassImpl LilvPluginClass
Definition lilv.h:83
struct LilvPluginImpl LilvPlugin
Definition lilv.h:82
struct LilvInstanceImpl LilvInstance
Definition lilv.h:89
struct LilvNodeImpl LilvNode
Definition lilv.h:87
void LilvPlugins
Definition lilv.h:94
void LilvIter
Definition lilv.h:92
void LilvUIs
Definition lilv.h:96
#define LV2_LOG__Warning
http://lv2plug.in/ns/ext/log#Warning
Definition log.h:36
#define LV2_LOG__Error
http://lv2plug.in/ns/ext/log#Error
Definition log.h:33
struct _LV2_Log LV2_Log_Log
#define LV2_LOG__log
http://lv2plug.in/ns/ext/log#log
Definition log.h:37
void * LV2_Log_Handle
Definition log.h:59
#define LV2_LOG__Trace
http://lv2plug.in/ns/ext/log#Trace
Definition log.h:35
#define LV2_LOG__Note
http://lv2plug.in/ns/ext/log#Note
Definition log.h:34
#define LV2_CORE__integer
http://lv2plug.in/ns/lv2core#integer
Definition lv2.h:102
#define LV2_CORE__GeneratorPlugin
http://lv2plug.in/ns/lv2core#GeneratorPlugin
Definition lv2.h:61
#define LV2_CORE__minimum
http://lv2plug.in/ns/lv2core#minimum
Definition lv2.h:107
#define LV2_CORE__maximum
http://lv2plug.in/ns/lv2core#maximum
Definition lv2.h:105
#define LV2_CORE_PREFIX
http://lv2plug.in/ns/lv2core#
Definition lv2.h:35
#define LV2_CORE__freeWheeling
http://lv2plug.in/ns/lv2core#freeWheeling
Definition lv2.h:98
#define LV2_CORE__InputPort
http://lv2plug.in/ns/lv2core#InputPort
Definition lv2.h:63
#define LV2_CORE__symbol
http://lv2plug.in/ns/lv2core#symbol
Definition lv2.h:119
#define LV2_CORE__AudioPort
http://lv2plug.in/ns/lv2core#AudioPort
Definition lv2.h:40
#define LV2_CORE__control
http://lv2plug.in/ns/lv2core#control
Definition lv2.h:92
void * LV2_Handle
Definition lv2.h:133
#define LV2_CORE__portProperty
http://lv2plug.in/ns/lv2core#portProperty
Definition lv2.h:112
#define LV2_CORE__default
http://lv2plug.in/ns/lv2core#default
Definition lv2.h:93
#define LV2_CORE__InstrumentPlugin
http://lv2plug.in/ns/lv2core#InstrumentPlugin
Definition lv2.h:64
#define LV2_CORE__optionalFeature
http://lv2plug.in/ns/lv2core#optionalFeature
Definition lv2.h:110
#define LV2_CORE__toggled
http://lv2plug.in/ns/lv2core#toggled
Definition lv2.h:120
#define LV2_CORE__CVPort
http://lv2plug.in/ns/lv2core#CVPort
Definition lv2.h:42
#define LV2_CORE__scalePoint
http://lv2plug.in/ns/lv2core#scalePoint
Definition lv2.h:118
#define LV2_CORE__name
http://lv2plug.in/ns/lv2core#name
Definition lv2.h:109
#define LV2_CORE__designation
http://lv2plug.in/ns/lv2core#designation
Definition lv2.h:94
#define LV2_CORE__appliesTo
http://lv2plug.in/ns/lv2core#appliesTo
Definition lv2.h:89
#define LV2_CORE__connectionOptional
http://lv2plug.in/ns/lv2core#connectionOptional
Definition lv2.h:91
#define LV2_CORE__ControlPort
http://lv2plug.in/ns/lv2core#ControlPort
Definition lv2.h:47
#define LV2_CORE__enumeration
http://lv2plug.in/ns/lv2core#enumeration
Definition lv2.h:96
#define LV2_CORE__requiredFeature
http://lv2plug.in/ns/lv2core#requiredFeature
Definition lv2.h:116
#define LV2_CORE__extensionData
http://lv2plug.in/ns/lv2core#extensionData
Definition lv2.h:97
#define LV2_CORE__OutputPort
http://lv2plug.in/ns/lv2core#OutputPort
Definition lv2.h:71
struct _LV2_Feature LV2_Feature
#define LV2_MIDI__MidiEvent
http://lv2plug.in/ns/ext/midi#MidiEvent
Definition midi.h:48
struct _LV2_Options_Interface LV2_Options_Interface
#define LV2_OPTIONS__interface
http://lv2plug.in/ns/ext/options#interface
Definition options.h:38
struct _LV2_Options_Option LV2_Options_Option
#define LV2_OPTIONS__options
http://lv2plug.in/ns/ext/options#options
Definition options.h:39
@ LV2_OPTIONS_INSTANCE
Definition options.h:55
#define LV2_PARAMETERS__sampleRate
http://lv2plug.in/ns/ext/parameters#sampleRate
Definition parameters.h:51
#define LV2_PATCH__Set
http://lv2plug.in/ns/ext/patch#Set
Definition patch.h:47
#define LV2_PATCH__writable
http://lv2plug.in/ns/ext/patch#writable
Definition patch.h:60
#define LV2_PATCH__value
http://lv2plug.in/ns/ext/patch#value
Definition patch.h:58
#define LV2_PATCH__property
http://lv2plug.in/ns/ext/patch#property
Definition patch.h:52
#define LV2_PORT_GROUPS__mainInput
http://lv2plug.in/ns/ext/port-groups#mainInput
Definition port-groups.h:54
#define LV2_PORT_GROUPS__subGroupOf
http://lv2plug.in/ns/ext/port-groups#subGroupOf
Definition port-groups.h:65
#define LV2_PORT_GROUPS__OutputGroup
http://lv2plug.in/ns/ext/port-groups#OutputGroup
Definition port-groups.h:41
#define LV2_PORT_GROUPS__mainOutput
http://lv2plug.in/ns/ext/port-groups#mainOutput
Definition port-groups.h:55
#define LV2_PORT_GROUPS__Group
http://lv2plug.in/ns/ext/port-groups#Group
Definition port-groups.h:37
#define LV2_PORT_GROUPS__InputGroup
http://lv2plug.in/ns/ext/port-groups#InputGroup
Definition port-groups.h:38
#define LV2_PORT_GROUPS__group
http://lv2plug.in/ns/ext/port-groups#group
Definition port-groups.h:51
#define LV2_PRESETS__Preset
http://lv2plug.in/ns/ext/presets#Preset
Definition presets.h:32
#define LV2_RESIZE_PORT__resize
http://lv2plug.in/ns/ext/port#resize
Definition resize-port.h:36
#define LV2_RESIZE_PORT__minimumSize
http://lv2plug.in/ns/ext/port#minimumSize
Definition resize-port.h:35
LV2_Resize_Port_Status
Definition resize-port.h:45
void * LV2_Resize_Port_Feature_Data
Definition resize-port.h:52
@ LV2_RESIZE_PORT_ERR_UNKNOWN
Definition resize-port.h:47
@ LV2_RESIZE_PORT_SUCCESS
Definition resize-port.h:46
SERD_PURE_API bool serd_uri_string_has_scheme(const uint8_t *SERD_NULLABLE utf8)
Return true iff utf8 starts with a valid URI scheme.
#define LV2_STATE__loadDefaultState
http://lv2plug.in/ns/ext/state#loadDefaultState
Definition state.h:42
#define LV2_STATE__threadSafeRestore
http://lv2plug.in/ns/ext/state#threadSafeRestore
Definition state.h:47
#define LV2_STATE__StateChanged
http://lv2plug.in/ns/ext/state#StateChanged
Definition state.h:48
@ LV2_STATE_IS_PORTABLE
Definition state.h:89
@ LV2_STATE_IS_POD
Definition state.h:78
#define LV2_TIME__beat
http://lv2plug.in/ns/ext/time#beat
Definition time.h:41
#define LV2_TIME__frame
http://lv2plug.in/ns/ext/time#frame
Definition time.h:45
#define LV2_TIME__barBeat
http://lv2plug.in/ns/ext/time#barBeat
Definition time.h:39
#define LV2_TIME__Position
http://lv2plug.in/ns/ext/time#Position
Definition time.h:36
#define LV2_TIME__beatsPerMinute
http://lv2plug.in/ns/ext/time#beatsPerMinute
Definition time.h:44
#define LV2_TIME__speed
http://lv2plug.in/ns/ext/time#speed
Definition time.h:47
#define LV2_TIME__beatUnit
http://lv2plug.in/ns/ext/time#beatUnit
Definition time.h:42
#define LV2_TIME__beatsPerBar
http://lv2plug.in/ns/ext/time#beatsPerBar
Definition time.h:43
#define LV2_TIME__bar
http://lv2plug.in/ns/ext/time#bar
Definition time.h:40
void * LV2UI_Controller
Definition ui.h:104
#define LV2_UI__portMap
http://lv2plug.in/ns/extensions/ui#portMap
Definition ui.h:59
#define LV2_UI__X11UI
http://lv2plug.in/ns/extensions/ui#X11UI
Definition ui.h:48
#define LV2_UI__floatProtocol
http://lv2plug.in/ns/extensions/ui#floatProtocol
Definition ui.h:63
#define LV2_UI__CocoaUI
http://lv2plug.in/ns/extensions/ui#CocoaUI
Definition ui.h:39
struct _LV2UI_Idle_Interface LV2UI_Idle_Interface
#define LV2_UI__idleInterface
http://lv2plug.in/ns/extensions/ui#idleInterface
Definition ui.h:53
#define LV2_UI__touch
http://lv2plug.in/ns/extensions/ui#touch
Definition ui.h:69
void * LV2UI_Widget
Definition ui.h:90
#define LV2_UI__parent
http://lv2plug.in/ns/extensions/ui#parent
Definition ui.h:56
struct _LV2UI_Port_Map LV2UI_Port_Map
struct _LV2UI_Descriptor LV2UI_Descriptor
#define LV2_UI__resize
http://lv2plug.in/ns/extensions/ui#resize
Definition ui.h:66
#define LV2_UI__WindowsUI
http://lv2plug.in/ns/extensions/ui#WindowsUI
Definition ui.h:47
#define LV2_UI__scaleFactor
http://lv2plug.in/ns/extensions/ui#scaleFactor
Definition ui.h:67
#define LV2_UI__noUserResize
http://lv2plug.in/ns/extensions/ui#noUserResize
Definition ui.h:54
struct _LV2UI_Resize LV2UI_Resize
void * LV2UI_Handle
Definition ui.h:97
void * LV2UI_Feature_Handle
Definition ui.h:109
#define LV2UI_INVALID_PORT_INDEX
Definition ui.h:77
struct _LV2UI_Touch LV2UI_Touch
#define LV2_UNITS__frame
http://lv2plug.in/ns/ext/units#frame
Definition units.h:43
#define LV2_UNITS__beat
http://lv2plug.in/ns/ext/units#beat
Definition units.h:35
void * LV2_URID_Map_Handle
Definition urid.h:48
#define LV2_URID__map
http://lv2plug.in/ns/ext/urid#map
Definition urid.h:33
uint32_t LV2_URID
Definition urid.h:58
struct _LV2_URID_Unmap LV2_URID_Unmap
void * LV2_URID_Unmap_Handle
Definition urid.h:53
struct _LV2_URID_Map LV2_URID_Map
#define LV2_URID__unmap
http://lv2plug.in/ns/ext/urid#unmap
Definition urid.h:34
#define LV2_WORKER__schedule
http://lv2plug.in/ns/ext/worker#schedule
Definition worker.h:37
LV2_Worker_Status
Definition worker.h:46
struct _LV2_Worker_Schedule LV2_Worker_Schedule
#define LV2_WORKER__interface
http://lv2plug.in/ns/ext/worker#interface
Definition worker.h:36
void * LV2_Worker_Schedule_Handle
Definition worker.h:124
struct _LV2_Worker_Interface LV2_Worker_Interface
void * LV2_Worker_Respond_Handle
Definition worker.h:53
@ LV2_WORKER_SUCCESS
Definition worker.h:47
@ LV2_WORKER_ERR_UNKNOWN
Definition worker.h:48
@ LV2_WORKER_ERR_NO_SPACE
Definition worker.h:49
struct backing_store_struct * info
Definition jmemsys.h:183
JSAMPIMAGE data
Definition jpeglib.h:945
#define JUCE_BEGIN_IGNORE_WARNINGS_MSVC(warnings)
Definition juce_CompilerWarnings.h:198
#define JUCE_END_IGNORE_WARNINGS_MSVC
Definition juce_CompilerWarnings.h:199
#define X(str)
Definition juce_LV2Common.h:197
#define JUCE_LEAK_DETECTOR(OwnerClass)
Definition juce_LeakedObjectDetector.h:138
#define TRANS(stringLiteral)
Definition juce_LocalisedStrings.h:208
#define JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
Definition juce_MessageManager.h:465
#define JUCE_ASSERT_MESSAGE_THREAD
Definition juce_MessageManager.h:473
#define jassert(expression)
#define JUCE_DECLARE_NON_MOVEABLE(className)
#define jassertquiet(expression)
#define JUCE_DECLARE_NON_COPYABLE(className)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
#define jassertfalse
#define LILV_NS_RDFS
Definition lilv.h:61
#define LILV_NS_RDF
Definition lilv.h:60
static SordNode * uri(SordWorld *world, int num)
Definition sord_test.c:47
static const LV2_Lib_Descriptor lib
Definition lib_descriptor.c:100
float control
Definition lilv_test.c:1462
static LilvWorld * world
Definition lilv_test.c:64
float in
Definition lilv_test.c:1460
static int JUCE_CDECL comp(const void *a, const void *b)
Definition lsp.c:298
int int32_t
Definition mid.cpp:97
unsigned int uint32_t
Definition mid.cpp:100
static void ** pointers
Definition misc.c:24
@ unknown
Definition VstPlugin.cpp:75
bool verify(tresult result)
Definition vstpresetfile.cpp:75
void move(void *from, void *to)
Definition juce_FixedSizeFunction.h:53
auto & get(ProcessorChain< Processors... > &chain) noexcept
Definition juce_ProcessorChain.h:133
JOCTET * buffer
Definition juce_JPEGLoader.cpp:302
Definition carla_juce.cpp:31
jack_client_t client jack_client_t client jack_client_t client jack_client_t JackInfoShutdownCallback void arg jack_client_t jack_port_t * port
Definition juce_linux_JackAudio.cpp:65
@ no
Definition juce_AlertWindow.cpp:567
@ yes
Definition juce_AlertWindow.cpp:567
CriticalSection::ScopedLockType ScopedLock
Definition juce_CriticalSection.h:186
juce::String toString(const Steinberg::char8 *string) noexcept
Definition juce_VST3Common.h:159
Type unalignedPointerCast(void *ptr) noexcept
Definition juce_Memory.h:88
JUCE_END_IGNORE_WARNINGS_MSVC Optional< std::decay_t< Value > > makeOptional(Value &&v)
Definition juce_Optional.h:298
RangedDirectoryIterator end(const RangedDirectoryIterator &)
Definition juce_RangedDirectoryIterator.h:184
jack_client_t client jack_client_t client jack_client_t client jack_client_t JackInfoShutdownCallback void arg jack_client_t jack_port_t port void func jack_client_t const char const char unsigned long flags const jack_port_t port jack_client_t jack_port_id_t port_id const jack_port_t const char port_name const jack_port_t port void * ptr
Definition juce_linux_JackAudio.cpp:79
void ignoreUnused(Types &&...) noexcept
Definition juce_MathsFunctions.h:333
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Definition juce_MathsFunctions.h:279
void writeUnaligned(void *dstPtr, Type value) noexcept
Definition juce_Memory.h:74
@ window
Definition juce_AccessibilityRole.h:63
@ list
Definition juce_AccessibilityRole.h:56
@ tree
Definition juce_AccessibilityRole.h:58
@ label
Definition juce_AccessibilityRole.h:44
@ group
Definition juce_AccessibilityRole.h:61
RangedDirectoryIterator begin(const RangedDirectoryIterator &it)
Definition juce_RangedDirectoryIterator.h:179
constexpr Nullopt nullopt
Definition juce_Optional.h:48
std::unique_ptr< T > rawToUniquePtr(T *ptr)
Definition juce_Memory.h:195
jack_client_t client jack_client_t client jack_client_t client jack_client_t JackInfoShutdownCallback void arg jack_client_t jack_port_t port void func jack_client_t const char const char unsigned long flags
Definition juce_linux_JackAudio.cpp:69
auto getText(std::string_view name) -> QString
Definition embed.cpp:125
float maximum(const float *abs_spectrum, unsigned int spec_size)
Definition fft_helpers.cpp:40
rtosc::Ports ports
Definition MiddleWare.cpp:383
const AbsTime * time
Definition PADnoteParameters.h:189
#define true
Definition ordinals.h:82
#define false
Definition ordinals.h:83
#define min(x, y)
Definition os.h:74
#define max(x, y)
Definition os.h:78
void * data
Definition lv2.h:157
uint32_t size
Definition atom-forge.h:105
uint8_t * buf
Definition atom-forge.h:103
uint32_t size
Definition atom.h:107
uint32_t type
Definition atom.h:108
const char * URI
Definition lv2.h:188
void(* cleanup)(LV2_Handle instance)
Definition lv2.h:337
LV2_Handle(* instantiate)(const struct LV2_Descriptor *descriptor, double sample_rate, const char *bundle_path, const LV2_Feature *const *features)
Definition lv2.h:217
void * data
Definition lv2.h:171
LONG bottom
Definition swell-types.h:232
LONG left
Definition swell-types.h:232
LONG top
Definition swell-types.h:232
LONG right
Definition swell-types.h:232
itr_t end() const
Definition ports.h:171
itr_t begin() const
Definition ports.h:168
size_t size() const
Definition ports.h:174
#define WM_SIZE
const char const char const char const char char * fn
Definition swell-functions.h:168
const char * text
Definition swell-functions.h:167
RECT const char void(* callback)(const char *droppath))) SWELL_API_DEFINE(BOOL
Definition swell-functions.h:1004
RECT const char void HWND hwnd
Definition swell-functions.h:1066
bool GetWindowRect(HWND hwnd, RECT *r)
Definition swell-generic-headless.cpp:130
void * HINSTANCE
Definition swell-types.h:212
LONG_PTR LRESULT
Definition swell-types.h:171
unsigned int UINT
Definition swell-types.h:166
LONG_PTR LPARAM
Definition swell-types.h:170
ULONG_PTR WPARAM
Definition swell-types.h:169
struct HWND__ * HWND
Definition swell-types.h:210
#define CALLBACK
Definition swell-types.h:635
DWORD GetCurrentThreadId()
Definition swell.cpp:471
static LV2_State_Status save(LV2_Handle instance, LV2_State_Store_Function store, void *callback_data, uint32_t flags, const LV2_Feature *const *features)
Definition test.c:161
static LV2_State_Status restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, void *callback_data, uint32_t flags, const LV2_Feature *const *features)
Definition test.c:292
uch * p
Definition crypt.c:594
return c
Definition crypt.c:175
for(n=0;n< RAND_HEAD_LEN;n++)
Definition crypt.c:467
int r
Definition crypt.c:458
uch h[RAND_HEAD_LEN]
Definition crypt.c:459
b
Definition crypt.c:628
ulg size
Definition extract.c:2350
fmt[0]
Definition fileio.c:2503
int result
Definition process.c:1455
typedef int(UZ_EXP MsgFn)()
#define void
Definition unzip.h:396
struct zdirent * file
Definition win32.c:1500
_WDL_CSTRING_PREFIX void INT_PTR const char * format
Definition wdlcstring.h:263
#define const
Definition zconf.h:137