LMMS
Loading...
Searching...
No Matches
juce_VST3PluginFormat.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_VST3 && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD)
27
28#include "juce_VST3Headers.h"
29#include "juce_VST3Common.h"
30#include "juce_ARACommon.h"
31
32#if JUCE_PLUGINHOST_ARA && (JUCE_MAC || JUCE_WINDOWS)
33#include <ARA_API/ARAVST3.h>
34
35namespace ARA
36{
37DEF_CLASS_IID (IMainFactory)
38DEF_CLASS_IID (IPlugInEntryPoint)
39DEF_CLASS_IID (IPlugInEntryPoint2)
40}
41#endif
42
43namespace juce
44{
45
46// UB Sanitizer doesn't necessarily have instrumentation for loaded plugins, so
47// it won't recognize the dynamic types of pointers to the plugin's interfaces.
49
50using namespace Steinberg;
51
52//==============================================================================
53#ifndef JUCE_VST3_DEBUGGING
54 #define JUCE_VST3_DEBUGGING 0
55#endif
56
57#if JUCE_VST3_DEBUGGING
58 #define VST3_DBG(a) Logger::writeToLog (a);
59#else
60 #define VST3_DBG(a)
61#endif
62
63#if JUCE_DEBUG
64static int warnOnFailure (int result) noexcept
65{
66 const char* message = "Unknown result!";
67
68 switch (result)
69 {
70 case kResultOk: return result;
71 case kNotImplemented: message = "kNotImplemented"; break;
72 case kNoInterface: message = "kNoInterface"; break;
73 case kResultFalse: message = "kResultFalse"; break;
74 case kInvalidArgument: message = "kInvalidArgument"; break;
75 case kInternalError: message = "kInternalError"; break;
76 case kNotInitialized: message = "kNotInitialized"; break;
77 case kOutOfMemory: message = "kOutOfMemory"; break;
78 default: break;
79 }
80
81 DBG (message);
82 return result;
83}
84
85static int warnOnFailureIfImplemented (int result) noexcept
86{
87 if (result != kResultOk && result != kNotImplemented)
88 return warnOnFailure (result);
89
90 return result;
91}
92#else
93 #define warnOnFailure(x) x
94 #define warnOnFailureIfImplemented(x) x
95#endif
96
97enum class Direction { input, output };
98enum class MediaKind { audio, event };
99
100static Vst::MediaType toVstType (MediaKind x) { return x == MediaKind::audio ? Vst::kAudio : Vst::kEvent; }
101static Vst::BusDirection toVstType (Direction x) { return x == Direction::input ? Vst::kInput : Vst::kOutput; }
102
103static std::vector<Vst::ParamID> getAllParamIDs (Vst::IEditController& controller)
104{
105 std::vector<Vst::ParamID> result;
106
107 auto count = controller.getParameterCount();
108
109 for (decltype (count) i = 0; i < count; ++i)
110 {
111 Vst::ParameterInfo info{};
112 controller.getParameterInfo (i, info);
113 result.push_back (info.id);
114 }
115
116 return result;
117}
118
119//==============================================================================
120/* Allows parameter updates to be queued up without blocking,
121 and automatically dispatches these updates on the main thread.
122*/
123class EditControllerParameterDispatcher : private Timer
124{
125public:
126 ~EditControllerParameterDispatcher() override { stopTimer(); }
127
128 void push (Steinberg::int32 index, float value)
129 {
130 if (controller == nullptr)
131 return;
132
133 if (MessageManager::getInstance()->isThisTheMessageThread())
134 controller->setParamNormalized (cache.getParamID (index), value);
135 else
136 cache.set (index, value);
137 }
138
139 void start (Vst::IEditController& controllerIn)
140 {
141 controller = &controllerIn;
142 cache = CachedParamValues { getAllParamIDs (controllerIn) };
143 startTimerHz (60);
144 }
145
146 void flush()
147 {
148 cache.ifSet ([this] (Steinberg::int32 index, float value)
149 {
150 controller->setParamNormalized (cache.getParamID (index), value);
151 });
152 }
153
154private:
155 void timerCallback() override
156 {
157 flush();
158 }
159
160 CachedParamValues cache;
161 Vst::IEditController* controller = nullptr;
162};
163
164//==============================================================================
165static std::array<uint32, 4> getNormalisedTUID (const TUID& tuid) noexcept
166{
167 const FUID fuid { tuid };
168 return { { fuid.getLong1(), fuid.getLong2(), fuid.getLong3(), fuid.getLong4() } };
169}
170
171template <typename Range>
172static int getHashForRange (Range&& range) noexcept
173{
174 uint32 value = 0;
175
176 for (const auto& item : range)
177 value = (value * 31) + (uint32) item;
178
179 return (int) value;
180}
181
182template <typename ObjectType>
183static void fillDescriptionWith (PluginDescription& description, ObjectType& object)
184{
185 description.version = toString (object.version).trim();
186 description.category = toString (object.subCategories).trim();
187
188 if (description.manufacturerName.trim().isEmpty())
189 description.manufacturerName = toString (object.vendor).trim();
190}
191
192static void createPluginDescription (PluginDescription& description,
193 const File& pluginFile, const String& company, const String& name,
194 const PClassInfo& info, PClassInfo2* info2, PClassInfoW* infoW,
195 int numInputs, int numOutputs)
196{
197 description.fileOrIdentifier = pluginFile.getFullPathName();
198 description.lastFileModTime = pluginFile.getLastModificationTime();
199 description.lastInfoUpdateTime = Time::getCurrentTime();
200 description.manufacturerName = company;
201 description.name = name;
202 description.descriptiveName = name;
203 description.pluginFormatName = "VST3";
204 description.numInputChannels = numInputs;
205 description.numOutputChannels = numOutputs;
206
207 description.deprecatedUid = getHashForRange (info.cid);
208 description.uniqueId = getHashForRange (getNormalisedTUID (info.cid));
209
210 if (infoW != nullptr) fillDescriptionWith (description, *infoW);
211 else if (info2 != nullptr) fillDescriptionWith (description, *info2);
212
213 if (description.category.isEmpty())
214 description.category = toString (info.category).trim();
215
216 description.isInstrument = description.category.containsIgnoreCase ("Instrument"); // This seems to be the only way to find that out! ARGH!
217}
218
219static int getNumSingleDirectionBusesFor (Vst::IComponent* component,
220 MediaKind kind,
221 Direction direction)
222{
223 jassert (component != nullptr);
225 return (int) component->getBusCount (toVstType (kind), toVstType (direction));
226}
227
229static int getNumSingleDirectionChannelsFor (Vst::IComponent* component, Direction busDirection)
230{
231 jassert (component != nullptr);
233
234 const auto direction = toVstType (busDirection);
235 const Steinberg::int32 numBuses = component->getBusCount (Vst::kAudio, direction);
236
237 int numChannels = 0;
238
239 for (Steinberg::int32 i = numBuses; --i >= 0;)
240 {
241 Vst::BusInfo busInfo;
242 warnOnFailure (component->getBusInfo (Vst::kAudio, direction, i, busInfo));
243 numChannels += ((busInfo.flags & Vst::BusInfo::kDefaultActive) != 0 ? (int) busInfo.channelCount : 0);
244 }
245
246 return numChannels;
247}
248
249static void setStateForAllEventBuses (Vst::IComponent* component,
250 bool state,
251 Direction busDirection)
252{
253 jassert (component != nullptr);
255
256 const auto direction = toVstType (busDirection);
257 const Steinberg::int32 numBuses = component->getBusCount (Vst::kEvent, direction);
258
259 for (Steinberg::int32 i = numBuses; --i >= 0;)
260 warnOnFailure (component->activateBus (Vst::kEvent, direction, i, state));
261}
262
263//==============================================================================
264static void toProcessContext (Vst::ProcessContext& context,
265 AudioPlayHead* playHead,
266 double sampleRate)
267{
268 jassert (sampleRate > 0.0); //Must always be valid, as stated by the VST3 SDK
269
270 using namespace Vst;
271
272 zerostruct (context);
273 context.sampleRate = sampleRate;
274
275 const auto position = playHead != nullptr ? playHead->getPosition()
276 : nullopt;
277
278 if (position.hasValue())
279 {
280 if (const auto timeInSamples = position->getTimeInSamples())
281 context.projectTimeSamples = *timeInSamples;
282 else
283 jassertfalse; // The time in samples *must* be valid.
284
285 if (const auto tempo = position->getBpm())
286 {
287 context.state |= ProcessContext::kTempoValid;
288 context.tempo = *tempo;
289 }
290
291 if (const auto loop = position->getLoopPoints())
292 {
293 context.state |= ProcessContext::kCycleValid;
294 context.cycleStartMusic = loop->ppqStart;
295 context.cycleEndMusic = loop->ppqEnd;
296 }
297
298 if (const auto sig = position->getTimeSignature())
299 {
300 context.state |= ProcessContext::kTimeSigValid;
301 context.timeSigNumerator = sig->numerator;
302 context.timeSigDenominator = sig->denominator;
303 }
304
305 if (const auto pos = position->getPpqPosition())
306 {
307 context.state |= ProcessContext::kProjectTimeMusicValid;
308 context.projectTimeMusic = *pos;
309 }
310
311 if (const auto barStart = position->getPpqPositionOfLastBarStart())
312 {
313 context.state |= ProcessContext::kBarPositionValid;
314 context.barPositionMusic = *barStart;
315 }
316
317 if (const auto frameRate = position->getFrameRate())
318 {
319 if (const auto offset = position->getEditOriginTime())
320 {
321 context.state |= ProcessContext::kSmpteValid;
322 context.smpteOffsetSubframes = (Steinberg::int32) (80.0 * *offset * frameRate->getEffectiveRate());
323 context.frameRate.framesPerSecond = (Steinberg::uint32) frameRate->getBaseRate();
324 context.frameRate.flags = (Steinberg::uint32) ((frameRate->isDrop() ? FrameRate::kDropRate : 0)
325 | (frameRate->isPullDown() ? FrameRate::kPullDownRate : 0));
326 }
327 }
328
329 if (const auto hostTime = position->getHostTimeNs())
330 {
331 context.state |= ProcessContext::kSystemTimeValid;
332 context.systemTime = (int64_t) *hostTime;
333 jassert (context.systemTime >= 0);
334 }
335
336 if (position->getIsPlaying()) context.state |= ProcessContext::kPlaying;
337 if (position->getIsRecording()) context.state |= ProcessContext::kRecording;
338 if (position->getIsLooping()) context.state |= ProcessContext::kCycleActive;
339 }
340}
341
342//==============================================================================
343class VST3PluginInstance;
344
345struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0
346 public Vst::IComponentHandler2, // From VST V3.1.0 (a very well named class, of course!)
347 public Vst::IComponentHandler3, // From VST V3.5.0 (also very well named!)
348 public Vst::IContextMenuTarget,
349 public Vst::IHostApplication,
350 public Vst::IUnitHandler,
351 private ComponentRestarter::Listener
352{
353 VST3HostContext()
354 {
355 appName = File::getSpecialLocation (File::currentApplicationFile).getFileNameWithoutExtension();
356 }
357
358 ~VST3HostContext() override = default;
359
361
362 FUnknown* getFUnknown() { return static_cast<Vst::IComponentHandler*> (this); }
363
364 static bool hasFlag (Steinberg::int32 source, Steinberg::int32 flag) noexcept
365 {
366 return (source & flag) == flag;
367 }
368
369 //==============================================================================
370 tresult PLUGIN_API beginEdit (Vst::ParamID paramID) override;
371 tresult PLUGIN_API performEdit (Vst::ParamID paramID, Vst::ParamValue valueNormalized) override;
372 tresult PLUGIN_API endEdit (Vst::ParamID paramID) override;
373
374 tresult PLUGIN_API restartComponent (Steinberg::int32 flags) override;
375 tresult PLUGIN_API setDirty (TBool) override;
376
377 //==============================================================================
378 tresult PLUGIN_API requestOpenEditor (FIDString name) override
379 {
382 return kResultFalse;
383 }
384
385 tresult PLUGIN_API startGroupEdit() override
386 {
388 return kResultFalse;
389 }
390
391 tresult PLUGIN_API finishGroupEdit() override
392 {
394 return kResultFalse;
395 }
396
397 void setPlugin (VST3PluginInstance* instance)
398 {
399 jassert (plugin == nullptr);
400 plugin = instance;
401 }
402
403 //==============================================================================
404 struct ContextMenu : public Vst::IContextMenu
405 {
406 ContextMenu (VST3PluginInstance& pluginInstance) : owner (pluginInstance) {}
407 virtual ~ContextMenu() {}
408
411
412 Steinberg::int32 PLUGIN_API getItemCount() override { return (Steinberg::int32) items.size(); }
413
414 tresult PLUGIN_API addItem (const Item& item, IContextMenuTarget* target) override
415 {
416 jassert (target != nullptr);
417
418 ItemAndTarget newItem;
419 newItem.item = item;
420 newItem.target = target;
421
422 items.add (newItem);
423 return kResultOk;
424 }
425
426 tresult PLUGIN_API removeItem (const Item& toRemove, IContextMenuTarget* target) override
427 {
428 for (int i = items.size(); --i >= 0;)
429 {
430 auto& item = items.getReference(i);
431
432 if (item.item.tag == toRemove.tag && item.target == target)
433 items.remove (i);
434 }
435
436 return kResultOk;
437 }
438
439 tresult PLUGIN_API getItem (Steinberg::int32 tag, Item& result, IContextMenuTarget** target) override
440 {
441 for (int i = 0; i < items.size(); ++i)
442 {
443 auto& item = items.getReference(i);
444
445 if (item.item.tag == tag)
446 {
447 result = item.item;
448
449 if (target != nullptr)
450 *target = item.target;
451
452 return kResultTrue;
453 }
454 }
455
457 return kResultFalse;
458 }
459
460 tresult PLUGIN_API popup (Steinberg::UCoord x, Steinberg::UCoord y) override;
461
462 #if ! JUCE_MODAL_LOOPS_PERMITTED
463 static void menuFinished (int modalResult, VSTComSmartPtr<ContextMenu> menu) { menu->handleResult (modalResult); }
464 #endif
465
466 private:
467 enum { zeroTagReplacement = 0x7fffffff };
468
469 Atomic<int> refCount;
470 VST3PluginInstance& owner;
471
472 struct ItemAndTarget
473 {
474 Item item;
475 VSTComSmartPtr<IContextMenuTarget> target;
476 };
477
478 Array<ItemAndTarget> items;
479
480 void handleResult (int result)
481 {
482 if (result == 0)
483 return;
484
485 if (result == zeroTagReplacement)
486 result = 0;
487
488 for (int i = 0; i < items.size(); ++i)
489 {
490 auto& item = items.getReference(i);
491
492 if ((int) item.item.tag == result)
493 {
494 if (item.target != nullptr)
495 item.target->executeMenuItem ((Steinberg::int32) result);
496
497 break;
498 }
499 }
500 }
501
503 };
504
505 Vst::IContextMenu* PLUGIN_API createContextMenu (IPlugView*, const Vst::ParamID*) override
506 {
507 if (plugin != nullptr)
508 return new ContextMenu (*plugin);
509
510 return nullptr;
511 }
512
513 tresult PLUGIN_API executeMenuItem (Steinberg::int32) override
514 {
516 return kResultFalse;
517 }
518
519 //==============================================================================
520 tresult PLUGIN_API getName (Vst::String128 name) override
521 {
522 Steinberg::String str (appName.toUTF8());
523 str.copyTo (name, 0, 127);
524 return kResultOk;
525 }
526
527 tresult PLUGIN_API createInstance (TUID cid, TUID iid, void** obj) override
528 {
529 *obj = nullptr;
530
531 if (! doUIDsMatch (cid, iid))
532 {
534 return kInvalidArgument;
535 }
536
537 if (doUIDsMatch (cid, Vst::IMessage::iid) && doUIDsMatch (iid, Vst::IMessage::iid))
538 {
539 *obj = new Message;
540 return kResultOk;
541 }
542
543 if (doUIDsMatch (cid, Vst::IAttributeList::iid) && doUIDsMatch (iid, Vst::IAttributeList::iid))
544 {
545 *obj = new AttributeList;
546 return kResultOk;
547 }
548
550 return kNotImplemented;
551 }
552
553 //==============================================================================
554 tresult PLUGIN_API notifyUnitSelection (Vst::UnitID) override
555 {
557 return kResultFalse;
558 }
559
560 tresult PLUGIN_API notifyProgramListChange (Vst::ProgramListID, Steinberg::int32) override;
561
562 //==============================================================================
563 tresult PLUGIN_API queryInterface (const TUID iid, void** obj) override
564 {
565 return testForMultiple (*this,
566 iid,
567 UniqueBase<Vst::IComponentHandler>{},
568 UniqueBase<Vst::IComponentHandler2>{},
569 UniqueBase<Vst::IComponentHandler3>{},
570 UniqueBase<Vst::IContextMenuTarget>{},
571 UniqueBase<Vst::IHostApplication>{},
572 UniqueBase<Vst::IUnitHandler>{},
573 SharedBase<FUnknown, Vst::IComponentHandler>{}).extract (obj);
574 }
575
576private:
577 //==============================================================================
578 VST3PluginInstance* plugin = nullptr;
579 Atomic<int> refCount;
580 String appName;
581
582 ComponentRestarter componentRestarter { *this };
583
584 void restartComponentOnMessageThread (int32 flags) override;
585
586 //==============================================================================
587 class Attribute
588 {
589 public:
590 using Int = Steinberg::int64;
591 using Float = double;
592 using String = std::vector<Vst::TChar>;
593 using Binary = std::vector<char>;
594
595 explicit Attribute (Int x) noexcept { constructFrom (std::move (x)); }
596 explicit Attribute (Float x) noexcept { constructFrom (std::move (x)); }
597 explicit Attribute (String x) noexcept { constructFrom (std::move (x)); }
598 explicit Attribute (Binary x) noexcept { constructFrom (std::move (x)); }
599
600 Attribute (Attribute&& other) noexcept
601 {
602 moveFrom (std::move (other));
603 }
604
605 Attribute& operator= (Attribute&& other) noexcept
606 {
607 reset();
608 moveFrom (std::move (other));
609 return *this;
610 }
611
612 ~Attribute() noexcept
613 {
614 reset();
615 }
616
617 tresult getInt (Steinberg::int64& result) const
618 {
619 if (kind != Kind::tagInt)
620 return kResultFalse;
621
622 result = storage.storedInt;
623 return kResultTrue;
624 }
625
626 tresult getFloat (double& result) const
627 {
628 if (kind != Kind::tagFloat)
629 return kResultFalse;
630
631 result = storage.storedFloat;
632 return kResultTrue;
633 }
634
635 tresult getString (Vst::TChar* data, Steinberg::uint32 numBytes) const
636 {
637 if (kind != Kind::tagString)
638 return kResultFalse;
639
640 std::memcpy (data,
641 storage.storedString.data(),
642 jmin (sizeof (Vst::TChar) * storage.storedString.size(), (size_t) numBytes));
643 return kResultTrue;
644 }
645
646 tresult getBinary (const void*& data, Steinberg::uint32& numBytes) const
647 {
648 if (kind != Kind::tagBinary)
649 return kResultFalse;
650
651 data = storage.storedBinary.data();
652 numBytes = (Steinberg::uint32) storage.storedBinary.size();
653 return kResultTrue;
654 }
655
656 private:
657 void constructFrom (Int x) noexcept { kind = Kind::tagInt; new (&storage.storedInt) Int (std::move (x)); }
658 void constructFrom (Float x) noexcept { kind = Kind::tagFloat; new (&storage.storedFloat) Float (std::move (x)); }
659 void constructFrom (String x) noexcept { kind = Kind::tagString; new (&storage.storedString) String (std::move (x)); }
660 void constructFrom (Binary x) noexcept { kind = Kind::tagBinary; new (&storage.storedBinary) Binary (std::move (x)); }
661
662 void reset() noexcept
663 {
664 switch (kind)
665 {
666 case Kind::tagInt: break;
667 case Kind::tagFloat: break;
668 case Kind::tagString: storage.storedString.~vector(); break;
669 case Kind::tagBinary: storage.storedBinary.~vector(); break;
670 }
671 }
672
673 void moveFrom (Attribute&& other) noexcept
674 {
675 switch (other.kind)
676 {
677 case Kind::tagInt: constructFrom (std::move (other.storage.storedInt)); break;
678 case Kind::tagFloat: constructFrom (std::move (other.storage.storedFloat)); break;
679 case Kind::tagString: constructFrom (std::move (other.storage.storedString)); break;
680 case Kind::tagBinary: constructFrom (std::move (other.storage.storedBinary)); break;
681 }
682 }
683
684 enum class Kind { tagInt, tagFloat, tagString, tagBinary };
685
686 union Storage
687 {
688 Storage() {}
689 ~Storage() {}
690
691 Steinberg::int64 storedInt;
692 double storedFloat;
693 std::vector<Vst::TChar> storedString;
694 std::vector<char> storedBinary;
695 };
696
697 Storage storage;
698 Kind kind;
699
701 };
702
703 //==============================================================================
704 class AttributeList : public Vst::IAttributeList
705 {
706 public:
707 AttributeList() = default;
708 virtual ~AttributeList() = default;
709
712
713 //==============================================================================
714 tresult PLUGIN_API setInt (AttrID attr, Steinberg::int64 value) override
715 {
716 return set (attr, value);
717 }
718
719 tresult PLUGIN_API setFloat (AttrID attr, double value) override
720 {
721 return set (attr, value);
722 }
723
724 tresult PLUGIN_API setString (AttrID attr, const Vst::TChar* string) override
725 {
726 return set (attr, std::vector<Vst::TChar> (string, string + 1 + tstrlen (string)));
727 }
728
729 tresult PLUGIN_API setBinary (AttrID attr, const void* data, Steinberg::uint32 size) override
730 {
731 const auto* ptr = static_cast<const char*> (data);
732 return set (attr, std::vector<char> (ptr, ptr + size));
733 }
734
735 tresult PLUGIN_API getInt (AttrID attr, Steinberg::int64& result) override
736 {
737 return get (attr, [&] (const auto& x) { return x.getInt (result); });
738 }
739
740 tresult PLUGIN_API getFloat (AttrID attr, double& result) override
741 {
742 return get (attr, [&] (const auto& x) { return x.getFloat (result); });
743 }
744
745 tresult PLUGIN_API getString (AttrID attr, Vst::TChar* result, Steinberg::uint32 length) override
746 {
747 return get (attr, [&] (const auto& x) { return x.getString (result, length); });
748 }
749
750 tresult PLUGIN_API getBinary (AttrID attr, const void*& data, Steinberg::uint32& size) override
751 {
752 return get (attr, [&] (const auto& x) { return x.getBinary (data, size); });
753 }
754
755 private:
756 template <typename Value>
757 tresult set (AttrID attr, Value&& value)
758 {
759 if (attr == nullptr)
760 return kInvalidArgument;
761
762 const auto iter = attributes.find (attr);
763
764 if (iter != attributes.end())
765 iter->second = Attribute (std::move (value));
766 else
767 attributes.emplace (attr, Attribute (std::move (value)));
768
769 return kResultTrue;
770 }
771
772 template <typename Visitor>
773 tresult get (AttrID attr, Visitor&& visitor)
774 {
775 if (attr == nullptr)
776 return kInvalidArgument;
777
778 const auto iter = attributes.find (attr);
779
780 if (iter == attributes.cend())
781 return kResultFalse;
782
783 return visitor (iter->second);
784 }
785
786 std::map<std::string, Attribute> attributes;
787 Atomic<int> refCount { 1 };
788
790 };
791
792 struct Message : public Vst::IMessage
793 {
794 Message() = default;
795 virtual ~Message() = default;
796
799
800 FIDString PLUGIN_API getMessageID() override { return messageId.toRawUTF8(); }
801 void PLUGIN_API setMessageID (FIDString id) override { messageId = toString (id); }
802 Vst::IAttributeList* PLUGIN_API getAttributes() override { return &attributeList; }
803
804 private:
805 AttributeList attributeList;
806 String messageId;
807 Atomic<int> refCount { 1 };
808
810 };
811
812 VSTComSmartPtr<AttributeList> attributeList;
813
815};
816
817//==============================================================================
818struct DescriptionFactory
819{
820 DescriptionFactory (VST3HostContext* host, IPluginFactory* pluginFactory)
821 : vst3HostContext (host), factory (pluginFactory)
822 {
823 jassert (pluginFactory != nullptr);
824 }
825
826 virtual ~DescriptionFactory() {}
827
828 Result findDescriptionsAndPerform (const File& file)
829 {
830 StringArray foundNames;
831 PFactoryInfo factoryInfo;
832 factory->getFactoryInfo (&factoryInfo);
833 auto companyName = toString (factoryInfo.vendor).trim();
834
835 Result result (Result::ok());
836
837 auto numClasses = factory->countClasses();
838
839 // Every ARA::IMainFactory must have a matching Steinberg::IComponent.
840 // The match is determined by the two classes having the same name.
841 std::unordered_set<String> araMainFactoryClassNames;
842
843 #if JUCE_PLUGINHOST_ARA && (JUCE_MAC || JUCE_WINDOWS)
844 for (Steinberg::int32 i = 0; i < numClasses; ++i)
845 {
846 PClassInfo info;
847 factory->getClassInfo (i, &info);
848 if (std::strcmp (info.category, kARAMainFactoryClass) == 0)
849 araMainFactoryClassNames.insert (info.name);
850 }
851 #endif
852
853 for (Steinberg::int32 i = 0; i < numClasses; ++i)
854 {
855 PClassInfo info;
856 factory->getClassInfo (i, &info);
857
858 if (std::strcmp (info.category, kVstAudioEffectClass) != 0)
859 continue;
860
861 const String name (toString (info.name).trim());
862
863 if (foundNames.contains (name, true))
864 continue;
865
866 std::unique_ptr<PClassInfo2> info2;
867 std::unique_ptr<PClassInfoW> infoW;
868
869 {
870 VSTComSmartPtr<IPluginFactory2> pf2;
871 VSTComSmartPtr<IPluginFactory3> pf3;
872
873 if (pf2.loadFrom (factory))
874 {
875 info2.reset (new PClassInfo2());
876 pf2->getClassInfo2 (i, info2.get());
877 }
878
879 if (pf3.loadFrom (factory))
880 {
881 infoW.reset (new PClassInfoW());
882 pf3->getClassInfoUnicode (i, infoW.get());
883 }
884 }
885
886 foundNames.add (name);
887
888 PluginDescription desc;
889
890 {
891 VSTComSmartPtr<Vst::IComponent> component;
892
893 if (component.loadFrom (factory, info.cid))
894 {
895 if (component->initialize (vst3HostContext->getFUnknown()) == kResultOk)
896 {
897 auto numInputs = getNumSingleDirectionChannelsFor (component, Direction::input);
898 auto numOutputs = getNumSingleDirectionChannelsFor (component, Direction::output);
899
900 createPluginDescription (desc, file, companyName, name,
901 info, info2.get(), infoW.get(), numInputs, numOutputs);
902
903 component->terminate();
904 }
905 else
906 {
908 }
909 }
910 else
911 {
913 }
914 }
915
916 if (araMainFactoryClassNames.find (name) != araMainFactoryClassNames.end())
917 desc.hasARAExtension = true;
918
919 if (desc.uniqueId != 0)
920 result = performOnDescription (desc);
921
922 if (result.failed())
923 break;
924 }
925
926 return result;
927 }
928
929 virtual Result performOnDescription (PluginDescription&) = 0;
930
931private:
932 VSTComSmartPtr<VST3HostContext> vst3HostContext;
933 VSTComSmartPtr<IPluginFactory> factory;
934
936};
937
938struct DescriptionLister : public DescriptionFactory
939{
940 DescriptionLister (VST3HostContext* host, IPluginFactory* pluginFactory)
941 : DescriptionFactory (host, pluginFactory)
942 {
943 }
944
945 Result performOnDescription (PluginDescription& desc)
946 {
947 list.add (new PluginDescription (desc));
948 return Result::ok();
949 }
950
951 OwnedArray<PluginDescription> list;
952
953private:
955};
956
957//==============================================================================
958struct DLLHandle
959{
960 DLLHandle (const File& fileToOpen)
961 : dllFile (fileToOpen)
962 {
963 open();
964 }
965
966 ~DLLHandle()
967 {
968 #if JUCE_MAC
969 if (bundleRef != nullptr)
970 #endif
971 {
972 if (factory != nullptr)
973 factory->release();
974
975 using ExitModuleFn = bool (PLUGIN_API*) ();
976
977 if (auto* exitFn = (ExitModuleFn) getFunction (exitFnName))
978 exitFn();
979
980 #if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD
981 library.close();
982 #endif
983 }
984 }
985
986 //==============================================================================
990 IPluginFactory* JUCE_CALLTYPE getPluginFactory()
991 {
992 if (factory == nullptr)
993 if (auto* proc = (GetFactoryProc) getFunction (factoryFnName))
994 factory = proc();
995
996 // The plugin NEEDS to provide a factory to be able to be called a VST3!
997 // Most likely you are trying to load a 32-bit VST3 from a 64-bit host
998 // or vice versa.
999 jassert (factory != nullptr);
1000 return factory;
1001 }
1002
1003 void* getFunction (const char* functionName)
1004 {
1005 #if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD
1006 return library.getFunction (functionName);
1007 #elif JUCE_MAC
1008 if (bundleRef == nullptr)
1009 return nullptr;
1010
1011 CFUniquePtr<CFStringRef> name (String (functionName).toCFString());
1012 return CFBundleGetFunctionPointerForName (bundleRef.get(), name.get());
1013 #endif
1014 }
1015
1016 File getFile() const noexcept { return dllFile; }
1017
1018private:
1019 File dllFile;
1020 IPluginFactory* factory = nullptr;
1021
1022 static constexpr const char* factoryFnName = "GetPluginFactory";
1023
1024 #if JUCE_WINDOWS
1025 static constexpr const char* entryFnName = "InitDll";
1026 static constexpr const char* exitFnName = "ExitDll";
1027
1028 using EntryProc = bool (PLUGIN_API*) ();
1029 #elif JUCE_LINUX || JUCE_BSD
1030 static constexpr const char* entryFnName = "ModuleEntry";
1031 static constexpr const char* exitFnName = "ModuleExit";
1032
1033 using EntryProc = bool (PLUGIN_API*) (void*);
1034 #elif JUCE_MAC
1035 static constexpr const char* entryFnName = "bundleEntry";
1036 static constexpr const char* exitFnName = "bundleExit";
1037
1038 using EntryProc = bool (*) (CFBundleRef);
1039 #endif
1040
1041 //==============================================================================
1042 #if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD
1043 DynamicLibrary library;
1044
1045 bool open()
1046 {
1047 if (library.open (dllFile.getFullPathName()))
1048 {
1049 if (auto* proc = (EntryProc) getFunction (entryFnName))
1050 {
1051 #if JUCE_WINDOWS
1052 if (proc())
1053 #else
1054 if (proc (library.getNativeHandle()))
1055 #endif
1056 return true;
1057 }
1058 else
1059 {
1060 // this is required for some plug-ins which don't export the dll entry point function
1061 return true;
1062 }
1063
1064 library.close();
1065 }
1066
1067 return false;
1068 }
1069 #elif JUCE_MAC
1070 CFUniquePtr<CFBundleRef> bundleRef;
1071
1072 bool open()
1073 {
1074 auto* utf8 = dllFile.getFullPathName().toRawUTF8();
1075
1076 if (auto url = CFUniquePtr<CFURLRef> (CFURLCreateFromFileSystemRepresentation (nullptr,
1077 (const UInt8*) utf8,
1078 (CFIndex) std::strlen (utf8),
1079 dllFile.isDirectory())))
1080 {
1081 bundleRef.reset (CFBundleCreate (kCFAllocatorDefault, url.get()));
1082
1083 if (bundleRef != nullptr)
1084 {
1085 CFObjectHolder<CFErrorRef> error;
1086
1087 if (CFBundleLoadExecutableAndReturnError (bundleRef.get(), &error.object))
1088 if (auto* proc = (EntryProc) getFunction (entryFnName))
1089 if (proc (bundleRef.get()))
1090 return true;
1091
1092 if (error.object != nullptr)
1093 if (auto failureMessage = CFUniquePtr<CFStringRef> (CFErrorCopyFailureReason (error.object)))
1094 DBG (String::fromCFString (failureMessage.get()));
1095
1096 bundleRef = nullptr;
1097 }
1098 }
1099
1100 return false;
1101 }
1102 #endif
1103
1104 //==============================================================================
1106};
1107
1108struct DLLHandleCache : public DeletedAtShutdown
1109{
1110 DLLHandleCache() = default;
1111 ~DLLHandleCache() override { clearSingletonInstance(); }
1112
1113 JUCE_DECLARE_SINGLETON (DLLHandleCache, false)
1114
1115 DLLHandle& findOrCreateHandle (const String& modulePath)
1116 {
1117 #if JUCE_LINUX || JUCE_BSD
1118 File file (getDLLFileFromBundle (modulePath));
1119 #else
1120 File file (modulePath);
1121 #endif
1122
1123 auto it = std::find_if (openHandles.begin(), openHandles.end(),
1124 [&] (const std::unique_ptr<DLLHandle>& handle)
1125 {
1126 return file == handle->getFile();
1127 });
1128
1129 if (it != openHandles.end())
1130 return *it->get();
1131
1132 openHandles.push_back (std::make_unique<DLLHandle> (file));
1133 return *openHandles.back().get();
1134 }
1135
1136private:
1137 #if JUCE_LINUX || JUCE_BSD
1138 File getDLLFileFromBundle (const String& bundlePath) const
1139 {
1140 auto machineName = []() -> String
1141 {
1142 struct utsname unameData;
1143 auto res = uname (&unameData);
1144
1145 if (res != 0)
1146 return {};
1147
1148 return unameData.machine;
1149 }();
1150
1151 File file (bundlePath);
1152
1153 return file.getChildFile ("Contents")
1154 .getChildFile (machineName + "-linux")
1155 .getChildFile (file.getFileNameWithoutExtension() + ".so");
1156 }
1157 #endif
1158
1159 std::vector<std::unique_ptr<DLLHandle>> openHandles;
1160
1161 //==============================================================================
1163};
1164
1165
1166JUCE_IMPLEMENT_SINGLETON (DLLHandleCache)
1167
1168//==============================================================================
1169#if JUCE_LINUX || JUCE_BSD
1170
1171class RunLoop final : public Steinberg::Linux::IRunLoop
1172{
1173public:
1174 RunLoop() = default;
1175
1176 ~RunLoop()
1177 {
1178 for (const auto& h : eventHandlerMap)
1179 LinuxEventLoop::unregisterFdCallback (h.first);
1180 }
1181
1182 //==============================================================================
1183 tresult PLUGIN_API registerEventHandler (Linux::IEventHandler* handler,
1184 Linux::FileDescriptor fd) override
1185 {
1186 if (handler == nullptr)
1187 return kInvalidArgument;
1188
1189 auto& handlers = eventHandlerMap[fd];
1190
1191 if (handlers.empty())
1192 {
1193 LinuxEventLoop::registerFdCallback (fd, [this] (int descriptor)
1194 {
1195 for (auto* h : eventHandlerMap[descriptor])
1196 h->onFDIsSet (descriptor);
1197
1198 return true;
1199 });
1200 }
1201
1202 handlers.push_back (handler);
1203
1204 return kResultTrue;
1205 }
1206
1207 tresult PLUGIN_API unregisterEventHandler (Linux::IEventHandler* handler) override
1208 {
1209 if (handler == nullptr)
1210 return kInvalidArgument;
1211
1212 for (auto iter = eventHandlerMap.begin(), end = eventHandlerMap.end(); iter != end;)
1213 {
1214 auto& handlers = iter->second;
1215
1216 auto handlersIter = std::find (std::begin (handlers), std::end (handlers), handler);
1217
1218 if (handlersIter != std::end (handlers))
1219 {
1220 handlers.erase (handlersIter);
1221
1222 if (handlers.empty())
1223 {
1224 LinuxEventLoop::unregisterFdCallback (iter->first);
1225 iter = eventHandlerMap.erase (iter);
1226 continue;
1227 }
1228 }
1229
1230 ++iter;
1231 }
1232
1233 return kResultTrue;
1234 }
1235
1236 //==============================================================================
1237 tresult PLUGIN_API registerTimer (Linux::ITimerHandler* handler, Linux::TimerInterval milliseconds) override
1238 {
1239 if (handler == nullptr || milliseconds <= 0)
1240 return kInvalidArgument;
1241
1242 timerCallers.emplace_back (handler, (int) milliseconds);
1243 return kResultTrue;
1244 }
1245
1246 tresult PLUGIN_API unregisterTimer (Linux::ITimerHandler* handler) override
1247 {
1248 auto iter = std::find (timerCallers.begin(), timerCallers.end(), handler);
1249
1250 if (iter == timerCallers.end())
1251 return kInvalidArgument;
1252
1253 timerCallers.erase (iter);
1254 return kResultTrue;
1255 }
1256
1257 //==============================================================================
1258 uint32 PLUGIN_API addRef() override { return 1000; }
1259 uint32 PLUGIN_API release() override { return 1000; }
1260 tresult PLUGIN_API queryInterface (const TUID, void**) override { return kNoInterface; }
1261
1262private:
1263 //==============================================================================
1264 struct TimerCaller : private Timer
1265 {
1266 TimerCaller (Linux::ITimerHandler* h, int interval) : handler (h) { startTimer (interval); }
1267 ~TimerCaller() override { stopTimer(); }
1268
1269 void timerCallback() override { handler->onTimer(); }
1270
1271 bool operator== (Linux::ITimerHandler* other) const noexcept { return handler == other; }
1272
1273 Linux::ITimerHandler* handler = nullptr;
1274 };
1275
1276 std::unordered_map<Linux::FileDescriptor, std::vector<Linux::IEventHandler*>> eventHandlerMap;
1277 std::list<TimerCaller> timerCallers;
1278
1279 //==============================================================================
1282};
1283
1284#endif
1285
1286//==============================================================================
1287struct VST3ModuleHandle : public ReferenceCountedObject
1288{
1289 explicit VST3ModuleHandle (const File& pluginFile, const PluginDescription& pluginDesc)
1290 : file (pluginFile)
1291 {
1292 if (open (pluginDesc))
1293 {
1294 isOpen = true;
1295 getActiveModules().add (this);
1296 }
1297 }
1298
1299 ~VST3ModuleHandle()
1300 {
1301 if (isOpen)
1302 getActiveModules().removeFirstMatchingValue (this);
1303 }
1304
1305 //==============================================================================
1306 using Ptr = ReferenceCountedObjectPtr<VST3ModuleHandle>;
1307
1308 static VST3ModuleHandle::Ptr findOrCreateModule (const File& file,
1309 const PluginDescription& description)
1310 {
1311 for (auto* module : getActiveModules())
1312 {
1313 // VST3s are basically shells, you must therefore check their name along with their file:
1314 if (module->file == file && module->name == description.name)
1315 return module;
1316 }
1317
1318 VST3ModuleHandle::Ptr modulePtr (new VST3ModuleHandle (file, description));
1319
1320 if (! modulePtr->isOpen)
1321 modulePtr = nullptr;
1322
1323 return modulePtr;
1324 }
1325
1326 //==============================================================================
1327 IPluginFactory* getPluginFactory()
1328 {
1329 return DLLHandleCache::getInstance()->findOrCreateHandle (file.getFullPathName()).getPluginFactory();
1330 }
1331
1332 File getFile() const noexcept { return file; }
1333 String getName() const noexcept { return name; }
1334
1335private:
1336 //==============================================================================
1337 static Array<VST3ModuleHandle*>& getActiveModules()
1338 {
1339 static Array<VST3ModuleHandle*> activeModules;
1340 return activeModules;
1341 }
1342
1343 //==============================================================================
1344 bool open (const PluginDescription& description)
1345 {
1346 VSTComSmartPtr<IPluginFactory> pluginFactory (DLLHandleCache::getInstance()->findOrCreateHandle (file.getFullPathName())
1347 .getPluginFactory());
1348
1349 if (pluginFactory != nullptr)
1350 {
1351 auto numClasses = pluginFactory->countClasses();
1352
1353 for (Steinberg::int32 i = 0; i < numClasses; ++i)
1354 {
1355 PClassInfo info;
1356 pluginFactory->getClassInfo (i, &info);
1357
1358 if (std::strcmp (info.category, kVstAudioEffectClass) != 0)
1359 continue;
1360
1361 if (toString (info.name).trim() == description.name
1362 && (getHashForRange (getNormalisedTUID (info.cid)) == description.uniqueId
1363 || getHashForRange (info.cid) == description.deprecatedUid))
1364 {
1365 name = description.name;
1366 return true;
1367 }
1368 }
1369 }
1370
1371 return false;
1372 }
1373
1374 File file;
1375 String name;
1376 bool isOpen = false;
1377
1378 //==============================================================================
1380};
1381
1382template <typename Type, size_t N>
1383static int compareWithString (Type (&charArray)[N], const String& str)
1384{
1385 return std::strncmp (str.toRawUTF8(),
1386 charArray,
1387 std::min (str.getNumBytesAsUTF8(), (size_t) numElementsInArray (charArray)));
1388}
1389
1390template <typename Callback>
1391static void forEachARAFactory (IPluginFactory* pluginFactory, Callback&& cb)
1392{
1393 #if JUCE_PLUGINHOST_ARA && (JUCE_MAC || JUCE_WINDOWS)
1394 const auto numClasses = pluginFactory->countClasses();
1395 for (Steinberg::int32 i = 0; i < numClasses; ++i)
1396 {
1397 PClassInfo info;
1398 pluginFactory->getClassInfo (i, &info);
1399
1400 if (std::strcmp (info.category, kARAMainFactoryClass) == 0)
1401 {
1402 const bool keepGoing = cb (info);
1403 if (! keepGoing)
1404 break;
1405 }
1406 }
1407 #else
1408 ignoreUnused (pluginFactory, cb);
1409 #endif
1410}
1411
1412static std::shared_ptr<const ARA::ARAFactory> getARAFactory (Steinberg::IPluginFactory* pluginFactory, const String& pluginName)
1413{
1414 std::shared_ptr<const ARA::ARAFactory> factory;
1415
1416 #if JUCE_PLUGINHOST_ARA && (JUCE_MAC || JUCE_WINDOWS)
1417 forEachARAFactory (pluginFactory,
1418 [&pluginFactory, &pluginName, &factory] (const auto& pcClassInfo)
1419 {
1420 if (compareWithString (pcClassInfo.name, pluginName) == 0)
1421 {
1422 ARA::IMainFactory* source;
1423 if (pluginFactory->createInstance (pcClassInfo.cid, ARA::IMainFactory::iid, (void**) &source)
1425 {
1426 factory = getOrCreateARAFactory (source->getFactory(),
1427 [source] (const ARA::ARAFactory*) { source->release(); });
1428 return false;
1429 }
1430 jassert (source == nullptr);
1431 }
1432
1433 return true;
1434 });
1435 #else
1436 ignoreUnused (pluginFactory, pluginName);
1437 #endif
1438
1439 return factory;
1440}
1441
1442static std::shared_ptr<const ARA::ARAFactory> getARAFactory (VST3ModuleHandle& module)
1443{
1444 auto* pluginFactory = module.getPluginFactory();
1445 return getARAFactory (pluginFactory, module.getName());
1446}
1447
1448//==============================================================================
1449struct VST3PluginWindow : public AudioProcessorEditor,
1450 private ComponentMovementWatcher,
1451 private IPlugFrame
1452{
1453 VST3PluginWindow (AudioPluginInstance* owner, IPlugView* pluginView)
1454 : AudioProcessorEditor (owner),
1455 ComponentMovementWatcher (this),
1456 view (pluginView, false)
1457 #if JUCE_MAC
1458 , embeddedComponent (*owner)
1459 #endif
1460 {
1461 setSize (10, 10);
1462 setOpaque (true);
1463 setVisible (true);
1464
1465 warnOnFailure (view->setFrame (this));
1466 view->queryInterface (Steinberg::IPlugViewContentScaleSupport::iid, (void**) &scaleInterface);
1467
1468 setContentScaleFactor();
1469 resizeToFit();
1470 }
1471
1472 ~VST3PluginWindow() override
1473 {
1474 if (scaleInterface != nullptr)
1475 scaleInterface->release();
1476
1477 #if JUCE_LINUX || JUCE_BSD
1478 embeddedComponent.removeClient();
1479 #endif
1480
1481 if (attachedCalled)
1482 warnOnFailure (view->removed());
1483
1484 warnOnFailure (view->setFrame (nullptr));
1485
1486 processor.editorBeingDeleted (this);
1487
1488 #if JUCE_MAC
1489 embeddedComponent.setView (nullptr);
1490 #endif
1491
1492 view = nullptr;
1493 }
1494
1495 #if JUCE_LINUX || JUCE_BSD
1496 Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID queryIid, void** obj) override
1497 {
1498 if (doUIDsMatch (queryIid, Steinberg::Linux::IRunLoop::iid))
1499 {
1500 *obj = &runLoop.get();
1501 return kResultTrue;
1502 }
1503
1505 *obj = nullptr;
1506
1508 }
1509 #else
1511 #endif
1512
1514
1515 void paint (Graphics& g) override
1516 {
1517 g.fillAll (Colours::black);
1518 }
1519
1520 void mouseWheelMove (const MouseEvent&, const MouseWheelDetails& wheel) override
1521 {
1522 view->onWheel (wheel.deltaY);
1523 }
1524
1525 void focusGained (FocusChangeType) override { view->onFocus (true); }
1526 void focusLost (FocusChangeType) override { view->onFocus (false); }
1527
1531 bool keyStateChanged (bool /*isKeyDown*/) override { return true; }
1532 bool keyPressed (const KeyPress& /*key*/) override { return true; }
1533
1534private:
1535 //==============================================================================
1536 void componentPeerChanged() override {}
1537
1538 /* Convert from the component's coordinate system to the hosted VST3's coordinate system. */
1539 ViewRect componentToVST3Rect (Rectangle<int> r) const
1540 {
1541 const auto physical = localAreaToGlobal (r) * nativeScaleFactor * getDesktopScaleFactor();
1542 return { 0, 0, physical.getWidth(), physical.getHeight() };
1543 }
1544
1545 /* Convert from the hosted VST3's coordinate system to the component's coordinate system. */
1546 Rectangle<int> vst3ToComponentRect (const ViewRect& vr) const
1547 {
1548 return getLocalArea (nullptr, Rectangle<int> { vr.right, vr.bottom } / (nativeScaleFactor * getDesktopScaleFactor()));
1549 }
1550
1551 void componentMovedOrResized (bool, bool wasResized) override
1552 {
1553 if (recursiveResize || ! wasResized || getTopLevelComponent()->getPeer() == nullptr)
1554 return;
1555
1556 if (view->canResize() == kResultTrue)
1557 {
1558 auto rect = componentToVST3Rect (getLocalBounds());
1559 view->checkSizeConstraint (&rect);
1560
1561 {
1562 const ScopedValueSetter<bool> recursiveResizeSetter (recursiveResize, true);
1563
1564 const auto logicalSize = vst3ToComponentRect (rect);
1565 setSize (logicalSize.getWidth(), logicalSize.getHeight());
1566 }
1567
1568 embeddedComponent.setBounds (getLocalBounds());
1569
1570 view->onSize (&rect);
1571 }
1572 else
1573 {
1574 ViewRect rect;
1575 warnOnFailure (view->getSize (&rect));
1576
1577 resizeWithRect (embeddedComponent, rect);
1578 }
1579
1580 // Some plugins don't update their cursor correctly when mousing out the window
1581 Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
1582 }
1583
1584 using ComponentMovementWatcher::componentMovedOrResized;
1585
1586 void componentVisibilityChanged() override
1587 {
1588 attachPluginWindow();
1589 resizeToFit();
1590 componentMovedOrResized (true, true);
1591 }
1592
1593 using ComponentMovementWatcher::componentVisibilityChanged;
1594
1595 void resizeToFit()
1596 {
1597 ViewRect rect;
1598 warnOnFailure (view->getSize (&rect));
1599 resizeWithRect (*this, rect);
1600 }
1601
1602 tresult PLUGIN_API resizeView (IPlugView* incomingView, ViewRect* newSize) override
1603 {
1604 const ScopedValueSetter<bool> recursiveResizeSetter (recursiveResize, true);
1605
1606 if (incomingView != nullptr && newSize != nullptr && incomingView == view)
1607 {
1608 const auto oldPhysicalSize = componentToVST3Rect (getLocalBounds());
1609 const auto logicalSize = vst3ToComponentRect (*newSize);
1610 setSize (logicalSize.getWidth(), logicalSize.getHeight());
1611 embeddedComponent.setSize (logicalSize.getWidth(), logicalSize.getHeight());
1612
1613 #if JUCE_WINDOWS
1614 embeddedComponent.updateHWNDBounds();
1615 #elif JUCE_LINUX || JUCE_BSD
1616 embeddedComponent.updateEmbeddedBounds();
1617 #endif
1618
1619 // According to the VST3 Workflow Diagrams, a resizeView from the plugin should
1620 // always trigger a response from the host which confirms the new size.
1621 auto currentPhysicalSize = componentToVST3Rect (getLocalBounds());
1622
1623 if (currentPhysicalSize.getWidth() != oldPhysicalSize.getWidth()
1624 || currentPhysicalSize.getHeight() != oldPhysicalSize.getHeight()
1625 || ! isInOnSize)
1626 {
1627 // Guard against plug-ins immediately calling resizeView() with the same size
1628 const ScopedValueSetter<bool> inOnSizeSetter (isInOnSize, true);
1629 view->onSize (&currentPhysicalSize);
1630 }
1631
1632 return kResultTrue;
1633 }
1634
1636 return kInvalidArgument;
1637 }
1638
1639 //==============================================================================
1640 void resizeWithRect (Component& comp, const ViewRect& rect) const
1641 {
1642 const auto logicalSize = vst3ToComponentRect (rect);
1643 comp.setSize (jmax (10, logicalSize.getWidth()),
1644 jmax (10, logicalSize.getHeight()));
1645 }
1646
1647 void attachPluginWindow()
1648 {
1649 if (pluginHandle == HandleFormat{})
1650 {
1651 #if JUCE_WINDOWS
1652 pluginHandle = static_cast<HWND> (embeddedComponent.getHWND());
1653 #endif
1654
1655 embeddedComponent.setBounds (getLocalBounds());
1656 addAndMakeVisible (embeddedComponent);
1657
1658 #if JUCE_MAC
1659 pluginHandle = (HandleFormat) embeddedComponent.getView();
1660 #elif JUCE_LINUX || JUCE_BSD
1661 pluginHandle = (HandleFormat) embeddedComponent.getHostWindowID();
1662 #endif
1663
1664 if (pluginHandle == HandleFormat{})
1665 {
1667 return;
1668 }
1669
1670 const auto attachedResult = view->attached ((void*) pluginHandle, defaultVST3WindowType);
1671 ignoreUnused (warnOnFailure (attachedResult));
1672
1673 if (attachedResult == kResultOk)
1674 attachedCalled = true;
1675
1676 updatePluginScale();
1677 }
1678 }
1679
1680 void updatePluginScale()
1681 {
1682 if (scaleInterface != nullptr)
1683 setContentScaleFactor();
1684 else
1685 resizeToFit();
1686 }
1687
1688 void setContentScaleFactor()
1689 {
1690 if (scaleInterface != nullptr)
1691 {
1692 const auto result = scaleInterface->setContentScaleFactor ((Steinberg::IPlugViewContentScaleSupport::ScaleFactor) getEffectiveScale());
1694
1695 #if ! JUCE_MAC
1696 ignoreUnused (warnOnFailure (result));
1697 #endif
1698 }
1699 }
1700
1701 void setScaleFactor (float s) override
1702 {
1703 userScaleFactor = s;
1704 setContentScaleFactor();
1705 resizeToFit();
1706 }
1707
1708 float getEffectiveScale() const
1709 {
1710 return nativeScaleFactor * userScaleFactor;
1711 }
1712
1713 //==============================================================================
1714 Atomic<int> refCount { 1 };
1715 VSTComSmartPtr<IPlugView> view;
1716
1717 #if JUCE_WINDOWS
1718 using HandleFormat = HWND;
1719
1720 struct ViewComponent : public HWNDComponent
1721 {
1722 ViewComponent()
1723 {
1724 setOpaque (true);
1725 inner.addToDesktop (0);
1726
1727 if (auto* peer = inner.getPeer())
1728 setHWND (peer->getNativeHandle());
1729 }
1730
1731 void paint (Graphics& g) override { g.fillAll (Colours::black); }
1732
1733 private:
1734 struct Inner : public Component
1735 {
1736 Inner() { setOpaque (true); }
1737 void paint (Graphics& g) override { g.fillAll (Colours::black); }
1738 };
1739
1740 Inner inner;
1741 };
1742
1743 ViewComponent embeddedComponent;
1744 #elif JUCE_MAC
1745 NSViewComponentWithParent embeddedComponent;
1746 using HandleFormat = NSView*;
1747 #elif JUCE_LINUX || JUCE_BSD
1748 SharedResourcePointer<RunLoop> runLoop;
1749 XEmbedComponent embeddedComponent { true, false };
1750 using HandleFormat = Window;
1751 #else
1752 Component embeddedComponent;
1753 using HandleFormat = void*;
1754 #endif
1755
1756 HandleFormat pluginHandle = {};
1757 bool recursiveResize = false, isInOnSize = false, attachedCalled = false;
1758
1759 Steinberg::IPlugViewContentScaleSupport* scaleInterface = nullptr;
1760 float nativeScaleFactor = 1.0f;
1761 float userScaleFactor = 1.0f;
1762
1763 struct ScaleNotifierCallback
1764 {
1765 VST3PluginWindow& window;
1766
1767 void operator() (float platformScale) const
1768 {
1769 MessageManager::callAsync ([ref = Component::SafePointer<VST3PluginWindow> (&window), platformScale]
1770 {
1771 if (auto* r = ref.getComponent())
1772 {
1773 r->nativeScaleFactor = platformScale;
1774 r->setContentScaleFactor();
1775 r->resizeToFit();
1776
1777 #if JUCE_WINDOWS
1778 r->embeddedComponent.updateHWNDBounds();
1779 #elif JUCE_LINUX || JUCE_BSD
1780 r->embeddedComponent.updateEmbeddedBounds();
1781 #endif
1782 }
1783 });
1784 }
1785 };
1786
1787 NativeScaleFactorNotifier scaleNotifier { this, ScaleNotifierCallback { *this } };
1788
1789 //==============================================================================
1791};
1792
1793JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) // warning about overriding deprecated methods
1794
1795//==============================================================================
1796static bool hasARAExtension (IPluginFactory* pluginFactory, const String& pluginClassName)
1797{
1798 bool result = false;
1799
1800 forEachARAFactory (pluginFactory,
1801 [&pluginClassName, &result] (const auto& pcClassInfo)
1802 {
1803 if (compareWithString (pcClassInfo.name, pluginClassName) == 0)
1804 {
1805 result = true;
1806
1807 return false;
1808 }
1809
1810 return true;
1811 });
1812
1813 return result;
1814}
1815
1816//==============================================================================
1817struct VST3ComponentHolder
1818{
1819 VST3ComponentHolder (const VST3ModuleHandle::Ptr& m) : module (m)
1820 {
1821 host = new VST3HostContext();
1822 }
1823
1824 ~VST3ComponentHolder()
1825 {
1826 terminate();
1827 }
1828
1829 bool isIComponentAlsoIEditController() const
1830 {
1831 if (component == nullptr)
1832 {
1834 return false;
1835 }
1836
1837 return VSTComSmartPtr<Vst::IEditController>().loadFrom (component);
1838 }
1839
1840 bool fetchController (VSTComSmartPtr<Vst::IEditController>& editController)
1841 {
1842 if (! isComponentInitialised && ! initialise())
1843 return false;
1844
1845 editController.loadFrom (component);
1846
1847 // Get the IEditController:
1848 TUID controllerCID = { 0 };
1849
1850 if (editController == nullptr
1851 && component->getControllerClassId (controllerCID) == kResultTrue
1852 && FUID (controllerCID).isValid())
1853 {
1854 editController.loadFrom (factory, controllerCID);
1855 }
1856
1857 if (editController == nullptr)
1858 {
1859 // Try finding the IEditController the long way around:
1860 auto numClasses = factory->countClasses();
1861
1862 for (Steinberg::int32 i = 0; i < numClasses; ++i)
1863 {
1864 PClassInfo classInfo;
1865 factory->getClassInfo (i, &classInfo);
1866
1867 if (std::strcmp (classInfo.category, kVstComponentControllerClass) == 0)
1868 editController.loadFrom (factory, classInfo.cid);
1869 }
1870 }
1871
1872 return (editController != nullptr);
1873 }
1874
1875 //==============================================================================
1876 void fillInPluginDescription (PluginDescription& description) const
1877 {
1878 jassert (module != nullptr && isComponentInitialised);
1879
1880 PFactoryInfo factoryInfo;
1881 factory->getFactoryInfo (&factoryInfo);
1882
1883 auto classIdx = getClassIndex (module->getName());
1884
1885 if (classIdx >= 0)
1886 {
1887 PClassInfo info;
1888 bool success = (factory->getClassInfo (classIdx, &info) == kResultOk);
1889 ignoreUnused (success);
1890 jassert (success);
1891
1892 VSTComSmartPtr<IPluginFactory2> pf2;
1893 VSTComSmartPtr<IPluginFactory3> pf3;
1894
1895 std::unique_ptr<PClassInfo2> info2;
1896 std::unique_ptr<PClassInfoW> infoW;
1897
1898 if (pf2.loadFrom (factory))
1899 {
1900 info2.reset (new PClassInfo2());
1901 pf2->getClassInfo2 (classIdx, info2.get());
1902 }
1903 else
1904 {
1905 info2.reset();
1906 }
1907
1908 if (pf3.loadFrom (factory))
1909 {
1910 pf3->setHostContext (host->getFUnknown());
1911 infoW.reset (new PClassInfoW());
1912 pf3->getClassInfoUnicode (classIdx, infoW.get());
1913 }
1914 else
1915 {
1916 infoW.reset();
1917 }
1918
1919 Vst::BusInfo bus;
1920 int totalNumInputChannels = 0, totalNumOutputChannels = 0;
1921
1922 int n = component->getBusCount (Vst::kAudio, Vst::kInput);
1923 for (int i = 0; i < n; ++i)
1924 if (component->getBusInfo (Vst::kAudio, Vst::kInput, i, bus) == kResultOk)
1925 totalNumInputChannels += ((bus.flags & Vst::BusInfo::kDefaultActive) != 0 ? bus.channelCount : 0);
1926
1927 n = component->getBusCount (Vst::kAudio, Vst::kOutput);
1928 for (int i = 0; i < n; ++i)
1929 if (component->getBusInfo (Vst::kAudio, Vst::kOutput, i, bus) == kResultOk)
1930 totalNumOutputChannels += ((bus.flags & Vst::BusInfo::kDefaultActive) != 0 ? bus.channelCount : 0);
1931
1932 createPluginDescription (description, module->getFile(),
1933 factoryInfo.vendor, module->getName(),
1934 info, info2.get(), infoW.get(),
1935 totalNumInputChannels,
1936 totalNumOutputChannels);
1937
1938 description.hasARAExtension = hasARAExtension (factory, description.name);
1939
1940 return;
1941 }
1942
1944 }
1945
1946 //==============================================================================
1947 bool initialise()
1948 {
1949 if (isComponentInitialised)
1950 return true;
1951
1952 // It's highly advisable to create your plugins using the message thread.
1953 // The VST3 spec requires that many of the functions called during
1954 // initialisation are only called from the message thread.
1956
1957 factory = VSTComSmartPtr<IPluginFactory> (module->getPluginFactory());
1958
1959 int classIdx;
1960 if ((classIdx = getClassIndex (module->getName())) < 0)
1961 return false;
1962
1963 PClassInfo info;
1964 if (factory->getClassInfo (classIdx, &info) != kResultOk)
1965 return false;
1966
1967 if (! component.loadFrom (factory, info.cid) || component == nullptr)
1968 return false;
1969
1970 cidOfComponent = FUID (info.cid);
1971
1972 if (warnOnFailure (component->initialize (host->getFUnknown())) != kResultOk)
1973 return false;
1974
1975 isComponentInitialised = true;
1976
1977 return true;
1978 }
1979
1980 void terminate()
1981 {
1982 if (isComponentInitialised)
1983 {
1984 component->terminate();
1985 isComponentInitialised = false;
1986 }
1987
1988 component = nullptr;
1989 }
1990
1991 //==============================================================================
1992 int getClassIndex (const String& className) const
1993 {
1994 PClassInfo info;
1995 const Steinberg::int32 numClasses = factory->countClasses();
1996
1997 for (Steinberg::int32 j = 0; j < numClasses; ++j)
1998 if (factory->getClassInfo (j, &info) == kResultOk
1999 && std::strcmp (info.category, kVstAudioEffectClass) == 0
2000 && toString (info.name).trim() == className)
2001 return j;
2002
2003 return -1;
2004 }
2005
2006 //==============================================================================
2007 VST3ModuleHandle::Ptr module;
2008 VSTComSmartPtr<IPluginFactory> factory;
2009 VSTComSmartPtr<VST3HostContext> host;
2010 VSTComSmartPtr<Vst::IComponent> component;
2011 FUID cidOfComponent;
2012
2013 bool isComponentInitialised = false;
2014};
2015
2016//==============================================================================
2017/* A queue which can store up to one element.
2018
2019 This is more memory-efficient than storing large vectors of
2020 parameter changes that we'll just throw away.
2021*/
2022class ParamValueQueue : public Vst::IParamValueQueue
2023{
2024public:
2025 ParamValueQueue (Vst::ParamID idIn, Steinberg::int32 parameterIndexIn)
2026 : paramId (idIn), parameterIndex (parameterIndexIn) {}
2027
2028 virtual ~ParamValueQueue() = default;
2029
2032
2033 Vst::ParamID PLUGIN_API getParameterId() override { return paramId; }
2034
2035 Steinberg::int32 getParameterIndex() const noexcept { return parameterIndex; }
2036
2037 Steinberg::int32 PLUGIN_API getPointCount() override { return size; }
2038
2039 tresult PLUGIN_API getPoint (Steinberg::int32 index,
2040 Steinberg::int32& sampleOffset,
2041 Vst::ParamValue& value) override
2042 {
2043 if (! isPositiveAndBelow (index, size))
2044 return kResultFalse;
2045
2046 sampleOffset = 0;
2047 value = cachedValue;
2048
2049 return kResultTrue;
2050 }
2051
2052 tresult PLUGIN_API addPoint (Steinberg::int32,
2053 Vst::ParamValue value,
2054 Steinberg::int32& index) override
2055 {
2056 index = size++;
2057 set ((float) value);
2058
2059 return kResultTrue;
2060 }
2061
2062 void set (float valueIn)
2063 {
2064 cachedValue = valueIn;
2065 size = 1;
2066 }
2067
2068 void clear() { size = 0; }
2069
2070 float get() const noexcept
2071 {
2072 jassert (size > 0);
2073 return cachedValue;
2074 }
2075
2076private:
2077 const Vst::ParamID paramId;
2078 const Steinberg::int32 parameterIndex;
2079 float cachedValue;
2081 Atomic<int> refCount;
2082};
2083
2084//==============================================================================
2085/* An implementation of IParameterChanges with some important characteristics:
2086 - Lookup by index is O(1)
2087 - Lookup by paramID is also O(1)
2088 - addParameterData never allocates, as long you pass a paramID already passed to initialise
2089*/
2090class ParameterChanges : public Vst::IParameterChanges
2091{
2092 static constexpr Steinberg::int32 notInVector = -1;
2093
2094 struct Entry
2095 {
2096 explicit Entry (std::unique_ptr<ParamValueQueue> queue) : ptr (queue.release()) {}
2097
2098 VSTComSmartPtr<ParamValueQueue> ptr;
2099 Steinberg::int32 index = notInVector;
2100 };
2101
2102 using Map = std::unordered_map<Vst::ParamID, Entry>;
2103 using Queues = std::vector<Entry*>;
2104
2105public:
2106 virtual ~ParameterChanges() = default;
2107
2110
2111 Steinberg::int32 PLUGIN_API getParameterCount() override
2112 {
2113 return (Steinberg::int32) queues.size();
2114 }
2115
2116 ParamValueQueue* PLUGIN_API getParameterData (Steinberg::int32 index) override
2117 {
2118 if (isPositiveAndBelow (index, queues.size()))
2119 {
2120 auto& entry = queues[(size_t) index];
2121 // If this fails, our container has become internally inconsistent
2122 jassert (entry->index == index);
2123 return entry->ptr.get();
2124 }
2125
2126 return nullptr;
2127 }
2128
2129 ParamValueQueue* PLUGIN_API addParameterData (const Vst::ParamID& id,
2130 Steinberg::int32& index) override
2131 {
2132 const auto it = map.find (id);
2133
2134 if (it == map.end())
2135 return nullptr;
2136
2137 auto& result = it->second;
2138
2139 if (result.index == notInVector)
2140 {
2141 result.index = (Steinberg::int32) queues.size();
2142 queues.push_back (&result);
2143 }
2144
2145 index = result.index;
2146 return result.ptr.get();
2147 }
2148
2149 void set (Vst::ParamID id, float value)
2150 {
2151 Steinberg::int32 indexOut = notInVector;
2152
2153 if (auto* queue = addParameterData (id, indexOut))
2154 queue->set (value);
2155 }
2156
2157 void clear()
2158 {
2159 for (auto* item : queues)
2160 item->index = notInVector;
2161
2162 queues.clear();
2163 }
2164
2165 void initialise (const std::vector<Vst::ParamID>& idsIn)
2166 {
2167 Steinberg::int32 index = 0;
2168
2169 for (const auto& id : idsIn)
2170 map.emplace (id, Entry { std::make_unique<ParamValueQueue> (id, Steinberg::int32 { index++ }) });
2171
2172 queues.reserve (map.size());
2173 queues.clear();
2174 }
2175
2176 template <typename Callback>
2177 void forEach (Callback&& callback) const
2178 {
2179 for (const auto* item : queues)
2180 {
2181 auto* ptr = item->ptr.get();
2182 callback (ptr->getParameterIndex(), ptr->get());
2183 }
2184 }
2185
2186private:
2187 Map map;
2188 Queues queues;
2189 Atomic<int> refCount;
2190};
2191
2192//==============================================================================
2193class VST3PluginInstance final : public AudioPluginInstance
2194{
2195public:
2196 //==============================================================================
2197 struct VST3Parameter final : public Parameter
2198 {
2199 VST3Parameter (VST3PluginInstance& parent,
2200 Steinberg::int32 vstParameterIndex,
2201 Steinberg::Vst::ParamID parameterID,
2202 bool parameterIsAutomatable)
2203 : pluginInstance (parent),
2204 vstParamIndex (vstParameterIndex),
2205 paramID (parameterID),
2206 automatable (parameterIsAutomatable)
2207 {
2208 }
2209
2210 float getValue() const override
2211 {
2212 return pluginInstance.cachedParamValues.get (vstParamIndex);
2213 }
2214
2215 /* The 'normal' setValue call, which will update both the processor and editor.
2216 */
2217 void setValue (float newValue) override
2218 {
2219 pluginInstance.cachedParamValues.set (vstParamIndex, newValue);
2220 }
2221
2222 /* If we're syncing the editor to the processor, the processor won't need to
2223 be notified about the parameter updates, so we can avoid flagging the
2224 change when updating the float cache.
2225 */
2226 void setValueWithoutUpdatingProcessor (float newValue)
2227 {
2228 pluginInstance.cachedParamValues.setWithoutNotifying (vstParamIndex, newValue);
2229 sendValueChangedMessageToListeners (newValue);
2230 }
2231
2232 String getText (float value, int maximumLength) const override
2233 {
2234 MessageManagerLock lock;
2235
2236 if (pluginInstance.editController != nullptr)
2237 {
2238 Vst::String128 result;
2239
2240 if (pluginInstance.editController->getParamStringByValue (paramID, value, result) == kResultOk)
2241 return toString (result).substring (0, maximumLength);
2242 }
2243
2244 return Parameter::getText (value, maximumLength);
2245 }
2246
2247 float getValueForText (const String& text) const override
2248 {
2249 MessageManagerLock lock;
2250
2251 if (pluginInstance.editController != nullptr)
2252 {
2253 Vst::ParamValue result;
2254
2255 if (pluginInstance.editController->getParamValueByString (paramID, toString (text), result) == kResultOk)
2256 return (float) result;
2257 }
2258
2259 return Parameter::getValueForText (text);
2260 }
2261
2262 float getDefaultValue() const override
2263 {
2264 return (float) getParameterInfo().defaultNormalizedValue;
2265 }
2266
2267 String getName (int /*maximumStringLength*/) const override
2268 {
2269 return toString (getParameterInfo().title);
2270 }
2271
2272 String getLabel() const override
2273 {
2274 return toString (getParameterInfo().units);
2275 }
2276
2277 bool isAutomatable() const override
2278 {
2279 return automatable;
2280 }
2281
2282 bool isDiscrete() const override
2283 {
2284 return discrete;
2285 }
2286
2287 int getNumSteps() const override
2288 {
2289 return numSteps;
2290 }
2291
2292 StringArray getAllValueStrings() const override
2293 {
2294 return {};
2295 }
2296
2297 String getParameterID() const override
2298 {
2299 return String (paramID);
2300 }
2301
2302 Steinberg::Vst::ParamID getParamID() const noexcept { return paramID; }
2303
2304 private:
2305 Vst::ParameterInfo getParameterInfo() const
2306 {
2307 return pluginInstance.getParameterInfoForIndex (vstParamIndex);
2308 }
2309
2310 VST3PluginInstance& pluginInstance;
2311 const Steinberg::int32 vstParamIndex;
2312 const Steinberg::Vst::ParamID paramID;
2313 const bool automatable;
2314 const int numSteps = [&]
2315 {
2316 auto stepCount = getParameterInfo().stepCount;
2317 return stepCount == 0 ? AudioProcessor::getDefaultNumParameterSteps()
2318 : stepCount + 1;
2319 }();
2320 const bool discrete = getNumSteps() != AudioProcessor::getDefaultNumParameterSteps();
2321 };
2322
2323 //==============================================================================
2324 explicit VST3PluginInstance (std::unique_ptr<VST3ComponentHolder> componentHolder)
2325 : AudioPluginInstance (getBusProperties (componentHolder->component)),
2326 holder (std::move (componentHolder))
2327 {
2328 jassert (holder->isComponentInitialised);
2329 holder->host->setPlugin (this);
2330 }
2331
2332 ~VST3PluginInstance() override
2333 {
2334 callOnMessageThread ([this] { cleanup(); });
2335 }
2336
2337 void cleanup()
2338 {
2339 jassert (getActiveEditor() == nullptr); // You must delete any editors before deleting the plugin instance!
2340
2341 releaseResources();
2342
2343 if (editControllerConnection != nullptr && componentConnection != nullptr)
2344 {
2345 editControllerConnection->disconnect (componentConnection);
2346 componentConnection->disconnect (editControllerConnection);
2347 }
2348
2349 editController->setComponentHandler (nullptr);
2350
2351 if (isControllerInitialised && ! holder->isIComponentAlsoIEditController())
2352 editController->terminate();
2353
2354 holder->terminate();
2355
2356 componentConnection = nullptr;
2357 editControllerConnection = nullptr;
2358 unitData = nullptr;
2359 unitInfo = nullptr;
2360 programListData = nullptr;
2361 componentHandler2 = nullptr;
2362 componentHandler = nullptr;
2363 processor = nullptr;
2364 midiMapping = nullptr;
2365 editController2 = nullptr;
2366 editController = nullptr;
2367 }
2368
2369 //==============================================================================
2370 bool initialise()
2371 {
2372 // It's highly advisable to create your plugins using the message thread.
2373 // The VST3 spec requires that many of the functions called during
2374 // initialisation are only called from the message thread.
2376
2377 if (! holder->initialise())
2378 return false;
2379
2380 if (! (isControllerInitialised || holder->fetchController (editController)))
2381 return false;
2382
2383 // If the IComponent and IEditController are the same, we will have
2384 // already initialized the object at this point and should avoid doing so again.
2385 if (! holder->isIComponentAlsoIEditController())
2386 editController->initialize (holder->host->getFUnknown());
2387
2388 isControllerInitialised = true;
2389 editController->setComponentHandler (holder->host);
2390 grabInformationObjects();
2391 interconnectComponentAndController();
2392
2393 auto configureParameters = [this]
2394 {
2395 refreshParameterList();
2396 synchroniseStates();
2397 syncProgramNames();
2398 };
2399
2400 configureParameters();
2401 setupIO();
2402
2403 // Some plug-ins don't present their parameters until after the IO has been
2404 // configured, so we need to jump though all these hoops again
2405 if (getParameters().isEmpty() && editController->getParameterCount() > 0)
2406 configureParameters();
2407
2408 updateMidiMappings();
2409
2410 parameterDispatcher.start (*editController);
2411
2412 return true;
2413 }
2414
2415 void getExtensions (ExtensionsVisitor& visitor) const override
2416 {
2417 struct Extensions : public ExtensionsVisitor::VST3Client,
2418 public ExtensionsVisitor::ARAClient
2419 {
2420 explicit Extensions (const VST3PluginInstance* instanceIn) : instance (instanceIn) {}
2421
2422 Steinberg::Vst::IComponent* getIComponentPtr() const noexcept override { return instance->holder->component; }
2423
2424 MemoryBlock getPreset() const override { return instance->getStateForPresetFile(); }
2425
2426 bool setPreset (const MemoryBlock& rawData) const override
2427 {
2428 return instance->setStateFromPresetFile (rawData);
2429 }
2430
2431 void createARAFactoryAsync (std::function<void (ARAFactoryWrapper)> cb) const noexcept override
2432 {
2433 cb (ARAFactoryWrapper { ::juce::getARAFactory (*(instance->holder->module)) });
2434 }
2435
2436 const VST3PluginInstance* instance = nullptr;
2437 };
2438
2439 Extensions extensions { this };
2440 visitor.visitVST3Client (extensions);
2441
2442 if (::juce::getARAFactory (*(holder->module)))
2443 {
2444 visitor.visitARAClient (extensions);
2445 }
2446 }
2447
2448 void* getPlatformSpecificData() override { return holder->component; }
2449
2450 void updateMidiMappings()
2451 {
2452 // MIDI mappings will always be updated on the main thread, but we need to ensure
2453 // that we're not simultaneously reading them on the audio thread.
2454 const SpinLock::ScopedLockType processLock (processMutex);
2455
2456 if (midiMapping != nullptr)
2457 storedMidiMapping.storeMappings (*midiMapping);
2458 }
2459
2460 //==============================================================================
2461 const String getName() const override
2462 {
2463 auto& module = holder->module;
2464 return module != nullptr ? module->getName() : String();
2465 }
2466
2467 void repopulateArrangements (Array<Vst::SpeakerArrangement>& inputArrangements, Array<Vst::SpeakerArrangement>& outputArrangements) const
2468 {
2469 inputArrangements.clearQuick();
2470 outputArrangements.clearQuick();
2471
2472 auto numInputAudioBuses = getBusCount (true);
2473 auto numOutputAudioBuses = getBusCount (false);
2474
2475 for (int i = 0; i < numInputAudioBuses; ++i)
2476 inputArrangements.add (getArrangementForBus (processor, true, i));
2477
2478 for (int i = 0; i < numOutputAudioBuses; ++i)
2479 outputArrangements.add (getArrangementForBus (processor, false, i));
2480 }
2481
2482 void processorLayoutsToArrangements (Array<Vst::SpeakerArrangement>& inputArrangements, Array<Vst::SpeakerArrangement>& outputArrangements)
2483 {
2484 inputArrangements.clearQuick();
2485 outputArrangements.clearQuick();
2486
2487 auto numInputBuses = getBusCount (true);
2488 auto numOutputBuses = getBusCount (false);
2489
2490 for (int i = 0; i < numInputBuses; ++i)
2491 inputArrangements.add (getVst3SpeakerArrangement (getBus (true, i)->getLastEnabledLayout()));
2492
2493 for (int i = 0; i < numOutputBuses; ++i)
2494 outputArrangements.add (getVst3SpeakerArrangement (getBus (false, i)->getLastEnabledLayout()));
2495 }
2496
2497 void prepareToPlay (double newSampleRate, int estimatedSamplesPerBlock) override
2498 {
2499 // The VST3 spec requires that IComponent::setupProcessing() is called on the message
2500 // thread. If you call it from a different thread, some plugins may break.
2502 MessageManagerLock lock;
2503
2504 const SpinLock::ScopedLockType processLock (processMutex);
2505
2506 // Avoid redundantly calling things like setActive, which can be a heavy-duty call for some plugins:
2507 if (isActive
2508 && getSampleRate() == newSampleRate
2509 && getBlockSize() == estimatedSamplesPerBlock)
2510 return;
2511
2512 using namespace Vst;
2513
2514 ProcessSetup setup;
2515 setup.symbolicSampleSize = isUsingDoublePrecision() ? kSample64 : kSample32;
2516 setup.maxSamplesPerBlock = estimatedSamplesPerBlock;
2517 setup.sampleRate = newSampleRate;
2518 setup.processMode = isNonRealtime() ? kOffline : kRealtime;
2519
2520 warnOnFailure (processor->setupProcessing (setup));
2521
2522 holder->initialise();
2523
2524 Array<Vst::SpeakerArrangement> inputArrangements, outputArrangements;
2525 processorLayoutsToArrangements (inputArrangements, outputArrangements);
2526
2527 // Some plug-ins will crash if you pass a nullptr to setBusArrangements!
2528 SpeakerArrangement nullArrangement = {};
2529 auto* inputArrangementData = inputArrangements.isEmpty() ? &nullArrangement : inputArrangements.getRawDataPointer();
2530 auto* outputArrangementData = outputArrangements.isEmpty() ? &nullArrangement : outputArrangements.getRawDataPointer();
2531
2532 warnOnFailure (processor->setBusArrangements (inputArrangementData, inputArrangements.size(),
2533 outputArrangementData, outputArrangements.size()));
2534
2535 Array<Vst::SpeakerArrangement> actualInArr, actualOutArr;
2536 repopulateArrangements (actualInArr, actualOutArr);
2537
2538 jassert (actualInArr == inputArrangements && actualOutArr == outputArrangements);
2539
2540 // Needed for having the same sample rate in processBlock(); some plugins need this!
2541 setRateAndBufferSizeDetails (newSampleRate, estimatedSamplesPerBlock);
2542
2543 auto numInputBuses = getBusCount (true);
2544 auto numOutputBuses = getBusCount (false);
2545
2546 for (int i = 0; i < numInputBuses; ++i)
2547 warnOnFailure (holder->component->activateBus (Vst::kAudio, Vst::kInput, i, getBus (true, i)->isEnabled() ? 1 : 0));
2548
2549 for (int i = 0; i < numOutputBuses; ++i)
2550 warnOnFailure (holder->component->activateBus (Vst::kAudio, Vst::kOutput, i, getBus (false, i)->isEnabled() ? 1 : 0));
2551
2552 setLatencySamples (jmax (0, (int) processor->getLatencySamples()));
2553
2554 inputBusMap .prepare (createChannelMappings (true));
2555 outputBusMap.prepare (createChannelMappings (false));
2556
2557 setStateForAllMidiBuses (true);
2558
2559 warnOnFailure (holder->component->setActive (true));
2560 warnOnFailureIfImplemented (processor->setProcessing (true));
2561
2562 isActive = true;
2563 }
2564
2565 void releaseResources() override
2566 {
2567 const SpinLock::ScopedLockType lock (processMutex);
2568
2569 if (! isActive)
2570 return; // Avoids redundantly calling things like setActive
2571
2572 isActive = false;
2573
2574 if (processor != nullptr)
2575 warnOnFailureIfImplemented (processor->setProcessing (false));
2576
2577 if (holder->component != nullptr)
2578 warnOnFailure (holder->component->setActive (false));
2579
2580 setStateForAllMidiBuses (false);
2581 }
2582
2583 bool supportsDoublePrecisionProcessing() const override
2584 {
2585 return (processor->canProcessSampleSize (Vst::kSample64) == kResultTrue);
2586 }
2587
2588 //==============================================================================
2589 /* Important: It is strongly recommended to use this function if you need to
2590 find the JUCE parameter corresponding to a particular IEditController
2591 parameter.
2592
2593 Note that a parameter at a given index in the IEditController does not
2594 necessarily correspond to the parameter at the same index in
2595 AudioProcessor::getParameters().
2596 */
2597 VST3Parameter* getParameterForID (Vst::ParamID paramID) const
2598 {
2599 const auto it = idToParamMap.find (paramID);
2600 return it != idToParamMap.end() ? it->second : nullptr;
2601 }
2602
2603 //==============================================================================
2604 void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
2605 {
2606 jassert (! isUsingDoublePrecision());
2607
2608 const SpinLock::ScopedLockType processLock (processMutex);
2609
2610 if (isActive && processor != nullptr)
2611 processAudio (buffer, midiMessages, Vst::kSample32, false);
2612 }
2613
2614 void processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages) override
2615 {
2616 jassert (isUsingDoublePrecision());
2617
2618 const SpinLock::ScopedLockType processLock (processMutex);
2619
2620 if (isActive && processor != nullptr)
2621 processAudio (buffer, midiMessages, Vst::kSample64, false);
2622 }
2623
2624 void processBlockBypassed (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
2625 {
2626 jassert (! isUsingDoublePrecision());
2627
2628 const SpinLock::ScopedLockType processLock (processMutex);
2629
2630 if (bypassParam != nullptr)
2631 {
2632 if (isActive && processor != nullptr)
2633 processAudio (buffer, midiMessages, Vst::kSample32, true);
2634 }
2635 else
2636 {
2637 AudioProcessor::processBlockBypassed (buffer, midiMessages);
2638 }
2639 }
2640
2641 void processBlockBypassed (AudioBuffer<double>& buffer, MidiBuffer& midiMessages) override
2642 {
2643 jassert (isUsingDoublePrecision());
2644
2645 const SpinLock::ScopedLockType processLock (processMutex);
2646
2647 if (bypassParam != nullptr)
2648 {
2649 if (isActive && processor != nullptr)
2650 processAudio (buffer, midiMessages, Vst::kSample64, true);
2651 }
2652 else
2653 {
2654 AudioProcessor::processBlockBypassed (buffer, midiMessages);
2655 }
2656 }
2657
2658 //==============================================================================
2659 template <typename FloatType>
2660 void processAudio (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages,
2661 Vst::SymbolicSampleSizes sampleSize, bool isProcessBlockBypassedCall)
2662 {
2663 using namespace Vst;
2664 auto numSamples = buffer.getNumSamples();
2665
2666 auto numInputAudioBuses = getBusCount (true);
2667 auto numOutputAudioBuses = getBusCount (false);
2668
2669 updateBypass (isProcessBlockBypassedCall);
2670
2671 ProcessData data;
2672 data.processMode = isNonRealtime() ? kOffline : kRealtime;
2673 data.symbolicSampleSize = sampleSize;
2674 data.numInputs = numInputAudioBuses;
2675 data.numOutputs = numOutputAudioBuses;
2676 data.inputParameterChanges = inputParameterChanges;
2677 data.outputParameterChanges = outputParameterChanges;
2678 data.numSamples = (Steinberg::int32) numSamples;
2679
2680 updateTimingInformation (data, getSampleRate());
2681
2682 for (int i = getTotalNumInputChannels(); i < buffer.getNumChannels(); ++i)
2683 buffer.clear (i, 0, numSamples);
2684
2685 inputParameterChanges->clear();
2686 outputParameterChanges->clear();
2687
2688 associateWith (data, buffer);
2689 associateWith (data, midiMessages);
2690
2691 cachedParamValues.ifSet ([&] (Steinberg::int32 index, float value)
2692 {
2693 inputParameterChanges->set (cachedParamValues.getParamID (index), value);
2694 });
2695
2696 inputParameterChanges->forEach ([&] (Steinberg::int32 index, float value)
2697 {
2698 parameterDispatcher.push (index, value);
2699 });
2700
2701 processor->process (data);
2702
2703 outputParameterChanges->forEach ([&] (Steinberg::int32 index, float value)
2704 {
2705 parameterDispatcher.push (index, value);
2706 });
2707
2708 midiMessages.clear();
2709 MidiEventList::toMidiBuffer (midiMessages, *midiOutputs);
2710 }
2711
2712 //==============================================================================
2713 bool canAddBus (bool) const override { return false; }
2714 bool canRemoveBus (bool) const override { return false; }
2715
2716 bool isBusesLayoutSupported (const BusesLayout& layouts) const override
2717 {
2718 const SpinLock::ScopedLockType processLock (processMutex);
2719
2720 // if the processor is not active, we ask the underlying plug-in if the
2721 // layout is actually supported
2722 if (! isActive)
2723 return canApplyBusesLayout (layouts);
2724
2725 // not much we can do to check the layout while the audio processor is running
2726 // Let's at least check if it is a VST3 compatible layout
2727 for (int dir = 0; dir < 2; ++dir)
2728 {
2729 bool isInput = (dir == 0);
2730 auto n = getBusCount (isInput);
2731
2732 for (int i = 0; i < n; ++i)
2733 if (getChannelLayoutOfBus (isInput, i).isDiscreteLayout())
2734 return false;
2735 }
2736
2737 return true;
2738 }
2739
2740 bool syncBusLayouts (const BusesLayout& layouts) const
2741 {
2742 for (int dir = 0; dir < 2; ++dir)
2743 {
2744 bool isInput = (dir == 0);
2745 auto n = getBusCount (isInput);
2746 const Vst::BusDirection vstDir = (isInput ? Vst::kInput : Vst::kOutput);
2747
2748 for (int busIdx = 0; busIdx < n; ++busIdx)
2749 {
2750 const bool isEnabled = (! layouts.getChannelSet (isInput, busIdx).isDisabled());
2751
2752 if (holder->component->activateBus (Vst::kAudio, vstDir, busIdx, (isEnabled ? 1 : 0)) != kResultOk)
2753 return false;
2754 }
2755 }
2756
2757 Array<Vst::SpeakerArrangement> inputArrangements, outputArrangements;
2758
2759 for (int i = 0; i < layouts.inputBuses.size(); ++i)
2760 {
2761 const auto& requested = layouts.getChannelSet (true, i);
2762 inputArrangements.add (getVst3SpeakerArrangement (requested.isDisabled() ? getBus (true, i)->getLastEnabledLayout() : requested));
2763 }
2764
2765 for (int i = 0; i < layouts.outputBuses.size(); ++i)
2766 {
2767 const auto& requested = layouts.getChannelSet (false, i);
2768 outputArrangements.add (getVst3SpeakerArrangement (requested.isDisabled() ? getBus (false, i)->getLastEnabledLayout() : requested));
2769 }
2770
2771 // Some plug-ins will crash if you pass a nullptr to setBusArrangements!
2772 Vst::SpeakerArrangement nullArrangement = {};
2773 auto* inputArrangementData = inputArrangements.isEmpty() ? &nullArrangement : inputArrangements.getRawDataPointer();
2774 auto* outputArrangementData = outputArrangements.isEmpty() ? &nullArrangement : outputArrangements.getRawDataPointer();
2775
2776 if (processor->setBusArrangements (inputArrangementData, inputArrangements.size(),
2777 outputArrangementData, outputArrangements.size()) != kResultTrue)
2778 return false;
2779
2780 // check if the layout matches the request
2781 Array<Vst::SpeakerArrangement> actualIn, actualOut;
2782 repopulateArrangements (actualIn, actualOut);
2783
2784 return (actualIn == inputArrangements && actualOut == outputArrangements);
2785 }
2786
2787 bool canApplyBusesLayout (const BusesLayout& layouts) const override
2788 {
2789 // someone tried to change the layout while the AudioProcessor is running
2790 // call releaseResources first!
2791 jassert (! isActive);
2792
2793 const auto previousLayout = getBusesLayout();
2794 const auto result = syncBusLayouts (layouts);
2795 syncBusLayouts (previousLayout);
2796 return result;
2797 }
2798
2799 //==============================================================================
2800 void updateTrackProperties (const TrackProperties& properties) override
2801 {
2802 if (trackInfoListener != nullptr)
2803 {
2804 VSTComSmartPtr<Vst::IAttributeList> l (new TrackPropertiesAttributeList (properties));
2805 trackInfoListener->setChannelContextInfos (l);
2806 }
2807 }
2808
2809 struct TrackPropertiesAttributeList : public Vst::IAttributeList
2810 {
2811 TrackPropertiesAttributeList (const TrackProperties& properties) : props (properties) {}
2812 virtual ~TrackPropertiesAttributeList() {}
2813
2815
2816 tresult PLUGIN_API queryInterface (const TUID queryIid, void** obj) override
2817 {
2818 return testForMultiple (*this,
2819 queryIid,
2820 UniqueBase<Vst::IAttributeList>{},
2821 SharedBase<FUnknown, Vst::IAttributeList>{}).extract (obj);
2822 }
2823
2824 tresult PLUGIN_API setInt (AttrID, Steinberg::int64) override { return kOutOfMemory; }
2825 tresult PLUGIN_API setFloat (AttrID, double) override { return kOutOfMemory; }
2826 tresult PLUGIN_API setString (AttrID, const Vst::TChar*) override { return kOutOfMemory; }
2827 tresult PLUGIN_API setBinary (AttrID, const void*, Steinberg::uint32) override { return kOutOfMemory; }
2828 tresult PLUGIN_API getFloat (AttrID, double&) override { return kResultFalse; }
2829 tresult PLUGIN_API getBinary (AttrID, const void*&, Steinberg::uint32&) override { return kResultFalse; }
2830
2831 tresult PLUGIN_API getString (AttrID id, Vst::TChar* string, Steinberg::uint32 size) override
2832 {
2833 if (! std::strcmp (id, Vst::ChannelContext::kChannelNameKey))
2834 {
2835 Steinberg::String str (props.name.toRawUTF8());
2836 str.copyTo (string, 0, (Steinberg::int32) jmin (size, (Steinberg::uint32) std::numeric_limits<Steinberg::int32>::max()));
2837
2838 return kResultTrue;
2839 }
2840
2841 return kResultFalse;
2842 }
2843
2844 tresult PLUGIN_API getInt (AttrID id, Steinberg::int64& value) override
2845 {
2846 if (! std::strcmp (Vst::ChannelContext::kChannelNameLengthKey, id)) value = props.name.length();
2847 else if (! std::strcmp (Vst::ChannelContext::kChannelColorKey, id)) value = static_cast<Steinberg::int64> (props.colour.getARGB());
2848 else return kResultFalse;
2849
2850 return kResultTrue;
2851 }
2852
2853 Atomic<int> refCount;
2854 TrackProperties props;
2855
2856 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TrackPropertiesAttributeList)
2857 };
2858
2859 //==============================================================================
2860 String getChannelName (int channelIndex, Direction direction) const
2861 {
2862 auto numBuses = getNumSingleDirectionBusesFor (holder->component, MediaKind::audio, direction);
2863
2864 int numCountedChannels = 0;
2865
2866 for (int i = 0; i < numBuses; ++i)
2867 {
2868 auto busInfo = getBusInfo (MediaKind::audio, direction, i);
2869
2870 numCountedChannels += busInfo.channelCount;
2871
2872 if (channelIndex < numCountedChannels)
2873 return toString (busInfo.name);
2874 }
2875
2876 return {};
2877 }
2878
2879 const String getInputChannelName (int channelIndex) const override { return getChannelName (channelIndex, Direction::input); }
2880 const String getOutputChannelName (int channelIndex) const override { return getChannelName (channelIndex, Direction::output); }
2881
2882 bool isInputChannelStereoPair (int channelIndex) const override
2883 {
2884 int busIdx;
2885 return getOffsetInBusBufferForAbsoluteChannelIndex (true, channelIndex, busIdx) >= 0
2886 && getBusInfo (MediaKind::audio, Direction::input, busIdx).channelCount == 2;
2887 }
2888
2889 bool isOutputChannelStereoPair (int channelIndex) const override
2890 {
2891 int busIdx;
2892 return getOffsetInBusBufferForAbsoluteChannelIndex (false, channelIndex, busIdx) >= 0
2893 && getBusInfo (MediaKind::audio, Direction::output, busIdx).channelCount == 2;
2894 }
2895
2896 bool acceptsMidi() const override { return hasMidiInput; }
2897 bool producesMidi() const override { return hasMidiOutput; }
2898
2899 //==============================================================================
2900 AudioProcessorParameter* getBypassParameter() const override { return bypassParam; }
2901
2902 //==============================================================================
2904 double getTailLengthSeconds() const override
2905 {
2906 if (processor != nullptr)
2907 {
2908 auto sampleRate = getSampleRate();
2909
2910 if (sampleRate > 0.0)
2911 {
2912 auto tailSamples = processor->getTailSamples();
2913
2914 if (tailSamples == Vst::kInfiniteTail)
2915 return std::numeric_limits<double>::infinity();
2916
2917 return jlimit (0, 0x7fffffff, (int) processor->getTailSamples()) / sampleRate;
2918 }
2919 }
2920
2921 return 0.0;
2922 }
2923
2924 //==============================================================================
2925 AudioProcessorEditor* createEditor() override
2926 {
2927 if (auto* view = tryCreatingView())
2928 return new VST3PluginWindow (this, view);
2929
2930 return nullptr;
2931 }
2932
2933 bool hasEditor() const override
2934 {
2935 // (if possible, avoid creating a second instance of the editor, because that crashes some plugins)
2936 if (getActiveEditor() != nullptr)
2937 return true;
2938
2939 VSTComSmartPtr<IPlugView> view (tryCreatingView(), false);
2940 return view != nullptr;
2941 }
2942
2943 //==============================================================================
2944 int getNumPrograms() override { return programNames.size(); }
2945 const String getProgramName (int index) override { return index >= 0 ? programNames[index] : String(); }
2946 void changeProgramName (int, const String&) override {}
2947
2948 int getCurrentProgram() override
2949 {
2950 if (programNames.size() > 0 && editController != nullptr)
2951 if (auto* param = getParameterForID (programParameterID))
2952 return jmax (0, roundToInt (param->getValue() * (float) (programNames.size() - 1)));
2953
2954 return 0;
2955 }
2956
2957 void setCurrentProgram (int program) override
2958 {
2959 if (programNames.size() > 0 && editController != nullptr)
2960 {
2961 auto value = static_cast<Vst::ParamValue> (program) / static_cast<Vst::ParamValue> (jmax (1, programNames.size() - 1));
2962
2963 if (auto* param = getParameterForID (programParameterID))
2964 param->setValueNotifyingHost ((float) value);
2965 }
2966 }
2967
2968 //==============================================================================
2969 void reset() override
2970 {
2971 const SpinLock::ScopedLockType lock (processMutex);
2972
2973 if (holder->component != nullptr && processor != nullptr)
2974 {
2975 processor->setProcessing (false);
2976 holder->component->setActive (false);
2977
2978 holder->component->setActive (true);
2979 processor->setProcessing (true);
2980 }
2981 }
2982
2983 //==============================================================================
2984 void getStateInformation (MemoryBlock& destData) override
2985 {
2986 // The VST3 plugin format requires that get/set state calls are made
2987 // from the message thread.
2988 // We'll lock the message manager here as a safety precaution, but some
2989 // plugins may still misbehave!
2990
2992 MessageManagerLock lock;
2993
2994 parameterDispatcher.flush();
2995
2996 XmlElement state ("VST3PluginState");
2997
2998 appendStateFrom (state, holder->component, "IComponent");
2999 appendStateFrom (state, editController, "IEditController");
3000
3001 AudioProcessor::copyXmlToBinary (state, destData);
3002 }
3003
3004 void setStateInformation (const void* data, int sizeInBytes) override
3005 {
3006 // The VST3 plugin format requires that get/set state calls are made
3007 // from the message thread.
3008 // We'll lock the message manager here as a safety precaution, but some
3009 // plugins may still misbehave!
3010
3012 MessageManagerLock lock;
3013
3014 parameterDispatcher.flush();
3015
3016 if (auto head = AudioProcessor::getXmlFromBinary (data, sizeInBytes))
3017 {
3018 auto componentStream (createMemoryStreamForState (*head, "IComponent"));
3019
3020 if (componentStream != nullptr && holder->component != nullptr)
3021 holder->component->setState (componentStream);
3022
3023 if (editController != nullptr)
3024 {
3025 if (componentStream != nullptr)
3026 {
3027 int64 result;
3028 componentStream->seek (0, IBStream::kIBSeekSet, &result);
3029 setComponentStateAndResetParameters (*componentStream);
3030 }
3031
3032 auto controllerStream (createMemoryStreamForState (*head, "IEditController"));
3033
3034 if (controllerStream != nullptr)
3035 editController->setState (controllerStream);
3036 }
3037 }
3038 }
3039
3040 void setComponentStateAndResetParameters (Steinberg::MemoryStream& stream)
3041 {
3042 jassert (editController != nullptr);
3043
3044 warnOnFailureIfImplemented (editController->setComponentState (&stream));
3045 resetParameters();
3046 }
3047
3048 void resetParameters()
3049 {
3050 for (auto* parameter : getParameters())
3051 {
3052 auto* vst3Param = static_cast<VST3Parameter*> (parameter);
3053 const auto value = (float) editController->getParamNormalized (vst3Param->getParamID());
3054 vst3Param->setValueWithoutUpdatingProcessor (value);
3055 }
3056 }
3057
3058 MemoryBlock getStateForPresetFile() const
3059 {
3060 VSTComSmartPtr<Steinberg::MemoryStream> memoryStream (new Steinberg::MemoryStream(), false);
3061
3062 if (memoryStream == nullptr || holder->component == nullptr)
3063 return {};
3064
3065 const auto saved = Steinberg::Vst::PresetFile::savePreset (memoryStream,
3066 holder->cidOfComponent,
3067 holder->component,
3068 editController);
3069
3070 if (saved)
3071 return { memoryStream->getData(), static_cast<size_t> (memoryStream->getSize()) };
3072
3073 return {};
3074 }
3075
3076 bool setStateFromPresetFile (const MemoryBlock& rawData) const
3077 {
3078 auto rawDataCopy = rawData;
3079 VSTComSmartPtr<Steinberg::MemoryStream> memoryStream (new Steinberg::MemoryStream (rawDataCopy.getData(), (int) rawDataCopy.getSize()), false);
3080
3081 if (memoryStream == nullptr || holder->component == nullptr)
3082 return false;
3083
3084 return Steinberg::Vst::PresetFile::loadPreset (memoryStream, holder->cidOfComponent,
3085 holder->component, editController, nullptr);
3086 }
3087
3088 //==============================================================================
3089 void fillInPluginDescription (PluginDescription& description) const override
3090 {
3091 holder->fillInPluginDescription (description);
3092 }
3093
3095 void getCurrentProgramStateInformation (MemoryBlock& destData) override
3096 {
3097 destData.setSize (0, true);
3098 }
3099
3101 void setCurrentProgramStateInformation (const void* data, int sizeInBytes) override
3102 {
3103 ignoreUnused (data, sizeInBytes);
3104 }
3105
3106private:
3107 //==============================================================================
3108 #if JUCE_LINUX || JUCE_BSD
3109 SharedResourcePointer<RunLoop> runLoop;
3110 #endif
3111
3112 std::unique_ptr<VST3ComponentHolder> holder;
3113
3114 friend VST3HostContext;
3115
3116 // Information objects:
3117 String company;
3118 std::unique_ptr<PClassInfo> info;
3119 std::unique_ptr<PClassInfo2> info2;
3120 std::unique_ptr<PClassInfoW> infoW;
3121
3122 // Rudimentary interfaces:
3123 VSTComSmartPtr<Vst::IEditController> editController;
3124 VSTComSmartPtr<Vst::IEditController2> editController2;
3125 VSTComSmartPtr<Vst::IMidiMapping> midiMapping;
3126 VSTComSmartPtr<Vst::IAudioProcessor> processor;
3127 VSTComSmartPtr<Vst::IComponentHandler> componentHandler;
3128 VSTComSmartPtr<Vst::IComponentHandler2> componentHandler2;
3129 VSTComSmartPtr<Vst::IUnitInfo> unitInfo;
3130 VSTComSmartPtr<Vst::IUnitData> unitData;
3131 VSTComSmartPtr<Vst::IProgramListData> programListData;
3132 VSTComSmartPtr<Vst::IConnectionPoint> componentConnection, editControllerConnection;
3133 VSTComSmartPtr<Vst::ChannelContext::IInfoListener> trackInfoListener;
3134
3139 HostBufferMapper inputBusMap, outputBusMap;
3140
3141 StringArray programNames;
3142 Vst::ParamID programParameterID = (Vst::ParamID) -1;
3143
3144 std::map<Vst::ParamID, VST3Parameter*> idToParamMap;
3145 EditControllerParameterDispatcher parameterDispatcher;
3146 StoredMidiMapping storedMidiMapping;
3147
3148 /* The plugin may request a restart during playback, which may in turn
3149 attempt to call functions such as setProcessing and setActive. It is an
3150 error to call these functions simultaneously with
3151 IAudioProcessor::process, so we use this mutex to ensure that this
3152 scenario is impossible.
3153 */
3154 SpinLock processMutex;
3155
3156 //==============================================================================
3157 template <typename Type>
3158 static void appendStateFrom (XmlElement& head, VSTComSmartPtr<Type>& object, const String& identifier)
3159 {
3160 if (object != nullptr)
3161 {
3162 Steinberg::MemoryStream stream;
3163
3164 const auto result = object->getState (&stream);
3165
3166 if (result == kResultTrue)
3167 {
3168 MemoryBlock info (stream.getData(), (size_t) stream.getSize());
3169 head.createNewChildElement (identifier)->addTextElement (info.toBase64Encoding());
3170 }
3171 }
3172 }
3173
3174 static VSTComSmartPtr<Steinberg::MemoryStream> createMemoryStreamForState (XmlElement& head, StringRef identifier)
3175 {
3176 if (auto* state = head.getChildByName (identifier))
3177 {
3178 MemoryBlock mem;
3179
3180 if (mem.fromBase64Encoding (state->getAllSubText()))
3181 {
3182 VSTComSmartPtr<Steinberg::MemoryStream> stream (new Steinberg::MemoryStream(), false);
3183 stream->setSize ((TSize) mem.getSize());
3184 mem.copyTo (stream->getData(), 0, mem.getSize());
3185 return stream;
3186 }
3187 }
3188
3189 return nullptr;
3190 }
3191
3192 CachedParamValues cachedParamValues;
3193 VSTComSmartPtr<ParameterChanges> inputParameterChanges { new ParameterChanges };
3194 VSTComSmartPtr<ParameterChanges> outputParameterChanges { new ParameterChanges };
3195 VSTComSmartPtr<MidiEventList> midiInputs { new MidiEventList }, midiOutputs { new MidiEventList };
3196 Vst::ProcessContext timingInfo; //< Only use this in processBlock()!
3197 bool isControllerInitialised = false, isActive = false, lastProcessBlockCallWasBypass = false;
3198 const bool hasMidiInput = getNumSingleDirectionBusesFor (holder->component, MediaKind::event, Direction::input) > 0,
3199 hasMidiOutput = getNumSingleDirectionBusesFor (holder->component, MediaKind::event, Direction::output) > 0;
3200 VST3Parameter* bypassParam = nullptr;
3201
3202 //==============================================================================
3204 void interconnectComponentAndController()
3205 {
3206 componentConnection.loadFrom (holder->component);
3207 editControllerConnection.loadFrom (editController);
3208
3209 if (componentConnection != nullptr && editControllerConnection != nullptr)
3210 {
3211 warnOnFailure (componentConnection->connect (editControllerConnection));
3212 warnOnFailure (editControllerConnection->connect (componentConnection));
3213 }
3214 }
3215
3216 void refreshParameterList() override
3217 {
3218 AudioProcessorParameterGroup newParameterTree;
3219
3220 // We're going to add parameter groups to the tree recursively in the same order as the
3221 // first parameters contained within them.
3222 std::map<Vst::UnitID, Vst::UnitInfo> infoMap;
3223 std::map<Vst::UnitID, AudioProcessorParameterGroup*> groupMap;
3224 groupMap[Vst::kRootUnitId] = &newParameterTree;
3225
3226 if (unitInfo != nullptr)
3227 {
3228 const auto numUnits = unitInfo->getUnitCount();
3229
3230 for (int i = 1; i < numUnits; ++i)
3231 {
3232 Vst::UnitInfo ui{};
3233 unitInfo->getUnitInfo (i, ui);
3234 infoMap[ui.id] = std::move (ui);
3235 }
3236 }
3237
3238 {
3239 auto allIds = getAllParamIDs (*editController);
3240 inputParameterChanges ->initialise (allIds);
3241 outputParameterChanges->initialise (allIds);
3242 cachedParamValues = CachedParamValues { std::move (allIds) };
3243 }
3244
3245 for (int i = 0; i < editController->getParameterCount(); ++i)
3246 {
3247 auto paramInfo = getParameterInfoForIndex (i);
3248 auto* param = new VST3Parameter (*this,
3249 i,
3250 paramInfo.id,
3251 (paramInfo.flags & Vst::ParameterInfo::kCanAutomate) != 0);
3252
3253 if ((paramInfo.flags & Vst::ParameterInfo::kIsBypass) != 0)
3254 bypassParam = param;
3255
3256 std::function<AudioProcessorParameterGroup* (Vst::UnitID)> findOrCreateGroup;
3257 findOrCreateGroup = [&groupMap, &infoMap, &findOrCreateGroup] (Vst::UnitID groupID)
3258 {
3259 auto existingGroup = groupMap.find (groupID);
3260
3261 if (existingGroup != groupMap.end())
3262 return existingGroup->second;
3263
3264 auto groupInfo = infoMap.find (groupID);
3265
3266 if (groupInfo == infoMap.end())
3267 return groupMap[Vst::kRootUnitId];
3268
3269 auto* group = new AudioProcessorParameterGroup (String (groupInfo->first),
3270 toString (groupInfo->second.name),
3271 {});
3272 groupMap[groupInfo->first] = group;
3273
3274 auto* parentGroup = findOrCreateGroup (groupInfo->second.parentUnitId);
3275 parentGroup->addChild (std::unique_ptr<AudioProcessorParameterGroup> (group));
3276
3277 return group;
3278 };
3279
3280 auto* group = findOrCreateGroup (paramInfo.unitId);
3281 group->addChild (std::unique_ptr<AudioProcessorParameter> (param));
3282 }
3283
3284 setHostedParameterTree (std::move (newParameterTree));
3285
3286 idToParamMap = [this]
3287 {
3288 std::map<Vst::ParamID, VST3Parameter*> result;
3289
3290 for (auto* parameter : getParameters())
3291 {
3292 auto* vst3Param = static_cast<VST3Parameter*> (parameter);
3293 result.emplace (vst3Param->getParamID(), vst3Param);
3294 }
3295
3296 return result;
3297 }();
3298 }
3299
3300 void synchroniseStates()
3301 {
3302 Steinberg::MemoryStream stream;
3303
3304 if (holder->component->getState (&stream) == kResultTrue)
3305 if (stream.seek (0, Steinberg::IBStream::kIBSeekSet, nullptr) == kResultTrue)
3306 setComponentStateAndResetParameters (stream);
3307 }
3308
3309 void grabInformationObjects()
3310 {
3311 processor.loadFrom (holder->component);
3312 unitInfo.loadFrom (holder->component);
3313 programListData.loadFrom (holder->component);
3314 unitData.loadFrom (holder->component);
3315 editController2.loadFrom (holder->component);
3316 midiMapping.loadFrom (holder->component);
3317 componentHandler.loadFrom (holder->component);
3318 componentHandler2.loadFrom (holder->component);
3319 trackInfoListener.loadFrom (holder->component);
3320
3321 if (processor == nullptr) processor.loadFrom (editController);
3322 if (unitInfo == nullptr) unitInfo.loadFrom (editController);
3323 if (programListData == nullptr) programListData.loadFrom (editController);
3324 if (unitData == nullptr) unitData.loadFrom (editController);
3325 if (editController2 == nullptr) editController2.loadFrom (editController);
3326 if (midiMapping == nullptr) midiMapping.loadFrom (editController);
3327 if (componentHandler == nullptr) componentHandler.loadFrom (editController);
3328 if (componentHandler2 == nullptr) componentHandler2.loadFrom (editController);
3329 if (trackInfoListener == nullptr) trackInfoListener.loadFrom (editController);
3330 }
3331
3332 void setStateForAllMidiBuses (bool newState)
3333 {
3334 setStateForAllEventBuses (holder->component, newState, Direction::input);
3335 setStateForAllEventBuses (holder->component, newState, Direction::output);
3336 }
3337
3338 std::vector<ChannelMapping> createChannelMappings (bool isInput) const
3339 {
3340 std::vector<ChannelMapping> result;
3341 result.reserve ((size_t) getBusCount (isInput));
3342
3343 for (auto i = 0; i < getBusCount (isInput); ++i)
3344 result.emplace_back (*getBus (isInput, i));
3345
3346 return result;
3347 }
3348
3349 void setupIO()
3350 {
3351 setStateForAllMidiBuses (true);
3352
3353 Vst::ProcessSetup setup;
3354 setup.symbolicSampleSize = Vst::kSample32;
3355 setup.maxSamplesPerBlock = 1024;
3356 setup.sampleRate = 44100.0;
3357 setup.processMode = Vst::kRealtime;
3358
3359 warnOnFailure (processor->setupProcessing (setup));
3360
3361 inputBusMap .prepare (createChannelMappings (true));
3362 outputBusMap.prepare (createChannelMappings (false));
3363 setRateAndBufferSizeDetails (setup.sampleRate, (int) setup.maxSamplesPerBlock);
3364 }
3365
3366 static AudioProcessor::BusesProperties getBusProperties (VSTComSmartPtr<Vst::IComponent>& component)
3367 {
3368 AudioProcessor::BusesProperties busProperties;
3369 VSTComSmartPtr<Vst::IAudioProcessor> processor;
3370 processor.loadFrom (component.get());
3371
3372 for (int dirIdx = 0; dirIdx < 2; ++dirIdx)
3373 {
3374 const bool isInput = (dirIdx == 0);
3375 const Vst::BusDirection dir = (isInput ? Vst::kInput : Vst::kOutput);
3376 const int numBuses = component->getBusCount (Vst::kAudio, dir);
3377
3378 for (int i = 0; i < numBuses; ++i)
3379 {
3380 Vst::BusInfo info;
3381
3382 if (component->getBusInfo (Vst::kAudio, dir, (Steinberg::int32) i, info) != kResultOk)
3383 continue;
3384
3385 AudioChannelSet layout = (info.channelCount == 0 ? AudioChannelSet::disabled()
3386 : AudioChannelSet::discreteChannels (info.channelCount));
3387
3388 Vst::SpeakerArrangement arr;
3389 if (processor != nullptr && processor->getBusArrangement (dir, i, arr) == kResultOk)
3391
3392 busProperties.addBus (isInput, toString (info.name), layout,
3393 (info.flags & Vst::BusInfo::kDefaultActive) != 0);
3394 }
3395 }
3396
3397 return busProperties;
3398 }
3399
3400 //==============================================================================
3401 Vst::BusInfo getBusInfo (MediaKind kind, Direction direction, int index = 0) const
3402 {
3403 Vst::BusInfo busInfo;
3404 busInfo.mediaType = toVstType (kind);
3405 busInfo.direction = toVstType (direction);
3406 busInfo.channelCount = 0;
3407
3408 holder->component->getBusInfo (busInfo.mediaType, busInfo.direction,
3409 (Steinberg::int32) index, busInfo);
3410 return busInfo;
3411 }
3412
3413 //==============================================================================
3414 void updateBypass (bool processBlockBypassedCalled)
3415 {
3416 // to remain backward compatible, the logic needs to be the following:
3417 // - if processBlockBypassed was called then definitely bypass the VST3
3418 // - if processBlock was called then only un-bypass the VST3 if the previous
3419 // call was processBlockBypassed, otherwise do nothing
3420 if (processBlockBypassedCalled)
3421 {
3422 if (bypassParam != nullptr && (bypassParam->getValue() == 0.0f || ! lastProcessBlockCallWasBypass))
3423 bypassParam->setValue (1.0f);
3424 }
3425 else
3426 {
3427 if (lastProcessBlockCallWasBypass && bypassParam != nullptr)
3428 bypassParam->setValue (0.0f);
3429
3430 }
3431
3432 lastProcessBlockCallWasBypass = processBlockBypassedCalled;
3433 }
3434
3435 //==============================================================================
3437 IPlugView* tryCreatingView() const
3438 {
3440
3441 IPlugView* v = editController->createView (Vst::ViewType::kEditor);
3442
3443 if (v == nullptr) v = editController->createView (nullptr);
3444 if (v == nullptr) editController->queryInterface (IPlugView::iid, (void**) &v);
3445
3446 return v;
3447 }
3448
3449 //==============================================================================
3450 template <typename FloatType>
3451 void associateWith (Vst::ProcessData& destination, AudioBuffer<FloatType>& buffer)
3452 {
3453 destination.inputs = inputBusMap .getVst3LayoutForJuceBuffer (buffer);
3454 destination.outputs = outputBusMap.getVst3LayoutForJuceBuffer (buffer);
3455 }
3456
3457 void associateWith (Vst::ProcessData& destination, MidiBuffer& midiBuffer)
3458 {
3459 midiInputs->clear();
3460 midiOutputs->clear();
3461
3462 if (acceptsMidi())
3463 {
3464 MidiEventList::hostToPluginEventList (*midiInputs,
3465 midiBuffer,
3466 storedMidiMapping,
3467 [this] (const auto controlID, const auto paramValue)
3468 {
3469 if (auto* param = this->getParameterForID (controlID))
3470 param->setValueNotifyingHost ((float) paramValue);
3471 });
3472 }
3473
3474 destination.inputEvents = midiInputs;
3475 destination.outputEvents = midiOutputs;
3476 }
3477
3478 void updateTimingInformation (Vst::ProcessData& destination, double processSampleRate)
3479 {
3480 toProcessContext (timingInfo, getPlayHead(), processSampleRate);
3481 destination.processContext = &timingInfo;
3482 }
3483
3484 Vst::ParameterInfo getParameterInfoForIndex (Steinberg::int32 index) const
3485 {
3486 Vst::ParameterInfo paramInfo{};
3487
3488 if (editController != nullptr)
3489 editController->getParameterInfo ((int32) index, paramInfo);
3490
3491 return paramInfo;
3492 }
3493
3494 Vst::ProgramListInfo getProgramListInfo (int index) const
3495 {
3496 Vst::ProgramListInfo paramInfo{};
3497
3498 if (unitInfo != nullptr)
3499 unitInfo->getProgramListInfo (index, paramInfo);
3500
3501 return paramInfo;
3502 }
3503
3504 void syncProgramNames()
3505 {
3506 programNames.clear();
3507
3508 if (processor == nullptr || editController == nullptr)
3509 return;
3510
3511 Vst::UnitID programUnitID;
3512 Vst::ParameterInfo paramInfo{};
3513
3514 {
3515 int idx, num = editController->getParameterCount();
3516
3517 for (idx = 0; idx < num; ++idx)
3518 if (editController->getParameterInfo (idx, paramInfo) == kResultOk
3519 && (paramInfo.flags & Steinberg::Vst::ParameterInfo::kIsProgramChange) != 0)
3520 break;
3521
3522 if (idx >= num)
3523 return;
3524
3525 programParameterID = paramInfo.id;
3526 programUnitID = paramInfo.unitId;
3527 }
3528
3529 if (unitInfo != nullptr)
3530 {
3531 Vst::UnitInfo uInfo{};
3532 const int unitCount = unitInfo->getUnitCount();
3533
3534 for (int idx = 0; idx < unitCount; ++idx)
3535 {
3536 if (unitInfo->getUnitInfo(idx, uInfo) == kResultOk
3537 && uInfo.id == programUnitID)
3538 {
3539 const int programListCount = unitInfo->getProgramListCount();
3540
3541 for (int j = 0; j < programListCount; ++j)
3542 {
3543 Vst::ProgramListInfo programListInfo{};
3544
3545 if (unitInfo->getProgramListInfo (j, programListInfo) == kResultOk
3546 && programListInfo.id == uInfo.programListId)
3547 {
3548 Vst::String128 name;
3549
3550 for (int k = 0; k < programListInfo.programCount; ++k)
3551 if (unitInfo->getProgramName (programListInfo.id, k, name) == kResultOk)
3552 programNames.add (toString (name));
3553
3554 return;
3555 }
3556 }
3557
3558 break;
3559 }
3560 }
3561 }
3562
3563 if (editController != nullptr && paramInfo.stepCount > 0)
3564 {
3565 auto numPrograms = paramInfo.stepCount + 1;
3566
3567 for (int i = 0; i < numPrograms; ++i)
3568 {
3569 auto valueNormalized = static_cast<Vst::ParamValue> (i) / static_cast<Vst::ParamValue> (paramInfo.stepCount);
3570
3571 Vst::String128 programName;
3572 if (editController->getParamStringByValue (paramInfo.id, valueNormalized, programName) == kResultOk)
3573 programNames.add (toString (programName));
3574 }
3575 }
3576 }
3577
3579};
3580
3582
3583//==============================================================================
3584tresult VST3HostContext::beginEdit (Vst::ParamID paramID)
3585{
3586 if (plugin == nullptr)
3587 return kResultTrue;
3588
3589 if (auto* param = plugin->getParameterForID (paramID))
3590 {
3591 param->beginChangeGesture();
3592 return kResultTrue;
3593 }
3594
3595 return kResultFalse;
3596}
3597
3598tresult VST3HostContext::performEdit (Vst::ParamID paramID, Vst::ParamValue valueNormalised)
3599{
3600 if (plugin == nullptr)
3601 return kResultTrue;
3602
3603 if (auto* param = plugin->getParameterForID (paramID))
3604 {
3605 param->setValueNotifyingHost ((float) valueNormalised);
3606
3607 // did the plug-in already update the parameter internally
3608 if (plugin->editController->getParamNormalized (paramID) != (float) valueNormalised)
3609 return plugin->editController->setParamNormalized (paramID, valueNormalised);
3610
3611 return kResultTrue;
3612 }
3613
3614 return kResultFalse;
3615}
3616
3617tresult VST3HostContext::endEdit (Vst::ParamID paramID)
3618{
3619 if (plugin == nullptr)
3620 return kResultTrue;
3621
3622 if (auto* param = plugin->getParameterForID (paramID))
3623 {
3624 param->endChangeGesture();
3625 return kResultTrue;
3626 }
3627
3628 return kResultFalse;
3629}
3630
3631tresult VST3HostContext::restartComponent (Steinberg::int32 flags)
3632{
3633 // If you hit this, the plugin has requested a restart from a thread other than
3634 // the UI thread. JUCE should be able to cope, but you should consider filing a bug
3635 // report against the plugin.
3637
3638 componentRestarter.restart (flags);
3639 return kResultTrue;
3640}
3641
3642tresult PLUGIN_API VST3HostContext::setDirty (TBool needsSave)
3643{
3644 if (needsSave)
3645 plugin->updateHostDisplay (AudioPluginInstance::ChangeDetails{}.withNonParameterStateChanged (true));
3646
3647 return kResultOk;
3648}
3649
3650void VST3HostContext::restartComponentOnMessageThread (int32 flags)
3651{
3652 if (plugin == nullptr)
3653 {
3655 return;
3656 }
3657
3658 if (hasFlag (flags, Vst::kReloadComponent))
3659 plugin->reset();
3660
3661 if (hasFlag (flags, Vst::kIoChanged))
3662 {
3663 auto sampleRate = plugin->getSampleRate();
3664 auto blockSize = plugin->getBlockSize();
3665
3666 // Have to deactivate here, otherwise prepareToPlay might not pick up the new bus layouts
3667 plugin->releaseResources();
3668 plugin->prepareToPlay (sampleRate >= 8000 ? sampleRate : 44100.0,
3669 blockSize > 0 ? blockSize : 1024);
3670 }
3671
3672 if (hasFlag (flags, Vst::kLatencyChanged))
3673 if (plugin->processor != nullptr)
3674 plugin->setLatencySamples (jmax (0, (int) plugin->processor->getLatencySamples()));
3675
3676 if (hasFlag (flags, Vst::kMidiCCAssignmentChanged))
3677 plugin->updateMidiMappings();
3678
3679 if (hasFlag (flags, Vst::kParamValuesChanged))
3680 plugin->resetParameters();
3681
3682 plugin->updateHostDisplay (AudioProcessorListener::ChangeDetails().withProgramChanged (true)
3683 .withParameterInfoChanged (true));
3684}
3685
3686//==============================================================================
3687tresult VST3HostContext::ContextMenu::popup (Steinberg::UCoord x, Steinberg::UCoord y)
3688{
3689 Array<const Item*> subItemStack;
3690 OwnedArray<PopupMenu> menuStack;
3691 PopupMenu* topLevelMenu = menuStack.add (new PopupMenu());
3692
3693 for (int i = 0; i < items.size(); ++i)
3694 {
3695 auto& item = items.getReference (i).item;
3696 auto* menuToUse = menuStack.getLast();
3697
3698 if (hasFlag (item.flags, Item::kIsGroupStart & ~Item::kIsDisabled))
3699 {
3700 subItemStack.add (&item);
3701 menuStack.add (new PopupMenu());
3702 }
3703 else if (hasFlag (item.flags, Item::kIsGroupEnd))
3704 {
3705 if (auto* subItem = subItemStack.getLast())
3706 {
3707 if (auto* m = menuStack [menuStack.size() - 2])
3708 m->addSubMenu (toString (subItem->name), *menuToUse,
3709 ! hasFlag (subItem->flags, Item::kIsDisabled),
3710 nullptr,
3711 hasFlag (subItem->flags, Item::kIsChecked));
3712
3713 menuStack.removeLast (1);
3714 subItemStack.removeLast (1);
3715 }
3716 }
3717 else if (hasFlag (item.flags, Item::kIsSeparator))
3718 {
3719 menuToUse->addSeparator();
3720 }
3721 else
3722 {
3723 menuToUse->addItem (item.tag != 0 ? (int) item.tag : (int) zeroTagReplacement,
3724 toString (item.name),
3725 ! hasFlag (item.flags, Item::kIsDisabled),
3726 hasFlag (item.flags, Item::kIsChecked));
3727 }
3728 }
3729
3730 PopupMenu::Options options;
3731
3732 if (auto* ed = owner.getActiveEditor())
3733 {
3734 #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
3735 if (auto* peer = ed->getPeer())
3736 {
3737 auto scale = peer->getPlatformScaleFactor();
3738
3739 x = roundToInt (x / scale);
3740 y = roundToInt (y / scale);
3741 }
3742 #endif
3743
3744 options = options.withTargetScreenArea (ed->getScreenBounds().translated ((int) x, (int) y).withSize (1, 1));
3745 }
3746
3747 #if JUCE_MODAL_LOOPS_PERMITTED
3748 // Unfortunately, Steinberg's docs explicitly say this should be modal..
3749 handleResult (topLevelMenu->showMenu (options));
3750 #else
3751 topLevelMenu->showMenuAsync (options, ModalCallbackFunction::create (menuFinished, VSTComSmartPtr<ContextMenu> (this)));
3752 #endif
3753
3754 return kResultOk;
3755}
3756
3757//==============================================================================
3758tresult VST3HostContext::notifyProgramListChange (Vst::ProgramListID, Steinberg::int32)
3759{
3760 if (plugin != nullptr)
3761 plugin->syncProgramNames();
3762
3763 return kResultTrue;
3764}
3765
3766//==============================================================================
3767//==============================================================================
3768VST3PluginFormat::VST3PluginFormat() = default;
3769VST3PluginFormat::~VST3PluginFormat() = default;
3770
3771bool VST3PluginFormat::setStateFromVSTPresetFile (AudioPluginInstance* api, const MemoryBlock& rawData)
3772{
3773 if (auto vst3 = dynamic_cast<VST3PluginInstance*> (api))
3774 return vst3->setStateFromPresetFile (rawData);
3775
3776 return false;
3777}
3778
3779void VST3PluginFormat::findAllTypesForFile (OwnedArray<PluginDescription>& results, const String& fileOrIdentifier)
3780{
3781 if (fileMightContainThisPluginType (fileOrIdentifier))
3782 {
3788
3789 VSTComSmartPtr<IPluginFactory> pluginFactory (DLLHandleCache::getInstance()->findOrCreateHandle (fileOrIdentifier)
3790 .getPluginFactory());
3791
3792 if (pluginFactory != nullptr)
3793 {
3794 VSTComSmartPtr<VST3HostContext> host (new VST3HostContext());
3795 DescriptionLister lister (host, pluginFactory);
3796 lister.findDescriptionsAndPerform (File (fileOrIdentifier));
3797
3798 results.addCopiesOf (lister.list);
3799 }
3800 else
3801 {
3803 }
3804 }
3805}
3806
3807void VST3PluginFormat::createARAFactoryAsync (const PluginDescription& description, ARAFactoryCreationCallback callback)
3808{
3809 if (! description.hasARAExtension)
3810 {
3812 callback ({ {}, "The provided plugin does not support ARA features" });
3813 }
3814
3815 File file (description.fileOrIdentifier);
3816 VSTComSmartPtr<IPluginFactory> pluginFactory (
3817 DLLHandleCache::getInstance()->findOrCreateHandle (file.getFullPathName()).getPluginFactory());
3818 const auto* pluginName = description.name.toRawUTF8();
3819
3820 callback ({ ARAFactoryWrapper { ::juce::getARAFactory (pluginFactory, pluginName) }, {} });
3821}
3822
3823static std::unique_ptr<AudioPluginInstance> createVST3Instance (VST3PluginFormat& format,
3824 const PluginDescription& description)
3825{
3826 if (! format.fileMightContainThisPluginType (description.fileOrIdentifier))
3827 return nullptr;
3828
3829 const File file { description.fileOrIdentifier };
3830
3831 struct ScopedWorkingDirectory
3832 {
3833 ~ScopedWorkingDirectory() { previousWorkingDirectory.setAsCurrentWorkingDirectory(); }
3834 File previousWorkingDirectory = File::getCurrentWorkingDirectory();
3835 };
3836
3837 const ScopedWorkingDirectory scope;
3838 file.getParentDirectory().setAsCurrentWorkingDirectory();
3839
3840 const VST3ModuleHandle::Ptr module { VST3ModuleHandle::findOrCreateModule (file, description) };
3841
3842 if (module == nullptr)
3843 return nullptr;
3844
3845 auto holder = std::make_unique<VST3ComponentHolder> (module);
3846
3847 if (! holder->initialise())
3848 return nullptr;
3849
3850 auto instance = std::make_unique<VST3PluginInstance> (std::move (holder));
3851
3852 if (! instance->initialise())
3853 return nullptr;
3854
3855 return instance;
3856}
3857
3858void VST3PluginFormat::createPluginInstance (const PluginDescription& description,
3859 double, int, PluginCreationCallback callback)
3860{
3861 auto result = createVST3Instance (*this, description);
3862
3863 const auto errorMsg = result == nullptr ? TRANS ("Unable to load XXX plug-in file").replace ("XXX", "VST-3")
3864 : String();
3865
3866 callback (std::move (result), errorMsg);
3867}
3868
3869bool VST3PluginFormat::requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const
3870{
3871 return false;
3872}
3873
3874bool VST3PluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier)
3875{
3876 auto f = File::createFileWithoutCheckingPath (fileOrIdentifier);
3877
3878 return f.hasFileExtension (".vst3")
3879 #if JUCE_MAC || JUCE_LINUX || JUCE_BSD
3880 && f.exists();
3881 #else
3882 && f.existsAsFile();
3883 #endif
3884}
3885
3886String VST3PluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier)
3887{
3888 return fileOrIdentifier; //Impossible to tell because every VST3 is a type of shell...
3889}
3890
3891bool VST3PluginFormat::pluginNeedsRescanning (const PluginDescription& description)
3892{
3893 return File (description.fileOrIdentifier).getLastModificationTime() != description.lastFileModTime;
3894}
3895
3896bool VST3PluginFormat::doesPluginStillExist (const PluginDescription& description)
3897{
3898 return File (description.fileOrIdentifier).exists();
3899}
3900
3901StringArray VST3PluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive, bool)
3902{
3903 StringArray results;
3904
3905 for (int i = 0; i < directoriesToSearch.getNumPaths(); ++i)
3906 recursiveFileSearch (results, directoriesToSearch[i], recursive);
3907
3908 return results;
3909}
3910
3911void VST3PluginFormat::recursiveFileSearch (StringArray& results, const File& directory, const bool recursive)
3912{
3913 for (const auto& iter : RangedDirectoryIterator (directory, false, "*", File::findFilesAndDirectories))
3914 {
3915 auto f = iter.getFile();
3916 bool isPlugin = false;
3917
3918 if (fileMightContainThisPluginType (f.getFullPathName()))
3919 {
3920 isPlugin = true;
3921 results.add (f.getFullPathName());
3922 }
3923
3924 if (recursive && (! isPlugin) && f.isDirectory())
3925 recursiveFileSearch (results, f, true);
3926 }
3927}
3928
3929FileSearchPath VST3PluginFormat::getDefaultLocationsToSearch()
3930{
3931 #if JUCE_WINDOWS
3932 const auto localAppData = File::getSpecialLocation (File::windowsLocalAppData) .getFullPathName();
3933 const auto programFiles = File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName();
3934 return FileSearchPath (localAppData + "\\Programs\\Common\\VST3;" + programFiles + "\\Common Files\\VST3");
3935 #elif JUCE_MAC
3936 return FileSearchPath ("~/Library/Audio/Plug-Ins/VST3;/Library/Audio/Plug-Ins/VST3");
3937 #else
3938 return FileSearchPath ("~/.vst3/;/usr/lib/vst3/;/usr/local/lib/vst3/");
3939 #endif
3940}
3941
3943
3944} // namespace juce
3945
3946#endif // JUCE_PLUGINHOST_VST3
Type jmin(const Type a, const Type b)
Definition MathsFunctions.h:60
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
#define noexcept
Definition DistrhoDefines.h:72
#define final
Definition DistrhoDefines.h:74
static Audio_Scope * scope
Definition player.cpp:26
goto loop
Definition Spc_Cpu.h:155
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
int64_t int64
Definition basics.h:91
int32_t int32
Definition basics.h:89
uint32_t uint32
Definition basics.h:90
static const LV2_Descriptor descriptor
Definition bindings_test_plugin.c:165
ElementType & getReference(const int index) const noexcept
Definition Array.h:243
void clearQuick() noexcept
Definition Array.h:179
void removeLast(int howManyToRemove=1)
Definition Array.h:907
bool add(const ElementType &newElement) noexcept
Definition Array.h:354
int size() const noexcept
Definition Array.h:187
ElementType getLast() const
Definition Array.h:268
ElementType * getRawDataPointer() noexcept
Definition Array.h:283
bool isEmpty() const noexcept
Definition Array.h:193
static File createFileWithoutCheckingPath(const String &absolutePath) noexcept
Definition File.cpp:65
static File getCurrentWorkingDirectory()
Definition File.cpp:1395
int64 getLastModificationTime() const
Definition File.cpp:534
@ findFilesAndDirectories
Definition File.h:463
bool isDirectory() const
Definition File.cpp:1306
const String & getFullPathName() const noexcept
Definition File.h:152
static File getSpecialLocation(const SpecialLocationType type)
Definition File.cpp:1642
size_t getSize() const noexcept
Definition MemoryBlock.h:102
void setSize(const size_t newSize, bool initialiseNewSpaceToZero=false)
Definition MemoryBlock.cpp:109
void copyTo(void *destData, int sourceOffset, size_t numBytes) const noexcept
Definition MemoryBlock.cpp:240
void clear() noexcept
Definition MidiBuffer.cpp:106
static Result ok() noexcept
Definition Result.h:68
@ kIBSeekSet
set absolute seek position
Definition ibstream.h:34
float ScaleFactor
Definition iplugviewcontentscalesupport.h:53
static const FUID iid
Definition iplugviewcontentscalesupport.h:57
virtual tresult PLUGIN_API createInstance(FIDString cid, FIDString _iid, void **obj)=0
char * getData() const
returns the memory pointer
Definition memorystream.cpp:261
tresult PLUGIN_API seek(int64 pos, int32 mode, int64 *result) SMTG_OVERRIDE
Definition memorystream.cpp:154
void setSize(TSize size)
set the memory size, a realloc will occur if memory already used
Definition memorystream.cpp:196
TSize getSize() const
returns the current memory size
Definition memorystream.cpp:190
static bool loadPreset(IBStream *stream, const FUID &classID, IComponent *component, IEditController *editController=nullptr, std::vector< FUID > *otherClassIDArray=nullptr)
Definition vstpresetfile.cpp:144
static bool savePreset(IBStream *stream, const FUID &classID, IComponent *component, IEditController *editController=nullptr, const char *xmlBuffer=nullptr, int32 xmlSize=-1)
Definition vstpresetfile.cpp:102
void clear()
Definition StringArray.cpp:84
bool add(const String &stringToAdd)
Definition StringArray.cpp:108
bool contains(StringRef stringToLookFor, bool ignoreCase=false) const
Definition StringArray.cpp:152
size_t getNumBytesAsUTF8() const noexcept
Definition String.cpp:1956
const char * toRawUTF8() const
Definition String.cpp:1925
XmlElement * getChildByName(StringRef tagNameToLookFor) const noexcept
Definition XmlElement.cpp:613
XmlElement * createNewChildElement(StringRef tagName)
Definition XmlElement.cpp:668
const String & getFullPathName() const noexcept
Definition juce_File.h:153
String trim() const
Definition juce_String.cpp:1656
bool containsIgnoreCase(StringRef text) const noexcept
Definition juce_String.cpp:1045
String substring(int startIndex, int endIndex) const
Definition juce_String.cpp:1498
String getFileNameWithoutExtension() const
Definition File.cpp:378
const char * toRawUTF8() const
Definition String.cpp:1925
void addTextElement(const String &text)
Definition XmlElement.cpp:904
int * l
Definition inflate.c:1579
unsigned * m
Definition inflate.c:1559
register unsigned k
Definition inflate.c:946
register unsigned j
Definition inflate.c:1576
int y
Definition inflate.c:1588
unsigned v[N_MAX]
Definition inflate.c:1584
int g
Definition inflate.c:1573
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
#define DEF_CLASS_IID(ClassName)
Definition funknown.h:78
static PuglViewHint int value
Definition pugl.h:1708
static const char * title
Definition pugl.h:1747
static const char * name
Definition pugl.h:1582
static uintptr_t parent
Definition pugl.h:1644
virtual ASIOError start()=0
virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate)=0
Steinberg::IPluginFactory *(PLUGIN_API * GetFactoryProc)()
Definition ipluginbase.h:424
#define kVstAudioEffectClass
Definition ivstaudioprocessor.h:30
#define kVstComponentControllerClass
Definition ivsteditcontroller.h:30
struct backing_store_struct * info
Definition jmemsys.h:183
JSAMPIMAGE data
Definition jpeglib.h:945
int version
Definition jpeglib.h:901
#define JUCE_BEGIN_IGNORE_WARNINGS_MSVC(warnings)
Definition juce_CompilerWarnings.h:198
#define JUCE_END_NO_SANITIZE
Definition juce_CompilerWarnings.h:218
#define JUCE_END_IGNORE_WARNINGS_MSVC
Definition juce_CompilerWarnings.h:199
#define JUCE_BEGIN_NO_SANITIZE(warnings)
Definition juce_CompilerWarnings.h:217
#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 DBG(textToWrite)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
#define jassertfalse
#define JUCE_CALLTYPE
#define JUCE_IMPLEMENT_SINGLETON(Classname)
Definition juce_Singleton.h:201
#define JUCE_DECLARE_SINGLETON(Classname, doNotRecreateAfterDeletion)
Definition juce_Singleton.h:184
#define JUCE_DECLARE_VST3_COM_REF_METHODS
Definition juce_VST3Common.h:36
#define JUCE_DECLARE_VST3_COM_QUERY_METHODS
Definition juce_VST3Common.h:40
static void cleanup(void)
Definition lilv_test.c:152
static int JUCE_CDECL comp(const void *a, const void *b)
Definition lsp.c:298
Definition juce_ARACommon.h:29
uint32 ParamID
parameter identifier
Definition vsttypes.h:75
@ kRealtime
realtime processing
Definition ivstaudioprocessor.h:140
@ kOffline
offline processing
Definition ivstaudioprocessor.h:142
uint64 SpeakerArrangement
Bitset of speakers.
Definition vsttypes.h:98
@ kSample32
32-bit precision
Definition ivstaudioprocessor.h:112
@ kSample64
64-bit precision
Definition ivstaudioprocessor.h:113
int int32
Definition ftypes.h:50
int8 TUID[16]
plain UID type
Definition funknown.h:210
const char8 * FIDString
Definition ftypes.h:117
@ kNoInterface
Definition funknown.h:192
@ kResultOk
Definition funknown.h:193
@ kInvalidArgument
Definition funknown.h:196
@ kInternalError
Definition funknown.h:198
@ kOutOfMemory
Definition funknown.h:200
@ kNotImplemented
Definition funknown.h:197
@ kResultFalse
Definition funknown.h:195
@ kResultTrue
Definition funknown.h:194
@ kNotInitialized
Definition funknown.h:199
long long int64
Definition ftypes.h:66
int32 UCoord
Definition ftypes.h:135
int32 tresult
Definition ftypes.h:76
unsigned int uint32
Definition ftypes.h:51
void initialise()
Definition hardgate.cpp:79
void setInt(int value, VARIANT *variant)
Definition juce_win32_UIAHelpers.h:47
void setString(const String &value, VARIANT *variant)
Definition juce_win32_UIAHelpers.h:59
void clear(void *s)
Definition juce_FixedSizeFunction.h:71
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
InterfaceResultWithDeferredAddRef testForMultiple(ToTest &, const Steinberg::TUID)
Definition juce_VST3Common.h:140
void zerostruct(Type &structure) noexcept
Definition juce_Memory.h:32
void callOnMessageThread(Callback &&callback)
Definition juce_audio_processors.cpp:81
juce::String toString(const Steinberg::char8 *string) noexcept
Definition juce_VST3Common.h:159
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Definition juce_MathsFunctions.h:262
AudioChannelSet getChannelSetForSpeakerArrangement(Steinberg::Vst::SpeakerArrangement arr) noexcept
Definition juce_VST3Common.h:478
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
int roundToInt(const FloatType value) noexcept
Definition juce_MathsFunctions.h:465
@ window
Definition juce_AccessibilityRole.h:63
@ list
Definition juce_AccessibilityRole.h:56
@ group
Definition juce_AccessibilityRole.h:61
constexpr int numElementsInArray(Type(&)[N]) noexcept
Definition juce_MathsFunctions.h:344
constexpr Nullopt nullopt
Definition juce_Optional.h:48
QString getString(MimeType mT)
Definition Clipboard.cpp:62
auto getText(std::string_view name) -> QString
Definition embed.cpp:125
auto logicalSize(const QPixmap &pixmap) noexcept
Temporary shim for QPixmap::deviceIndependentSize.
Definition embed.h:61
PluginFactory * getPluginFactory()
Definition PluginFactory.cpp:103
QPoint position(const QDropEvent *de)
position is a backwards-compatible adapter for QDropEvent::position and pos functions.
Definition DeprecationHelper.h:47
bool isPlugin
Definition Util.cpp:41
#define N
Definition nseel-cfunc.c:36
#define false
Definition ordinals.h:83
png_uint_32 length
Definition png.c:2247
static void units(std::ostream &o, const char *u)
Definition ports.cpp:1772
@ kIsProgramChange
Definition ivsteditcontroller.h:69
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
struct HWND__ * HWND
Definition swell-types.h:210
int n
Definition crypt.c:458
int r
Definition crypt.c:458
uch h[RAND_HEAD_LEN]
Definition crypt.c:459
if(compr_offset< 4) return PK_OK
Definition fileio.c:737
int error
Definition extract.c:1038
ulg size
Definition extract.c:2350
void handler(int signal)
Definition fileio.c:1632
int flush(__G__ rawbuf, size, unshrink) __GDEF uch *rawbuf
int result
Definition process.c:1455
int flag
Definition unix.c:754
typedef int(UZ_EXP MsgFn)()
struct zdirent * file
Definition win32.c:1500
_WDL_CSTRING_PREFIX void INT_PTR count
Definition wdlcstring.h:263
_WDL_CSTRING_PREFIX void INT_PTR const char * format
Definition wdlcstring.h:263
#define const
Definition zconf.h:137