26#if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD)
32#if JUCE_PLUGINHOST_ARA && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX)
33#include <ARA_API/ARAVST3.h>
37DEF_CLASS_IID (IMainFactory)
38DEF_CLASS_IID (IPlugInEntryPoint)
39DEF_CLASS_IID (IPlugInEntryPoint2)
53#ifndef JUCE_VST3_DEBUGGING
54 #define JUCE_VST3_DEBUGGING 0
57#if JUCE_VST3_DEBUGGING
58 #define VST3_DBG(a) Logger::writeToLog (a);
66 const char* message =
"Unknown result!";
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;
87 if (result != kResultOk && result != kNotImplemented)
93 #define warnOnFailure(x) x
94 #define warnOnFailureIfImplemented(x) x
99static Vst::MediaType
toVstType (
MediaKind x) {
return x == MediaKind::audio ? Vst::kAudio : Vst::kEvent; }
100static Vst::BusDirection
toVstType (Direction x) {
return x == Direction::input ? Vst::kInput : Vst::kOutput; }
106 auto count = controller.getParameterCount();
108 for (
decltype (count) i = 0; i <
count; ++i)
110 Vst::ParameterInfo info{};
111 controller.getParameterInfo (i, info);
127 void push (Steinberg::int32 index,
float value)
129 if (controller ==
nullptr)
133 controller->setParamNormalized (cache.getParamID (index), value);
135 cache.set (index, value);
147 cache.ifSet ([
this] (Steinberg::int32 index,
float value)
149 controller->setParamNormalized (cache.getParamID (index), value);
154 void timerCallback()
override
160 Vst::IEditController* controller =
nullptr;
167 return { {
fuid.getLong1(),
fuid.getLong2(),
fuid.getLong3(),
fuid.getLong4() } };
170template <
typename Range>
175 for (
const auto& item : range)
176 value = (value * 31) + (
uint32) item;
181template <
typename ObjectType>
184 description.version =
toString (
object.version).trim();
185 description.category =
toString (
object.subCategories).trim();
187 if (description.manufacturerName.trim().isEmpty())
188 description.manufacturerName =
toString (
object.vendor).trim();
199 #if JUCE_PLUGINHOST_ARA && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX)
200 for (
const auto& c : info.classes)
202 factories.insert (CharPointer_UTF8 (c.name.c_str()));
208 for (
const auto& c : info.classes)
213 PluginDescription description;
215 description.fileOrIdentifier =
pluginFile.getFullPathName();
216 description.lastFileModTime =
pluginFile.getLastModificationTime();
218 description.manufacturerName = CharPointer_UTF8 (info.factoryInfo.vendor.
c_str());
219 description.name = CharPointer_UTF8 (c.name.c_str());
220 description.descriptiveName = CharPointer_UTF8 (c.name.c_str());
221 description.pluginFormatName =
"VST3";
222 description.numInputChannels = 0;
223 description.numOutputChannels = 0;
225 description.version = CharPointer_UTF8 (c.version.c_str());
227 const auto uid = VST3::UID::fromString (c.cid);
235 StringArray categories;
237 for (
const auto& category : c.subCategories)
238 categories.add (CharPointer_UTF8 (category.c_str()));
240 description.category = categories.joinIntoString (
"|");
242 description.isInstrument =
std::any_of (c.subCategories.begin(),
243 c.subCategories.end(),
244 [] (
const auto&
subcategory) { return subcategory ==
"Instrument"; });
254 const PClassInfo& info, PClassInfo2*
info2, PClassInfoW*
infoW,
255 int numInputs,
int numOutputs)
257 description.fileOrIdentifier =
pluginFile.getFullPathName();
258 description.lastFileModTime =
pluginFile.getLastModificationTime();
260 description.manufacturerName =
company;
261 description.name = name;
262 description.descriptiveName = name;
263 description.pluginFormatName =
"VST3";
264 description.numInputChannels = numInputs;
265 description.numOutputChannels = numOutputs;
273 if (description.category.isEmpty())
274 description.category =
toString (info.category).trim();
276 description.isInstrument = description.category.containsIgnoreCase (
"Instrument");
283 jassert (component !=
nullptr);
291 jassert (component !=
nullptr);
295 const Steinberg::int32
numBuses = component->getBusCount (Vst::kAudio, direction);
299 for (Steinberg::int32 i =
numBuses; --i >= 0;)
303 numChannels += ((
busInfo.flags & Vst::BusInfo::kDefaultActive) != 0 ? (
int)
busInfo.channelCount : 0);
313 jassert (component !=
nullptr);
317 const Steinberg::int32
numBuses = component->getBusCount (Vst::kEvent, direction);
319 for (Steinberg::int32 i =
numBuses; --i >= 0;)
320 warnOnFailure (component->activateBus (Vst::kEvent, direction, i, state));
325 AudioPlayHead* playHead,
333 context.sampleRate = sampleRate;
335 const auto position = playHead !=
nullptr ? playHead->getPosition()
338 if (position.hasValue())
340 if (
const auto timeInSamples = position->getTimeInSamples())
341 context.projectTimeSamples = *timeInSamples;
345 if (
const auto tempo = position->getBpm())
347 context.state |= ProcessContext::kTempoValid;
348 context.tempo = *tempo;
351 if (
const auto loop = position->getLoopPoints())
353 context.state |= ProcessContext::kCycleValid;
354 context.cycleStartMusic =
loop->ppqStart;
355 context.cycleEndMusic =
loop->ppqEnd;
358 if (
const auto sig = position->getTimeSignature())
360 context.state |= ProcessContext::kTimeSigValid;
361 context.timeSigNumerator =
sig->numerator;
362 context.timeSigDenominator =
sig->denominator;
365 if (
const auto pos = position->getPpqPosition())
367 context.state |= ProcessContext::kProjectTimeMusicValid;
368 context.projectTimeMusic = *pos;
371 if (
const auto barStart = position->getPpqPositionOfLastBarStart())
373 context.state |= ProcessContext::kBarPositionValid;
374 context.barPositionMusic = *
barStart;
377 if (
const auto frameRate = position->getFrameRate())
379 if (
const auto offset = position->getEditOriginTime())
381 context.state |= ProcessContext::kSmpteValid;
382 context.smpteOffsetSubframes = (Steinberg::int32) (80.0 * *offset * frameRate->getEffectiveRate());
383 context.frameRate.framesPerSecond = (Steinberg::uint32) frameRate->getBaseRate();
384 context.frameRate.flags = (Steinberg::uint32) ((frameRate->isDrop() ? FrameRate::kDropRate : 0)
385 | (frameRate->isPullDown() ? FrameRate::kPullDownRate : 0));
389 if (
const auto hostTime = position->getHostTimeNs())
391 context.state |= ProcessContext::kSystemTimeValid;
393 jassert (context.systemTime >= 0);
396 if (position->getIsPlaying()) context.state |= ProcessContext::kPlaying;
397 if (position->getIsRecording()) context.state |= ProcessContext::kRecording;
398 if (position->getIsLooping()) context.state |= ProcessContext::kCycleActive;
406 public Vst::IComponentHandler2,
407 public Vst::IComponentHandler3,
408 public Vst::IContextMenuTarget,
409 public Vst::IHostApplication,
410 public Vst::IUnitHandler,
411 private ComponentRestarter::Listener
422 FUnknown*
getFUnknown() {
return static_cast<Vst::IComponentHandler*
> (
this); }
424 static bool hasFlag (Steinberg::int32 source, Steinberg::int32 flag)
noexcept
426 return (source & flag) == flag;
430 tresult
PLUGIN_API beginEdit (Vst::ParamID paramID)
override;
431 tresult
PLUGIN_API performEdit (Vst::ParamID paramID, Vst::ParamValue valueNormalized)
override;
432 tresult
PLUGIN_API endEdit (Vst::ParamID paramID)
override;
434 tresult
PLUGIN_API restartComponent (Steinberg::int32 flags)
override;
465 ContextMenu (VST3PluginInstance& pluginInstance) : owner (pluginInstance) {}
466 virtual ~ContextMenu() {}
468 JUCE_DECLARE_VST3_COM_REF_METHODS
469 JUCE_DECLARE_VST3_COM_QUERY_METHODS
471 Steinberg::int32 PLUGIN_API getItemCount()
override {
return (Steinberg::int32) items.size(); }
473 tresult PLUGIN_API addItem (
const Item& item, IContextMenuTarget* target)
override
477 ItemAndTarget newItem;
479 newItem.target = addVSTComSmartPtrOwner (target);
485 tresult PLUGIN_API removeItem (
const Item& toRemove, IContextMenuTarget* target)
override
487 for (
int i = items.size(); --i >= 0;)
489 auto& item = items.getReference (i);
491 if (item.item.tag == toRemove.tag && item.target.get() == target)
498 tresult PLUGIN_API getItem (Steinberg::int32 tag, Item& result, IContextMenuTarget** target)
override
500 for (
int i = 0; i < items.size(); ++i)
502 auto& item = items.getReference (i);
504 if (item.item.tag == tag)
508 if (target !=
nullptr)
509 *target = item.target.get();
521 #if ! JUCE_MODAL_LOOPS_PERMITTED
522 static void menuFinished (
int modalResult, VSTComSmartPtr<ContextMenu> menu) { menu->handleResult (modalResult); }
526 enum { zeroTagReplacement = 0x7fffffff };
528 Atomic<int> refCount;
529 VST3PluginInstance& owner;
534 VSTComSmartPtr<IContextMenuTarget> target;
537 Array<ItemAndTarget> items;
539 void handleResult (
int result)
544 if (result == zeroTagReplacement)
547 for (
int i = 0; i < items.size(); ++i)
549 auto& item = items.getReference (i);
551 if ((
int) item.item.tag == result)
553 if (item.target !=
nullptr)
554 item.target->executeMenuItem ((Steinberg::int32) result);
564 Vst::IContextMenu*
PLUGIN_API createContextMenu (IPlugView*,
const Vst::ParamID*)
override
574 tresult
PLUGIN_API executeMenuItem (Steinberg::int32)
override
581 tresult
PLUGIN_API getName (Vst::String128 name)
override
584 str.copyTo (name, 0, 127);
588 tresult
PLUGIN_API createInstance (TUID cid, TUID iid,
void** obj)
override
595 return kInvalidArgument;
611 return kNotImplemented;
615 tresult
PLUGIN_API notifyUnitSelection (Vst::UnitID)
override
621 tresult
PLUGIN_API notifyProgramListChange (Vst::ProgramListID, Steinberg::int32)
override;
624 tresult
PLUGIN_API queryInterface (
const TUID iid,
void** obj)
override
640 Atomic<int> refCount;
651 using Int = Steinberg::int64;
656 explicit Attribute (Int x)
noexcept { constructFrom (std::move (x)); }
657 explicit Attribute (Float x)
noexcept { constructFrom (std::move (x)); }
658 explicit Attribute (String x)
noexcept { constructFrom (std::move (x)); }
659 explicit Attribute (Binary x)
noexcept { constructFrom (std::move (x)); }
661 Attribute (Attribute&& other)
noexcept
663 moveFrom (std::move (other));
666 Attribute& operator= (Attribute&& other)
noexcept
669 moveFrom (std::move (other));
673 ~Attribute() noexcept
678 tresult getInt (Steinberg::int64& result)
const
680 if (kind != Kind::tagInt)
683 result = storage.storedInt;
687 tresult getFloat (
double& result)
const
689 if (kind != Kind::tagFloat)
692 result = storage.storedFloat;
696 tresult getString (Vst::TChar* data, Steinberg::uint32 numBytes)
const
698 if (kind != Kind::tagString)
702 storage.storedString.data(),
703 jmin (
sizeof (Vst::TChar) * storage.storedString.size(), (
size_t) numBytes));
707 tresult getBinary (
const void*& data, Steinberg::uint32& numBytes)
const
709 if (kind != Kind::tagBinary)
712 data = storage.storedBinary.data();
713 numBytes = (Steinberg::uint32) storage.storedBinary.size();
718 void constructFrom (Int x)
noexcept { kind = Kind::tagInt;
new (&storage.storedInt) Int (std::move (x)); }
719 void constructFrom (Float x)
noexcept { kind = Kind::tagFloat;
new (&storage.storedFloat) Float (std::move (x)); }
720 void constructFrom (String x)
noexcept { kind = Kind::tagString;
new (&storage.storedString) String (std::move (x)); }
721 void constructFrom (Binary x)
noexcept { kind = Kind::tagBinary;
new (&storage.storedBinary) Binary (std::move (x)); }
723 void reset() noexcept
727 case Kind::tagInt:
break;
728 case Kind::tagFloat:
break;
729 case Kind::tagString: storage.storedString.~vector();
break;
730 case Kind::tagBinary: storage.storedBinary.~vector();
break;
734 void moveFrom (Attribute&& other)
noexcept
738 case Kind::tagInt: constructFrom (std::move (other.storage.storedInt));
break;
739 case Kind::tagFloat: constructFrom (std::move (other.storage.storedFloat));
break;
740 case Kind::tagString: constructFrom (std::move (other.storage.storedString));
break;
741 case Kind::tagBinary: constructFrom (std::move (other.storage.storedBinary));
break;
745 enum class Kind { tagInt, tagFloat, tagString, tagBinary };
752 Steinberg::int64 storedInt;
768 AttributeList() =
default;
769 virtual ~AttributeList() =
default;
771 JUCE_DECLARE_VST3_COM_REF_METHODS
772 JUCE_DECLARE_VST3_COM_QUERY_METHODS
775 tresult PLUGIN_API setInt (AttrID attr, Steinberg::int64 value)
override
777 return set (attr, value);
780 tresult PLUGIN_API setFloat (AttrID attr,
double value)
override
782 return set (attr, value);
785 tresult PLUGIN_API setString (AttrID attr,
const Vst::TChar*
string)
override
790 tresult PLUGIN_API setBinary (AttrID attr,
const void* data, Steinberg::uint32 size)
override
792 const auto* ptr =
static_cast<const char*
> (
data);
796 tresult PLUGIN_API getInt (AttrID attr, Steinberg::int64& result)
override
798 return get (attr, [&] (
const auto& x) {
return x.getInt (result); });
801 tresult PLUGIN_API getFloat (AttrID attr,
double& result)
override
803 return get (attr, [&] (
const auto& x) {
return x.getFloat (result); });
806 tresult PLUGIN_API getString (AttrID attr, Vst::TChar* result, Steinberg::uint32 length)
override
808 return get (attr, [&] (
const auto& x) {
return x.getString (result, length); });
811 tresult PLUGIN_API getBinary (AttrID attr,
const void*& data, Steinberg::uint32& size)
override
813 return get (attr, [&] (
const auto& x) {
return x.getBinary (data, size); });
817 template <
typename Value>
818 tresult set (AttrID attr, Value&& value)
821 return kInvalidArgument;
823 const auto iter = attributes.find (attr);
825 if (iter != attributes.end())
826 iter->second = Attribute (std::forward<Value> (value));
828 attributes.emplace (attr, Attribute (std::forward<Value> (value)));
833 template <
typename Visitor>
834 tresult
get (AttrID attr, Visitor&& visitor)
837 return kInvalidArgument;
839 const auto iter = attributes.find (attr);
841 if (iter == attributes.cend())
844 return visitor (iter->second);
848 Atomic<int> refCount { 1 };
853 struct Message
final :
public Vst::IMessage
856 virtual ~Message() =
default;
858 JUCE_DECLARE_VST3_COM_REF_METHODS
859 JUCE_DECLARE_VST3_COM_QUERY_METHODS
861 FIDString PLUGIN_API getMessageID()
override {
return messageId.toRawUTF8(); }
862 void PLUGIN_API setMessageID (FIDString
id)
override { messageId =
toString (
id); }
863 Vst::IAttributeList* PLUGIN_API getAttributes()
override {
return &attributeList; }
866 AttributeList attributeList;
868 Atomic<int> refCount { 1 };
902 const auto moduleinfoNewLocation = file.getChildFile (
"Contents").getChildFile (
"Resources").getChildFile (
"moduleinfo.json");
907 return tryLoadFast (file, file.getChildFile (
"Contents").getChildFile (
"moduleinfo.json"));
911 IPluginFactory& factory,
917 PFactoryInfo factoryInfo;
918 factory.getFactoryInfo (&factoryInfo);
927 #if JUCE_PLUGINHOST_ARA && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX)
928 for (Steinberg::int32 i = 0; i <
numClasses; ++i)
931 factory.getClassInfo (i, &info);
937 for (Steinberg::int32 i = 0; i <
numClasses; ++i)
940 factory.getClassInfo (i, &info);
945 const String name (toString (info.name).trim());
957 if (
pf2.loadFrom (&factory))
960 pf2->getClassInfo2 (i,
info2.get());
963 if (
pf3.loadFrom (&factory))
965 infoW.reset (
new PClassInfoW());
966 pf3->getClassInfoUnicode (i,
infoW.get());
972 PluginDescription desc;
977 if (component.loadFrom (&factory, info.cid))
979 if (component->initialize (host.getFUnknown()) == kResultOk)
985 info,
info2.get(),
infoW.get(), numInputs, numOutputs);
987 component->terminate();
1001 desc.hasARAExtension =
true;
1003 if (desc.uniqueId != 0)
1026 if (factory !=
nullptr)
1034 #if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD
1046 if (factory ==
nullptr)
1057 void* getFunction (
const char* functionName)
1059 #if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD
1060 return library.getFunction (functionName);
1074 IPluginFactory* factory =
nullptr;
1076 static constexpr const char*
factoryFnName =
"GetPluginFactory";
1079 static constexpr const char*
entryFnName =
"InitDll";
1080 static constexpr const char*
exitFnName =
"ExitDll";
1083 #elif JUCE_LINUX || JUCE_BSD
1084 static constexpr const char*
entryFnName =
"ModuleEntry";
1085 static constexpr const char*
exitFnName =
"ModuleExit";
1089 static constexpr const char*
entryFnName =
"bundleEntry";
1090 static constexpr const char*
exitFnName =
"bundleExit";
1096 #if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD
1097 DynamicLibrary library;
1101 if (library.open (
dllFile.getFullPathName()))
1108 if (
proc (library.getNativeHandle()))
1128 auto*
utf8 =
dllFile.getFullPathName().toRawUTF8();
1146 if (error.object !=
nullptr)
1171 #if JUCE_LINUX || JUCE_BSD
1180 return file == handle->getFile();
1186 openHandles.push_back (std::make_unique<DLLHandle> (file));
1191 #if JUCE_LINUX || JUCE_BSD
1207 return file.getChildFile (
"Contents")
1209 .getChildFile (file.getFileNameWithoutExtension() +
".so");
1223#if JUCE_LINUX || JUCE_BSD
1238 Linux::FileDescriptor
fd)
override
1240 if (handler ==
nullptr)
1241 return kInvalidArgument;
1247 LinuxEventLoop::registerFdCallback (
fd, [
this] (
int descriptor)
1263 if (handler ==
nullptr)
1264 return kInvalidArgument;
1278 LinuxEventLoop::unregisterFdCallback (iter->first);
1293 if (handler ==
nullptr || milliseconds <= 0)
1294 return kInvalidArgument;
1296 timerCallers.emplace_back (handler, (
int) milliseconds);
1305 return kInvalidArgument;
1314 tresult
PLUGIN_API queryInterface (
const TUID,
void**)
override {
return kNoInterface; }
1320 TimerCaller (Linux::ITimerHandler* h,
int interval) : handler (h) { startTimer (interval); }
1321 ~TimerCaller()
override { stopTimer(); }
1323 void timerCallback()
override { handler->onTimer(); }
1325 bool operator== (Linux::ITimerHandler* other)
const noexcept {
return handler == other; }
1327 Linux::ITimerHandler* handler =
nullptr;
1363 const PluginDescription& description)
1368 if (
module->file == file &&
module->name == description.name)
1381 IPluginFactory* getPluginFactory()
1383 return DLLHandleCache::getInstance()->findOrCreateHandle (file.getFullPathName()).getPluginFactory();
1386 File getFile()
const noexcept {
return file; }
1387 String getName()
const noexcept {
return name; }
1398 bool open (
const PluginDescription& description)
1406 for (Steinberg::int32 i = 0; i <
numClasses; ++i)
1417 if (toString (info.name).trim() == description.name
1418 && (uniqueId == description.uniqueId || deprecatedUid == description.deprecatedUid))
1420 name = description.name;
1431 bool isOpen =
false;
1437template <
typename Type,
size_t N>
1445template <
typename Callback>
1448 #if JUCE_PLUGINHOST_ARA && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX)
1450 for (Steinberg::int32 i = 0; i <
numClasses; ++i)
1470 #if JUCE_PLUGINHOST_ARA && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX)
1476 ARA::IMainFactory* source;
1477 if (pluginFactory->createInstance (pcClassInfo.cid, ARA::IMainFactory::iid, (void**) &source)
1478 == Steinberg::kResultOk)
1480 factory = getOrCreateARAFactory (source->getFactory(),
1481 [source]() { source->release(); });
1496 auto* pluginFactory =
module.getPluginFactory();
1497 return getARAFactory (pluginFactory, module.getName());
1501struct VST3PluginWindow final :
public AudioProcessorEditor,
1502 private ComponentMovementWatcher,
1503 private ComponentBoundsConstrainer,
1506 VST3PluginWindow (AudioPluginInstance* owner, VSTComSmartPtr<IPlugView> pluginView)
1507 : AudioProcessorEditor (owner),
1508 ComponentMovementWatcher (this),
1511 , embeddedComponent (*owner)
1517 setConstrainer (
this);
1519 warnOnFailure (view->setFrame (
this));
1520 view->queryInterface (Steinberg::IPlugViewContentScaleSupport::iid, (
void**) &scaleInterface);
1522 setContentScaleFactor();
1525 setResizable (view->canResize() == kResultTrue,
false);
1528 ~VST3PluginWindow()
override
1530 if (scaleInterface !=
nullptr)
1531 scaleInterface->release();
1533 #if JUCE_LINUX || JUCE_BSD
1534 embeddedComponent.removeClient();
1538 warnOnFailure (view->removed());
1540 warnOnFailure (view->setFrame (
nullptr));
1542 processor.editorBeingDeleted (
this);
1545 embeddedComponent.setView (
nullptr);
1551 #if JUCE_LINUX || JUCE_BSD
1552 Steinberg::tresult PLUGIN_API queryInterface (
const Steinberg::TUID queryIid,
void** obj)
override
1554 if (doUIDsMatch (queryIid, Steinberg::Linux::IRunLoop::iid))
1556 *obj = &runLoop.get();
1563 return Steinberg::kNotImplemented;
1566 JUCE_DECLARE_VST3_COM_QUERY_METHODS
1569 JUCE_DECLARE_VST3_COM_REF_METHODS
1571 void paint (Graphics& g)
override
1573 g.fillAll (Colours::black);
1576 void mouseWheelMove (
const MouseEvent&,
const MouseWheelDetails& wheel)
override
1578 view->onWheel (wheel.deltaY);
1581 void focusGained (FocusChangeType)
override { view->onFocus (
true); }
1582 void focusLost (FocusChangeType)
override { view->onFocus (
false); }
1587 bool keyStateChanged (
bool )
override {
return true; }
1588 bool keyPressed (
const KeyPress& )
override {
return true; }
1591 void checkBounds (Rectangle<int>& bounds,
1592 const Rectangle<int>&,
1593 const Rectangle<int>&,
1599 auto rect = componentToVST3Rect (bounds);
1600 view->checkSizeConstraint (&rect);
1601 bounds = vst3ToComponentRect (rect);
1605 void componentPeerChanged()
override {}
1608 ViewRect componentToVST3Rect (Rectangle<int> r)
const
1610 const auto physical = localAreaToGlobal (r) * nativeScaleFactor * getDesktopScaleFactor();
1611 return { 0, 0, physical.getWidth(), physical.getHeight() };
1615 Rectangle<int> vst3ToComponentRect (
const ViewRect& vr)
const
1617 return getLocalArea (
nullptr, Rectangle<int> { vr.right, vr.bottom } / (nativeScaleFactor * getDesktopScaleFactor()));
1620 void componentMovedOrResized (
bool,
bool wasResized)
override
1622 if (recursiveResize || ! wasResized || getTopLevelComponent()->getPeer() ==
nullptr)
1625 if (view->canResize() == kResultTrue)
1627 auto rect = componentToVST3Rect (getLocalBounds());
1628 view->checkSizeConstraint (&rect);
1631 const ScopedValueSetter<bool> recursiveResizeSetter (recursiveResize,
true);
1633 const auto logicalSize = vst3ToComponentRect (rect);
1634 setSize (logicalSize.getWidth(), logicalSize.getHeight());
1637 embeddedComponent.setBounds (getLocalBounds());
1639 view->onSize (&rect);
1644 warnOnFailure (view->getSize (&rect));
1646 resizeWithRect (embeddedComponent, rect);
1650 Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
1653 using ComponentMovementWatcher::componentMovedOrResized;
1655 void componentVisibilityChanged()
override
1657 attachPluginWindow();
1659 componentMovedOrResized (
true,
true);
1662 using ComponentMovementWatcher::componentVisibilityChanged;
1667 warnOnFailure (view->getSize (&rect));
1668 resizeWithRect (*
this, rect);
1671 tresult PLUGIN_API resizeView (IPlugView* incomingView, ViewRect* newSize)
override
1673 const ScopedValueSetter<bool> recursiveResizeSetter (recursiveResize,
true);
1675 if (incomingView !=
nullptr && newSize !=
nullptr && incomingView == view.get())
1677 const auto oldPhysicalSize = componentToVST3Rect (getLocalBounds());
1678 const auto logicalSize = vst3ToComponentRect (*newSize);
1679 setSize (logicalSize.getWidth(), logicalSize.getHeight());
1680 embeddedComponent.setSize (logicalSize.getWidth(), logicalSize.getHeight());
1683 embeddedComponent.updateHWNDBounds();
1684 #elif JUCE_LINUX || JUCE_BSD
1685 embeddedComponent.updateEmbeddedBounds();
1690 auto currentPhysicalSize = componentToVST3Rect (getLocalBounds());
1692 if (currentPhysicalSize.getWidth() != oldPhysicalSize.getWidth()
1693 || currentPhysicalSize.getHeight() != oldPhysicalSize.getHeight()
1697 const ScopedValueSetter<bool> inOnSizeSetter (isInOnSize,
true);
1698 view->onSize (¤tPhysicalSize);
1705 return kInvalidArgument;
1709 void resizeWithRect (Component& comp,
const ViewRect& rect)
const
1711 const auto logicalSize = vst3ToComponentRect (rect);
1712 comp.setSize (jmax (10, logicalSize.getWidth()),
1713 jmax (10, logicalSize.getHeight()));
1716 void attachPluginWindow()
1718 if (pluginHandle == HandleFormat{})
1721 pluginHandle =
static_cast<HWND
> (embeddedComponent.getHWND());
1724 embeddedComponent.setBounds (getLocalBounds());
1725 addAndMakeVisible (embeddedComponent);
1728 pluginHandle = (HandleFormat) embeddedComponent.getView();
1729 #elif JUCE_LINUX || JUCE_BSD
1730 pluginHandle = (HandleFormat) embeddedComponent.getHostWindowID();
1733 if (pluginHandle == HandleFormat{})
1739 [[maybe_unused]]
const auto attachedResult = view->attached ((
void*) pluginHandle, defaultVST3WindowType);
1740 [[maybe_unused]]
const auto warning = warnOnFailure (attachedResult);
1742 if (attachedResult == kResultOk)
1743 attachedCalled =
true;
1745 updatePluginScale();
1749 void updatePluginScale()
1751 if (scaleInterface !=
nullptr)
1752 setContentScaleFactor();
1757 void setContentScaleFactor()
1759 if (scaleInterface !=
nullptr)
1761 [[maybe_unused]]
const auto result = scaleInterface->setContentScaleFactor ((Steinberg::IPlugViewContentScaleSupport::ScaleFactor) getEffectiveScale());
1764 [[maybe_unused]]
const auto warning = warnOnFailure (result);
1769 void setScaleFactor (
float s)
override
1771 userScaleFactor = s;
1772 setContentScaleFactor();
1776 float getEffectiveScale()
const
1778 return nativeScaleFactor * userScaleFactor;
1782 Atomic<int> refCount { 1 };
1783 VSTComSmartPtr<IPlugView> view;
1786 using HandleFormat = HWND;
1788 struct ViewComponent final :
public HWNDComponent
1793 inner.addToDesktop (0);
1795 if (
auto* peer = inner.getPeer())
1796 setHWND (peer->getNativeHandle());
1799 void paint (Graphics& g)
override { g.fillAll (Colours::black); }
1802 struct Inner final :
public Component
1804 Inner() { setOpaque (
true); }
1805 void paint (Graphics& g)
override { g.fillAll (Colours::black); }
1811 ViewComponent embeddedComponent;
1813 NSViewComponentWithParent embeddedComponent;
1814 using HandleFormat = NSView*;
1815 #elif JUCE_LINUX || JUCE_BSD
1816 SharedResourcePointer<RunLoop> runLoop;
1817 XEmbedComponent embeddedComponent {
true,
false };
1818 using HandleFormat = Window;
1820 Component embeddedComponent;
1821 using HandleFormat =
void*;
1824 HandleFormat pluginHandle = {};
1825 bool recursiveResize =
false, isInOnSize =
false, attachedCalled =
false;
1828 float nativeScaleFactor = 1.0f;
1829 float userScaleFactor = 1.0f;
1831 struct ScaleNotifierCallback
1833 VST3PluginWindow& window;
1835 void operator() (
float platformScale)
const
1837 MessageManager::callAsync ([ref = Component::SafePointer<VST3PluginWindow> (&window), platformScale]
1839 if (
auto* r =
ref.getComponent())
1841 r->nativeScaleFactor = platformScale;
1842 r->setContentScaleFactor();
1846 r->embeddedComponent.updateHWNDBounds();
1847 #elif JUCE_LINUX || JUCE_BSD
1848 r->embeddedComponent.updateEmbeddedBounds();
1855 NativeScaleFactorNotifier scaleNotifier {
this, ScaleNotifierCallback { *
this } };
1861JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
1864static
bool hasARAExtension (IPluginFactory* pluginFactory, const String& pluginClassName)
1866 bool result =
false;
1868 forEachARAFactory (pluginFactory,
1869 [&pluginClassName, &result] (
const auto& pcClassInfo)
1871 if (compareWithString (pcClassInfo.name, pluginClassName) == 0)
1885struct VST3ComponentHolder
1887 VST3ComponentHolder (
const VST3ModuleHandle::Ptr& m) : module (m)
1889 host = addVSTComSmartPtrOwner (
new VST3HostContext());
1892 ~VST3ComponentHolder()
1897 bool isIComponentAlsoIEditController()
const
1899 if (component ==
nullptr)
1905 return VSTComSmartPtr<Vst::IEditController>().loadFrom (component.get());
1908 bool fetchController (VSTComSmartPtr<Vst::IEditController>& editController)
1910 if (! isComponentInitialised && ! initialise())
1913 editController.loadFrom (component.get());
1916 TUID controllerCID = { 0 };
1918 if (editController ==
nullptr
1919 && component->getControllerClassId (controllerCID) == kResultTrue
1920 && FUID (controllerCID).isValid())
1922 editController.loadFrom (factory.
get(), controllerCID);
1925 if (editController ==
nullptr)
1928 auto numClasses = factory->countClasses();
1930 for (Steinberg::int32 i = 0; i < numClasses; ++i)
1932 PClassInfo classInfo;
1933 factory->getClassInfo (i, &classInfo);
1936 editController.loadFrom (factory.
get(), classInfo.cid);
1940 return (editController !=
nullptr);
1944 void fillInPluginDescription (PluginDescription& description)
const
1946 jassert (module !=
nullptr && isComponentInitialised);
1948 PFactoryInfo factoryInfo;
1949 factory->getFactoryInfo (&factoryInfo);
1951 auto classIdx = getClassIndex (module->getName());
1956 [[maybe_unused]]
bool success = (factory->getClassInfo (classIdx, &info) == kResultOk);
1959 VSTComSmartPtr<IPluginFactory2> pf2;
1960 VSTComSmartPtr<IPluginFactory3> pf3;
1965 if (pf2.loadFrom (factory.
get()))
1967 info2.
reset (
new PClassInfo2());
1968 pf2->getClassInfo2 (classIdx, info2.
get());
1975 if (pf3.loadFrom (factory.
get()))
1977 pf3->setHostContext (host->getFUnknown());
1978 infoW.
reset (
new PClassInfoW());
1979 pf3->getClassInfoUnicode (classIdx, infoW.
get());
1987 int totalNumInputChannels = 0, totalNumOutputChannels = 0;
1989 int n = component->getBusCount (Vst::kAudio, Vst::kInput);
1990 for (
int i = 0; i < n; ++i)
1991 if (component->getBusInfo (Vst::kAudio, Vst::kInput, i, bus) == kResultOk)
1992 totalNumInputChannels += ((bus.flags & Vst::BusInfo::kDefaultActive) != 0 ? bus.channelCount : 0);
1994 n = component->getBusCount (Vst::kAudio, Vst::kOutput);
1995 for (
int i = 0; i < n; ++i)
1996 if (component->getBusInfo (Vst::kAudio, Vst::kOutput, i, bus) == kResultOk)
1997 totalNumOutputChannels += ((bus.flags & Vst::BusInfo::kDefaultActive) != 0 ? bus.channelCount : 0);
1999 createPluginDescription (description, module->getFile(),
2000 factoryInfo.vendor, module->getName(),
2001 info, info2.
get(), infoW.
get(),
2002 totalNumInputChannels,
2003 totalNumOutputChannels);
2005 description.hasARAExtension = hasARAExtension (factory.
get(), description.name);
2016 if (isComponentInitialised)
2024 factory = addVSTComSmartPtrOwner (module->getPluginFactory());
2027 if ((classIdx = getClassIndex (module->getName())) < 0)
2031 if (factory->getClassInfo (classIdx, &info) != kResultOk)
2034 if (! component.loadFrom (factory.get(), info.cid) || component ==
nullptr)
2037 cidOfComponent = FUID (info.cid);
2039 if (warnOnFailure (component->initialize (host->getFUnknown())) != kResultOk)
2042 isComponentInitialised =
true;
2049 if (isComponentInitialised)
2051 component->terminate();
2052 isComponentInitialised =
false;
2055 component =
nullptr;
2059 int getClassIndex (
const String& className)
const
2062 const Steinberg::int32 numClasses = factory->countClasses();
2064 for (Steinberg::int32 j = 0; j < numClasses; ++j)
2065 if (factory->getClassInfo (j, &info) == kResultOk
2067 &&
toString (info.name).trim() == className)
2074 VST3ModuleHandle::Ptr
module;
2075 VSTComSmartPtr<IPluginFactory> factory;
2076 VSTComSmartPtr<VST3HostContext> host;
2077 VSTComSmartPtr<Vst::IComponent> component;
2078 FUID cidOfComponent;
2080 bool isComponentInitialised =
false;
2089class ParamValueQueue final :
public Vst::IParamValueQueue
2092 ParamValueQueue (Vst::ParamID idIn, Steinberg::int32 parameterIndexIn)
2093 : paramId (idIn), parameterIndex (parameterIndexIn) {}
2095 virtual ~ParamValueQueue() =
default;
2097 JUCE_DECLARE_VST3_COM_REF_METHODS
2098 JUCE_DECLARE_VST3_COM_QUERY_METHODS
2100 Vst::ParamID PLUGIN_API getParameterId()
override {
return paramId; }
2102 Steinberg::int32 getParameterIndex() const noexcept {
return parameterIndex; }
2104 Steinberg::int32 PLUGIN_API getPointCount()
override {
return size; }
2106 tresult PLUGIN_API getPoint (Steinberg::int32 index,
2107 Steinberg::int32& sampleOffset,
2108 Vst::ParamValue& value)
override
2110 if (! isPositiveAndBelow (index, size))
2111 return kResultFalse;
2114 value = cachedValue;
2119 tresult PLUGIN_API addPoint (Steinberg::int32,
2120 Vst::ParamValue value,
2121 Steinberg::int32& index)
override
2124 set ((
float) value);
2129 void set (
float valueIn)
2131 cachedValue = valueIn;
2135 void clear() {
size = 0; }
2137 float get() const noexcept
2144 const Vst::ParamID paramId;
2145 const Steinberg::int32 parameterIndex;
2147 Steinberg::int32
size = 0;
2148 Atomic<int> refCount;
2157class ParameterChanges final :
public Vst::IParameterChanges
2159 static constexpr Steinberg::int32 notInVector = -1;
2165 VSTComSmartPtr<ParamValueQueue> ptr;
2166 Steinberg::int32 index = notInVector;
2173 virtual ~ParameterChanges() =
default;
2175 JUCE_DECLARE_VST3_COM_REF_METHODS
2176 JUCE_DECLARE_VST3_COM_QUERY_METHODS
2178 Steinberg::int32 PLUGIN_API getParameterCount()
override
2180 return (Steinberg::int32) queues.
size();
2183 ParamValueQueue* PLUGIN_API getParameterData (Steinberg::int32 index)
override
2185 if (isPositiveAndBelow (index, queues.size()))
2187 auto& entry = queues[(
size_t) index];
2189 jassert (entry->index == index);
2190 return entry->ptr.get();
2196 ParamValueQueue* PLUGIN_API addParameterData (
const Vst::ParamID&
id,
2197 Steinberg::int32& index)
override
2199 const auto it = map.find (
id);
2201 if (it == map.end())
2204 auto& result = it->second;
2206 if (result.index == notInVector)
2208 result.index = (Steinberg::int32) queues.size();
2209 queues.push_back (&result);
2212 index = result.index;
2213 return result.ptr.get();
2216 void set (Vst::ParamID
id,
float value)
2218 Steinberg::int32 indexOut = notInVector;
2220 if (
auto* queue = addParameterData (
id, indexOut))
2226 for (
auto* item : queues)
2227 item->index = notInVector;
2234 for (
const auto [index,
id] :
enumerate (idsIn))
2235 map.emplace (id, Entry {
std::
make_unique<ParamValueQueue> (id, (Steinberg::int32) index) });
2237 queues.reserve (map.size());
2241 template <
typename Callback>
2242 void forEach (Callback&& callback)
const
2244 for (
const auto* item : queues)
2246 auto* ptr = item->ptr.get();
2247 callback (ptr->getParameterIndex(), ptr->getParameterId(), ptr->get());
2254 Atomic<int> refCount;
2258class VST3PluginInstance final :
public AudioPluginInstance
2262 struct VST3Parameter final :
public Parameter
2264 VST3Parameter (VST3PluginInstance& parent,
2265 Steinberg::int32 vstParameterIndex,
2267 bool parameterIsAutomatable)
2268 : pluginInstance (parent),
2269 vstParamIndex (vstParameterIndex),
2270 paramID (parameterID),
2271 automatable (parameterIsAutomatable)
2275 float getValue()
const override
2277 return pluginInstance.cachedParamValues.get (vstParamIndex);
2282 void setValue (
float newValue)
override
2284 pluginInstance.cachedParamValues.set (vstParamIndex, newValue);
2285 pluginInstance.parameterDispatcher.push (vstParamIndex, newValue);
2292 void setValueWithoutUpdatingProcessor (
float newValue)
2294 if (! exactlyEqual (pluginInstance.cachedParamValues.exchangeWithoutNotifying (vstParamIndex, newValue), newValue))
2295 sendValueChangedMessageToListeners (newValue);
2298 String getText (
float value,
int maximumLength)
const override
2300 MessageManagerLock
lock;
2302 if (pluginInstance.editController !=
nullptr)
2304 Vst::String128 result;
2306 if (pluginInstance.editController->getParamStringByValue (paramID, value, result) == kResultOk)
2307 return toString (result).substring (0, maximumLength);
2310 return Parameter::getText (value, maximumLength);
2313 float getValueForText (
const String& text)
const override
2315 MessageManagerLock
lock;
2317 if (pluginInstance.editController !=
nullptr)
2319 Vst::ParamValue result;
2321 if (pluginInstance.editController->getParamValueByString (paramID,
toString (text), result) == kResultOk)
2322 return (
float) result;
2325 return Parameter::getValueForText (text);
2328 float getDefaultValue()
const override
2330 return (
float) getParameterInfo().defaultNormalizedValue;
2333 String getName (
int )
const override
2335 return toString (getParameterInfo().title);
2338 String getLabel()
const override
2340 return toString (getParameterInfo().units);
2343 bool isAutomatable()
const override
2348 bool isDiscrete()
const override
2353 int getNumSteps()
const override
2358 StringArray getAllValueStrings()
const override
2363 String getParameterID()
const override
2365 return String (paramID);
2371 Vst::ParameterInfo getParameterInfo()
const
2373 return pluginInstance.getParameterInfoForIndex (vstParamIndex);
2376 VST3PluginInstance& pluginInstance;
2377 const Steinberg::int32 vstParamIndex;
2379 const bool automatable;
2380 const int numSteps = [&]
2382 auto stepCount = getParameterInfo().stepCount;
2383 return stepCount == 0 ? AudioProcessor::getDefaultNumParameterSteps()
2386 const bool discrete = getNumSteps() != AudioProcessor::getDefaultNumParameterSteps();
2391 : AudioPluginInstance (getBusProperties (componentHolder->component)),
2392 holder (
std::
move (componentHolder))
2394 jassert (holder->isComponentInitialised);
2395 holder->host->setPlugin (
this);
2398 ~VST3PluginInstance()
override
2400 callOnMessageThread ([
this] { cleanup(); });
2405 jassert (getActiveEditor() ==
nullptr);
2409 if (editControllerConnection !=
nullptr && componentConnection !=
nullptr)
2411 editControllerConnection->disconnect (componentConnection.get());
2412 componentConnection->disconnect (editControllerConnection.get());
2415 editController->setComponentHandler (
nullptr);
2417 if (isControllerInitialised && ! holder->isIComponentAlsoIEditController())
2418 editController->terminate();
2420 holder->terminate();
2422 componentConnection =
nullptr;
2423 editControllerConnection =
nullptr;
2426 programListData =
nullptr;
2427 componentHandler2 =
nullptr;
2428 componentHandler =
nullptr;
2429 processor =
nullptr;
2430 midiMapping =
nullptr;
2431 editController2 =
nullptr;
2432 editController =
nullptr;
2443 if (! holder->initialise())
2446 if (! (isControllerInitialised || holder->fetchController (editController)))
2451 if (! holder->isIComponentAlsoIEditController())
2452 editController->initialize (holder->host->getFUnknown());
2454 isControllerInitialised =
true;
2455 editController->setComponentHandler (holder->host.get());
2456 grabInformationObjects();
2457 interconnectComponentAndController();
2459 auto configureParameters = [
this]
2461 initialiseParameterList();
2462 synchroniseStates();
2466 configureParameters();
2471 if (getParameters().isEmpty() && editController->getParameterCount() > 0)
2472 configureParameters();
2474 updateMidiMappings();
2476 parameterDispatcher.start (*editController);
2481 void getExtensions (ExtensionsVisitor& visitor)
const override
2483 struct Extensions final :
public ExtensionsVisitor::VST3Client,
2484 public ExtensionsVisitor::ARAClient
2486 explicit Extensions (
const VST3PluginInstance* instanceIn) : instance (instanceIn) {}
2490 MemoryBlock getPreset()
const override {
return instance->getStateForPresetFile(); }
2492 bool setPreset (
const MemoryBlock& rawData)
const override
2494 return instance->setStateFromPresetFile (rawData);
2502 const VST3PluginInstance* instance =
nullptr;
2505 Extensions extensions {
this };
2506 visitor.visitVST3Client (extensions);
2510 visitor.visitARAClient (extensions);
2514 void* getPlatformSpecificData()
override {
return holder->component.get(); }
2516 void updateMidiMappings()
2520 const SpinLock::ScopedLockType processLock (processMutex);
2522 if (midiMapping !=
nullptr)
2523 storedMidiMapping.storeMappings (*midiMapping);
2527 const String getName()
const override
2529 auto&
module = holder->module;
2530 return module != nullptr ? module->getName() : String();
2537 const auto numBuses = getBusCount (isInput);
2539 for (
auto i = 0; i < numBuses; ++i)
2540 result.
push_back (getArrangementForBus (processor.get(), isInput, i));
2549 const auto numBuses = getBusCount (isInput);
2551 for (
auto i = 0; i < numBuses; ++i)
2553 if (
const auto arr = getVst3SpeakerArrangement (getBus (isInput, i)->getLastEnabledLayout()))
2562 void prepareToPlay (
double newSampleRate,
int estimatedSamplesPerBlock)
override
2567 MessageManagerLock
lock;
2569 const SpinLock::ScopedLockType processLock (processMutex);
2573 && approximatelyEqual (getSampleRate(), newSampleRate)
2574 && getBlockSize() == estimatedSamplesPerBlock)
2577 using namespace Vst;
2586 setup.maxSamplesPerBlock = estimatedSamplesPerBlock;
2587 setup.sampleRate = newSampleRate;
2590 warnOnFailure (processor->setupProcessing (setup));
2592 holder->initialise();
2599 auto* inData = inArrangements .empty() ? &nullArrangement : inArrangements .data();
2600 auto* outData = outArrangements.empty() ? &nullArrangement : outArrangements.data();
2602 warnOnFailure (processor->setBusArrangements (inData,
static_cast<int32
> (inArrangements .size()),
2603 outData,
static_cast<int32
> (outArrangements.size())));
2605 const auto inArrActual = getActualArrangements (
true);
2606 const auto outArrActual = getActualArrangements (
false);
2608 jassert (inArrActual == inArrangements && outArrActual == outArrangements);
2611 setRateAndBufferSizeDetails (newSampleRate, estimatedSamplesPerBlock);
2613 auto numInputBuses = getBusCount (
true);
2614 auto numOutputBuses = getBusCount (
false);
2616 for (
int i = 0; i < numInputBuses; ++i)
2617 warnOnFailure (holder->component->activateBus (Vst::kAudio, Vst::kInput, i, getBus (
true, i)->isEnabled() ? 1 : 0));
2619 for (
int i = 0; i < numOutputBuses; ++i)
2620 warnOnFailure (holder->component->activateBus (Vst::kAudio, Vst::kOutput, i, getBus (
false, i)->isEnabled() ? 1 : 0));
2622 setLatencySamples (jmax (0, (
int) processor->getLatencySamples()));
2624 inputBusMap .prepare (createChannelMappings (
true));
2625 outputBusMap.prepare (createChannelMappings (
false));
2627 setStateForAllMidiBuses (
true);
2629 warnOnFailure (holder->component->setActive (
true));
2630 warnOnFailureIfImplemented (processor->setProcessing (
true));
2635 void releaseResources()
override
2637 const SpinLock::ScopedLockType
lock (processMutex);
2641 bool supportsDoublePrecisionProcessing()
const override
2643 return (processor->canProcessSampleSize (Vst::kSample64) == kResultTrue);
2655 VST3Parameter* getParameterForID (Vst::ParamID paramID)
const
2657 const auto it = idToParamMap.find (paramID);
2658 return it != idToParamMap.end() ? it->second :
nullptr;
2662 void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
override
2664 jassert (! isUsingDoublePrecision());
2666 const SpinLock::ScopedLockType processLock (processMutex);
2668 if (isActive && processor !=
nullptr)
2669 processAudio (buffer, midiMessages, Vst::kSample32,
false);
2672 void processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages)
override
2674 jassert (isUsingDoublePrecision());
2676 const SpinLock::ScopedLockType processLock (processMutex);
2678 if (isActive && processor !=
nullptr)
2679 processAudio (buffer, midiMessages, Vst::kSample64,
false);
2682 void processBlockBypassed (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
override
2684 jassert (! isUsingDoublePrecision());
2686 const SpinLock::ScopedLockType processLock (processMutex);
2688 if (bypassParam !=
nullptr)
2690 if (isActive && processor !=
nullptr)
2691 processAudio (buffer, midiMessages, Vst::kSample32,
true);
2695 AudioProcessor::processBlockBypassed (buffer, midiMessages);
2699 void processBlockBypassed (AudioBuffer<double>& buffer, MidiBuffer& midiMessages)
override
2701 jassert (isUsingDoublePrecision());
2703 const SpinLock::ScopedLockType processLock (processMutex);
2705 if (bypassParam !=
nullptr)
2707 if (isActive && processor !=
nullptr)
2708 processAudio (buffer, midiMessages, Vst::kSample64,
true);
2712 AudioProcessor::processBlockBypassed (buffer, midiMessages);
2717 template <
typename FloatType>
2718 void processAudio (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages,
2719 Vst::SymbolicSampleSizes sampleSize,
bool isProcessBlockBypassedCall)
2721 using namespace Vst;
2722 auto numSamples = buffer.getNumSamples();
2724 auto numInputAudioBuses = getBusCount (
true);
2725 auto numOutputAudioBuses = getBusCount (
false);
2727 updateBypass (isProcessBlockBypassedCall);
2731 data.symbolicSampleSize = sampleSize;
2732 data.numInputs = numInputAudioBuses;
2733 data.numOutputs = numOutputAudioBuses;
2734 data.inputParameterChanges = inputParameterChanges.get();
2735 data.outputParameterChanges = outputParameterChanges.get();
2736 data.numSamples = (Steinberg::int32) numSamples;
2738 updateTimingInformation (data, getSampleRate());
2740 for (
int i = getTotalNumInputChannels(); i < buffer.getNumChannels(); ++i)
2741 buffer.clear (i, 0, numSamples);
2743 inputParameterChanges->clear();
2744 outputParameterChanges->clear();
2746 associateWith (data, buffer);
2747 associateWith (data, midiMessages);
2749 cachedParamValues.ifSet ([&] (Steinberg::int32 index,
float value)
2751 inputParameterChanges->set (cachedParamValues.getParamID (index), value);
2754 processor->process (data);
2756 outputParameterChanges->forEach ([&] (Steinberg::int32 vstParamIndex, Vst::ParamID
id,
float value)
2759 parameterDispatcher.push (vstParamIndex, value);
2762 if (
auto* param = getParameterForID (
id))
2763 param->setValueWithoutUpdatingProcessor (value);
2766 midiMessages.clear();
2767 MidiEventList::toMidiBuffer (midiMessages, *midiOutputs);
2771 bool canAddBus (
bool)
const override {
return false; }
2772 bool canRemoveBus (
bool)
const override {
return false; }
2774 bool isBusesLayoutSupported (
const BusesLayout& layouts)
const override
2776 const SpinLock::ScopedLockType processLock (processMutex);
2781 return canApplyBusesLayout (layouts);
2785 for (
const auto isInput : {
true,
false })
2787 auto n = getBusCount (isInput);
2789 for (
int i = 0; i < n; ++i)
2790 if (getChannelLayoutOfBus (isInput, i).isDiscreteLayout())
2797 bool syncBusLayouts (
const BusesLayout& layouts)
const
2799 for (
const auto isInput : {
true,
false })
2801 auto n = getBusCount (isInput);
2802 const Vst::BusDirection vstDir = (isInput ? Vst::kInput : Vst::kOutput);
2804 for (
int busIdx = 0; busIdx < n; ++busIdx)
2806 const bool isEnabled = (! layouts.getChannelSet (isInput, busIdx).isDisabled());
2808 if (holder->component->activateBus (Vst::kAudio, vstDir, busIdx, (isEnabled ? 1 : 0)) != kResultOk)
2817 for (
int i = 0; i < layouts.getBuses (isInput).size(); ++i)
2819 const auto& requested = layouts.getChannelSet (isInput, i);
2821 if (
const auto arr = getVst3SpeakerArrangement (requested.isDisabled() ? getBus (isInput, i)->getLastEnabledLayout() : requested))
2822 result.push_back (*arr);
2830 auto inArrangements = getPotentialArrangements (
true);
2831 auto outArrangements = getPotentialArrangements (
false);
2833 if (! inArrangements.has_value() || ! outArrangements.has_value())
2839 auto& inputArrangements = *inArrangements;
2840 auto& outputArrangements = *outArrangements;
2843 Vst::SpeakerArrangement nullArrangement = {};
2844 auto* inputArrangementData = inputArrangements .empty() ? &nullArrangement : inputArrangements .data();
2845 auto* outputArrangementData = outputArrangements.empty() ? &nullArrangement : outputArrangements.data();
2847 if (processor->setBusArrangements (inputArrangementData,
static_cast<int32
> (inputArrangements .size()),
2848 outputArrangementData,
static_cast<int32
> (outputArrangements.size())) != kResultTrue)
2852 const auto inArrActual = getActualArrangements (
true);
2853 const auto outArrActual = getActualArrangements (
false);
2855 return (inArrActual == inputArrangements && outArrActual == outputArrangements);
2858 bool canApplyBusesLayout (
const BusesLayout& layouts)
const override
2864 const auto previousLayout = getBusesLayout();
2865 const auto result = syncBusLayouts (layouts);
2866 syncBusLayouts (previousLayout);
2871 void updateTrackProperties (
const TrackProperties& properties)
override
2873 if (trackInfoListener !=
nullptr)
2875 auto l = addVSTComSmartPtrOwner (
new TrackPropertiesAttributeList (properties));
2876 trackInfoListener->setChannelContextInfos (l.get());
2880 struct TrackPropertiesAttributeList final :
public Vst::IAttributeList
2882 TrackPropertiesAttributeList (
const TrackProperties& properties) : props (properties) {}
2883 virtual ~TrackPropertiesAttributeList() {}
2885 JUCE_DECLARE_VST3_COM_REF_METHODS
2887 tresult PLUGIN_API queryInterface (
const TUID queryIid,
void** obj)
override
2889 return testForMultiple (*
this,
2891 UniqueBase<Vst::IAttributeList>{},
2892 SharedBase<FUnknown, Vst::IAttributeList>{}).extract (obj);
2895 tresult PLUGIN_API setInt (AttrID, Steinberg::int64)
override {
return kOutOfMemory; }
2896 tresult PLUGIN_API setFloat (AttrID,
double)
override {
return kOutOfMemory; }
2897 tresult PLUGIN_API setString (AttrID,
const Vst::TChar*)
override {
return kOutOfMemory; }
2898 tresult PLUGIN_API setBinary (AttrID,
const void*, Steinberg::uint32)
override {
return kOutOfMemory; }
2899 tresult PLUGIN_API getFloat (AttrID,
double&)
override {
return kResultFalse; }
2900 tresult PLUGIN_API getBinary (AttrID,
const void*&, Steinberg::uint32&)
override {
return kResultFalse; }
2902 tresult PLUGIN_API getString (AttrID
id, Vst::TChar*
string, Steinberg::uint32 size)
override
2904 if (!
std::strcmp (
id, Vst::ChannelContext::kChannelNameKey))
2912 return kResultFalse;
2915 tresult PLUGIN_API getInt (AttrID
id, Steinberg::int64& value)
override
2917 if (!
std::strcmp (Vst::ChannelContext::kChannelNameLengthKey,
id)) value = props.name.length();
2918 else if (!
std::strcmp (Vst::ChannelContext::kChannelColorKey,
id)) value =
static_cast<Steinberg::int64
> (props.colour.getARGB());
2919 else return kResultFalse;
2924 Atomic<int> refCount;
2925 TrackProperties props;
2931 String getChannelName (
int channelIndex, Direction direction)
const
2933 auto numBuses = getNumSingleDirectionBusesFor (holder->component.get(), MediaKind::audio, direction);
2935 int numCountedChannels = 0;
2937 for (
int i = 0; i < numBuses; ++i)
2939 auto busInfo = getBusInfo (MediaKind::audio, direction, i);
2941 numCountedChannels += busInfo.channelCount;
2943 if (channelIndex < numCountedChannels)
2950 const String getInputChannelName (
int channelIndex)
const override {
return getChannelName (channelIndex, Direction::input); }
2951 const String getOutputChannelName (
int channelIndex)
const override {
return getChannelName (channelIndex, Direction::output); }
2953 bool isInputChannelStereoPair (
int channelIndex)
const override
2956 return getOffsetInBusBufferForAbsoluteChannelIndex (
true, channelIndex, busIdx) >= 0
2957 && getBusInfo (MediaKind::audio, Direction::input, busIdx).channelCount == 2;
2960 bool isOutputChannelStereoPair (
int channelIndex)
const override
2963 return getOffsetInBusBufferForAbsoluteChannelIndex (
false, channelIndex, busIdx) >= 0
2964 && getBusInfo (MediaKind::audio, Direction::output, busIdx).channelCount == 2;
2967 bool acceptsMidi()
const override {
return hasMidiInput; }
2968 bool producesMidi()
const override {
return hasMidiOutput; }
2971 AudioProcessorParameter* getBypassParameter()
const override {
return bypassParam; }
2975 double getTailLengthSeconds()
const override
2977 if (processor !=
nullptr)
2979 auto sampleRate = getSampleRate();
2981 if (sampleRate > 0.0)
2983 auto tailSamples = processor->getTailSamples();
2985 if (tailSamples == Vst::kInfiniteTail)
2988 return jlimit (0, 0x7fffffff, (
int) processor->getTailSamples()) / sampleRate;
2996 AudioProcessorEditor* createEditor()
override
2998 if (
auto view = becomeVSTComSmartPtrOwner (tryCreatingView()))
2999 return new VST3PluginWindow (
this, view);
3004 bool hasEditor()
const override
3007 if (getActiveEditor() !=
nullptr)
3010 auto view = becomeVSTComSmartPtrOwner (tryCreatingView());
3011 return view !=
nullptr;
3015 int getNumPrograms()
override {
return programNames.size(); }
3016 const String getProgramName (
int index)
override {
return index >= 0 ? programNames[index] : String(); }
3017 void changeProgramName (
int,
const String&)
override {}
3019 int getCurrentProgram()
override
3021 if (programNames.size() > 0 && editController !=
nullptr)
3022 if (
auto* param = getParameterForID (programParameterID))
3023 return jmax (0, roundToInt (param->getValue() * (
float) (programNames.size() - 1)));
3028 void setCurrentProgram (
int program)
override
3030 if (programNames.size() > 0 && editController !=
nullptr)
3032 auto value =
static_cast<Vst::ParamValue
> (program) /
static_cast<Vst::ParamValue
> (jmax (1, programNames.size() - 1));
3034 if (
auto* param = getParameterForID (programParameterID))
3035 param->setValueNotifyingHost ((
float) value);
3040 void reset()
override
3042 const SpinLock::ScopedLockType
lock (processMutex);
3044 if (holder->component !=
nullptr && processor !=
nullptr)
3046 processor->setProcessing (
false);
3047 holder->component->setActive (
false);
3049 holder->component->setActive (
true);
3050 processor->setProcessing (
true);
3055 void getStateInformation (MemoryBlock& destData)
override
3063 MessageManagerLock
lock;
3065 parameterDispatcher.flush();
3067 XmlElement state (
"VST3PluginState");
3069 appendStateFrom (state, holder->component,
"IComponent");
3070 appendStateFrom (state, editController,
"IEditController");
3072 AudioProcessor::copyXmlToBinary (state, destData);
3075 void setStateInformation (
const void* data,
int sizeInBytes)
override
3083 MessageManagerLock
lock;
3085 parameterDispatcher.flush();
3087 if (
auto head = AudioProcessor::getXmlFromBinary (data, sizeInBytes))
3089 auto componentStream (createMemoryStreamForState (*head,
"IComponent"));
3091 if (componentStream !=
nullptr && holder->component !=
nullptr)
3092 holder->component->setState (componentStream.get());
3094 if (editController !=
nullptr)
3096 if (componentStream !=
nullptr)
3098 Steinberg::int64 result;
3099 componentStream->seek (0, IBStream::kIBSeekSet, &result);
3100 setComponentStateAndResetParameters (*componentStream);
3103 auto controllerStream (createMemoryStreamForState (*head,
"IEditController"));
3105 if (controllerStream !=
nullptr)
3106 editController->setState (controllerStream.get());
3113 jassert (editController !=
nullptr);
3115 warnOnFailureIfImplemented (editController->setComponentState (&stream));
3119 void resetParameters()
3121 for (
auto* parameter : getParameters())
3123 auto* vst3Param =
static_cast<VST3Parameter*
> (parameter);
3124 const auto value = (
float) editController->getParamNormalized (vst3Param->getParamID());
3125 vst3Param->setValueWithoutUpdatingProcessor (value);
3129 MemoryBlock getStateForPresetFile()
const
3133 if (memoryStream ==
nullptr || holder->component ==
nullptr)
3137 holder->cidOfComponent,
3138 holder->component.get(),
3139 editController.get());
3142 return { memoryStream->getData(),
static_cast<size_t> (memoryStream->getSize()) };
3147 bool setStateFromPresetFile (
const MemoryBlock& rawData)
const
3149 auto rawDataCopy = rawData;
3150 auto memoryStream = becomeVSTComSmartPtrOwner (
new Steinberg::MemoryStream (rawDataCopy.getData(), (
int) rawDataCopy.getSize()));
3152 if (memoryStream ==
nullptr || holder->component ==
nullptr)
3156 holder->component.get(), editController.get(),
nullptr);
3160 void fillInPluginDescription (PluginDescription& description)
const override
3162 holder->fillInPluginDescription (description);
3166 void getCurrentProgramStateInformation (MemoryBlock& destData)
override
3168 destData.setSize (0,
true);
3172 void setCurrentProgramStateInformation ([[maybe_unused]]
const void* data,
3173 [[maybe_unused]]
int sizeInBytes)
override
3185 if (processor !=
nullptr)
3186 warnOnFailureIfImplemented (processor->setProcessing (
false));
3188 if (holder->component !=
nullptr)
3189 warnOnFailure (holder->component->setActive (
false));
3191 setStateForAllMidiBuses (
false);
3195 #if JUCE_LINUX || JUCE_BSD
3196 SharedResourcePointer<RunLoop> runLoop;
3201 friend VST3HostContext;
3210 VSTComSmartPtr<Vst::IEditController> editController;
3211 VSTComSmartPtr<Vst::IEditController2> editController2;
3212 VSTComSmartPtr<Vst::IMidiMapping> midiMapping;
3213 VSTComSmartPtr<Vst::IAudioProcessor> processor;
3214 VSTComSmartPtr<Vst::IComponentHandler> componentHandler;
3215 VSTComSmartPtr<Vst::IComponentHandler2> componentHandler2;
3216 VSTComSmartPtr<Vst::IUnitInfo> unitInfo;
3217 VSTComSmartPtr<Vst::IUnitData> unitData;
3218 VSTComSmartPtr<Vst::IProgramListData> programListData;
3219 VSTComSmartPtr<Vst::IConnectionPoint> componentConnection, editControllerConnection;
3220 VSTComSmartPtr<Vst::ChannelContext::IInfoListener> trackInfoListener;
3226 HostBufferMapper inputBusMap, outputBusMap;
3228 StringArray programNames;
3229 Vst::ParamID programParameterID = (Vst::ParamID) -1;
3232 EditControllerParameterDispatcher parameterDispatcher;
3233 StoredMidiMapping storedMidiMapping;
3241 SpinLock processMutex;
3244 template <
typename Type>
3245 static void appendStateFrom (XmlElement& head, VSTComSmartPtr<Type>&
object,
const String& identifier)
3247 if (
object !=
nullptr)
3251 const auto result =
object->getState (&stream);
3253 if (result == kResultTrue)
3256 head.createNewChildElement (identifier)->addTextElement (info.toBase64Encoding());
3261 static VSTComSmartPtr<Steinberg::MemoryStream> createMemoryStreamForState (XmlElement& head, StringRef identifier)
3263 if (
auto* state = head.getChildByName (identifier))
3267 if (mem.fromBase64Encoding (state->getAllSubText()))
3270 stream->
setSize ((TSize) mem.getSize());
3271 mem.copyTo (stream->
getData(), 0, mem.getSize());
3279 CachedParamValues cachedParamValues;
3280 VSTComSmartPtr<ParameterChanges> inputParameterChanges = addVSTComSmartPtrOwner (
new ParameterChanges);
3281 VSTComSmartPtr<ParameterChanges> outputParameterChanges = addVSTComSmartPtrOwner (
new ParameterChanges);
3282 VSTComSmartPtr<MidiEventList> midiInputs = addVSTComSmartPtrOwner (
new MidiEventList);
3283 VSTComSmartPtr<MidiEventList> midiOutputs = addVSTComSmartPtrOwner (
new MidiEventList);
3284 Vst::ProcessContext timingInfo;
3285 bool isControllerInitialised =
false, isActive =
false, lastProcessBlockCallWasBypass =
false;
3286 const bool hasMidiInput = getNumSingleDirectionBusesFor (holder->component.
get(), MediaKind::event, Direction::input) > 0,
3287 hasMidiOutput = getNumSingleDirectionBusesFor (holder->component.
get(), MediaKind::event, Direction::output) > 0;
3288 VST3Parameter* bypassParam =
nullptr;
3292 void interconnectComponentAndController()
3294 componentConnection.loadFrom (holder->component.
get());
3295 editControllerConnection.loadFrom (editController.get());
3297 if (componentConnection !=
nullptr && editControllerConnection !=
nullptr)
3299 warnOnFailure (componentConnection->connect (editControllerConnection.get()));
3300 warnOnFailure (editControllerConnection->connect (componentConnection.get()));
3304 void initialiseParameterList()
3306 AudioProcessorParameterGroup newParameterTree;
3312 groupMap[Vst::kRootUnitId] = &newParameterTree;
3314 if (unitInfo !=
nullptr)
3316 const auto numUnits = unitInfo->getUnitCount();
3318 for (
int i = 1; i < numUnits; ++i)
3321 unitInfo->getUnitInfo (i, ui);
3322 infoMap[ui.id] = std::move (ui);
3327 auto allIds = getAllParamIDs (*editController);
3328 inputParameterChanges ->initialise (allIds);
3329 outputParameterChanges->initialise (allIds);
3330 cachedParamValues = CachedParamValues { std::move (allIds) };
3333 for (
int i = 0; i < editController->getParameterCount(); ++i)
3335 auto paramInfo = getParameterInfoForIndex (i);
3336 auto* param =
new VST3Parameter (*
this,
3339 (paramInfo.flags & Vst::ParameterInfo::kCanAutomate) != 0);
3341 if ((paramInfo.flags & Vst::ParameterInfo::kIsBypass) != 0)
3342 bypassParam = param;
3344 std::function<AudioProcessorParameterGroup* (Vst::UnitID)> findOrCreateGroup;
3345 findOrCreateGroup = [&groupMap, &infoMap, &findOrCreateGroup] (Vst::UnitID groupID)
3347 auto existingGroup = groupMap.
find (groupID);
3349 if (existingGroup != groupMap.
end())
3350 return existingGroup->second;
3352 auto groupInfo = infoMap.find (groupID);
3354 if (groupInfo == infoMap.end())
3355 return groupMap[Vst::kRootUnitId];
3357 auto* group =
new AudioProcessorParameterGroup (String (groupInfo->first),
3360 groupMap[groupInfo->first] = group;
3362 auto* parentGroup = findOrCreateGroup (groupInfo->second.parentUnitId);
3368 auto* group = findOrCreateGroup (paramInfo.unitId);
3372 setHostedParameterTree (std::move (newParameterTree));
3374 idToParamMap = [
this]
3378 for (
auto* parameter : getParameters())
3380 auto* vst3Param =
static_cast<VST3Parameter*
> (parameter);
3381 result.
emplace (vst3Param->getParamID(), vst3Param);
3388 void synchroniseStates()
3392 if (holder->component->getState (&stream) == kResultTrue)
3394 setComponentStateAndResetParameters (stream);
3397 void grabInformationObjects()
3399 processor.loadFrom (holder->component.
get());
3400 unitInfo.loadFrom (holder->component.
get());
3401 programListData.loadFrom (holder->component.
get());
3402 unitData.loadFrom (holder->component.
get());
3403 editController2.loadFrom (holder->component.
get());
3404 midiMapping.loadFrom (holder->component.
get());
3405 componentHandler.loadFrom (holder->component.
get());
3406 componentHandler2.loadFrom (holder->component.
get());
3407 trackInfoListener.loadFrom (holder->component.
get());
3409 if (processor ==
nullptr) processor.loadFrom (editController.get());
3410 if (unitInfo ==
nullptr) unitInfo.loadFrom (editController.get());
3411 if (programListData ==
nullptr) programListData.loadFrom (editController.get());
3412 if (unitData ==
nullptr) unitData.loadFrom (editController.get());
3413 if (editController2 ==
nullptr) editController2.loadFrom (editController.get());
3414 if (midiMapping ==
nullptr) midiMapping.loadFrom (editController.get());
3415 if (componentHandler ==
nullptr) componentHandler.loadFrom (editController.get());
3416 if (componentHandler2 ==
nullptr) componentHandler2.loadFrom (editController.get());
3417 if (trackInfoListener ==
nullptr) trackInfoListener.loadFrom (editController.get());
3420 void setStateForAllMidiBuses (
bool newState)
3422 setStateForAllEventBuses (holder->component.
get(), newState, Direction::input);
3423 setStateForAllEventBuses (holder->component.
get(), newState, Direction::output);
3429 result.
reserve ((
size_t) getBusCount (isInput));
3431 for (
auto i = 0; i < getBusCount (isInput); ++i)
3439 setStateForAllMidiBuses (
true);
3441 Vst::ProcessSetup setup;
3442 setup.symbolicSampleSize = Vst::kSample32;
3443 setup.maxSamplesPerBlock = 1024;
3444 setup.sampleRate = 44100.0;
3445 setup.processMode = Vst::kRealtime;
3447 warnOnFailure (processor->setupProcessing (setup));
3449 inputBusMap .prepare (createChannelMappings (
true));
3450 outputBusMap.prepare (createChannelMappings (
false));
3451 setRateAndBufferSizeDetails (setup.sampleRate, (
int) setup.maxSamplesPerBlock);
3454 static AudioProcessor::BusesProperties getBusProperties (VSTComSmartPtr<Vst::IComponent>& component)
3456 AudioProcessor::BusesProperties busProperties;
3457 VSTComSmartPtr<Vst::IAudioProcessor> processor;
3458 processor.loadFrom (component.get());
3460 for (
const auto isInput : {
true,
false })
3462 const Vst::BusDirection dir = (isInput ? Vst::kInput : Vst::kOutput);
3463 const int numBuses = component->getBusCount (Vst::kAudio, dir);
3465 for (
int i = 0; i < numBuses; ++i)
3469 if (component->getBusInfo (Vst::kAudio, dir, (Steinberg::int32) i, info) != kResultOk)
3472 AudioChannelSet layout = (info.channelCount == 0 ? AudioChannelSet::disabled()
3473 : AudioChannelSet::discreteChannels (info.channelCount));
3475 Vst::SpeakerArrangement arr;
3476 if (processor !=
nullptr && processor->getBusArrangement (dir, i, arr) == kResultOk)
3477 if (
const auto set = getChannelSetForSpeakerArrangement (arr))
3480 busProperties.addBus (isInput, toString (info.name), layout,
3481 (info.flags & Vst::BusInfo::kDefaultActive) != 0);
3485 return busProperties;
3489 Vst::BusInfo getBusInfo (MediaKind kind, Direction direction,
int index = 0)
const
3491 Vst::BusInfo busInfo;
3492 busInfo.mediaType = toVstType (kind);
3493 busInfo.direction = toVstType (direction);
3494 busInfo.channelCount = 0;
3496 holder->component->getBusInfo (busInfo.mediaType, busInfo.direction,
3497 (Steinberg::int32) index, busInfo);
3502 void updateBypass (
bool processBlockBypassedCalled)
3508 if (processBlockBypassedCalled)
3510 if (bypassParam !=
nullptr && (approximatelyEqual (bypassParam->getValue(), 0.0f) || ! lastProcessBlockCallWasBypass))
3511 bypassParam->setValue (1.0f);
3515 if (lastProcessBlockCallWasBypass && bypassParam !=
nullptr)
3516 bypassParam->setValue (0.0f);
3520 lastProcessBlockCallWasBypass = processBlockBypassedCalled;
3525 IPlugView* tryCreatingView()
const
3529 IPlugView* v = editController->createView (Vst::ViewType::kEditor);
3531 if (v ==
nullptr) v = editController->createView (
nullptr);
3532 if (v ==
nullptr) editController->queryInterface (IPlugView::iid, (
void**) &v);
3538 template <
typename FloatType>
3539 void associateWith (Vst::ProcessData& destination, AudioBuffer<FloatType>& buffer)
3541 destination.inputs = inputBusMap .getVst3LayoutForJuceBuffer (buffer);
3542 destination.outputs = outputBusMap.getVst3LayoutForJuceBuffer (buffer);
3545 void associateWith (Vst::ProcessData& destination, MidiBuffer& midiBuffer)
3547 midiInputs->clear();
3548 midiOutputs->clear();
3552 MidiEventList::hostToPluginEventList (*midiInputs,
3555 [
this] (
const auto controlID,
const auto paramValue)
3557 if (
auto* param = this->getParameterForID (controlID))
3558 param->setValueNotifyingHost ((
float) paramValue);
3562 destination.inputEvents = midiInputs.get();
3563 destination.outputEvents = midiOutputs.get();
3566 void updateTimingInformation (Vst::ProcessData& destination,
double processSampleRate)
3568 toProcessContext (timingInfo, getPlayHead(), processSampleRate);
3569 destination.processContext = &timingInfo;
3572 Vst::ParameterInfo getParameterInfoForIndex (Steinberg::int32 index)
const
3574 Vst::ParameterInfo paramInfo{};
3576 if (editController !=
nullptr)
3577 editController->getParameterInfo ((int32) index, paramInfo);
3582 Vst::ProgramListInfo getProgramListInfo (
int index)
const
3584 Vst::ProgramListInfo paramInfo{};
3586 if (unitInfo !=
nullptr)
3587 unitInfo->getProgramListInfo (index, paramInfo);
3592 void syncProgramNames()
3594 programNames.clear();
3596 if (processor ==
nullptr || editController ==
nullptr)
3599 Vst::UnitID programUnitID;
3600 Vst::ParameterInfo paramInfo{};
3603 int idx, num = editController->getParameterCount();
3605 for (idx = 0; idx < num; ++idx)
3606 if (editController->getParameterInfo (idx, paramInfo) == kResultOk
3613 programParameterID = paramInfo.id;
3614 programUnitID = paramInfo.unitId;
3617 if (unitInfo !=
nullptr)
3619 Vst::UnitInfo uInfo{};
3620 const int unitCount = unitInfo->getUnitCount();
3622 for (
int idx = 0; idx < unitCount; ++idx)
3624 if (unitInfo->getUnitInfo (idx, uInfo) == kResultOk
3625 && uInfo.id == programUnitID)
3627 const int programListCount = unitInfo->getProgramListCount();
3629 for (
int j = 0; j < programListCount; ++j)
3631 Vst::ProgramListInfo programListInfo{};
3633 if (unitInfo->getProgramListInfo (j, programListInfo) == kResultOk
3634 && programListInfo.id == uInfo.programListId)
3636 Vst::String128 name;
3638 for (
int k = 0; k < programListInfo.programCount; ++k)
3639 if (unitInfo->getProgramName (programListInfo.id, k, name) == kResultOk)
3640 programNames.add (toString (name));
3651 if (editController !=
nullptr && paramInfo.stepCount > 0)
3653 auto numPrograms = paramInfo.stepCount + 1;
3655 for (
int i = 0; i < numPrograms; ++i)
3657 auto valueNormalized =
static_cast<Vst::ParamValue
> (i) /
static_cast<Vst::ParamValue
> (paramInfo.stepCount);
3659 Vst::String128 programName;
3660 if (editController->getParamStringByValue (paramInfo.id, valueNormalized, programName) == kResultOk)
3661 programNames.add (
toString (programName));
3669JUCE_END_IGNORE_WARNINGS_MSVC
3672tresult VST3HostContext::beginEdit (Vst::ParamID paramID)
3674 if (plugin ==
nullptr)
3677 if (
auto* param = plugin->getParameterForID (paramID))
3679 param->beginChangeGesture();
3683 return kResultFalse;
3686tresult VST3HostContext::performEdit (Vst::ParamID paramID, Vst::ParamValue valueNormalised)
3688 if (plugin ==
nullptr)
3691 if (
auto* param = plugin->getParameterForID (paramID))
3693 param->setValueNotifyingHost ((
float) valueNormalised);
3696 if (! approximatelyEqual (plugin->editController->getParamNormalized (paramID), valueNormalised))
3697 return plugin->editController->setParamNormalized (paramID, valueNormalised);
3702 return kResultFalse;
3705tresult VST3HostContext::endEdit (Vst::ParamID paramID)
3707 if (plugin ==
nullptr)
3710 if (
auto* param = plugin->getParameterForID (paramID))
3712 param->endChangeGesture();
3716 return kResultFalse;
3719tresult VST3HostContext::restartComponent (Steinberg::int32 flags)
3726 componentRestarter.restart (flags);
3730tresult PLUGIN_API VST3HostContext::setDirty (TBool needsSave)
3733 plugin->updateHostDisplay (AudioPluginInstance::ChangeDetails{}.withNonParameterStateChanged (
true));
3738void VST3HostContext::restartComponentOnMessageThread (int32 flags)
3740 if (plugin ==
nullptr)
3746 if (hasFlag (flags, Vst::kReloadComponent))
3749 if (hasFlag (flags, Vst::kIoChanged))
3751 auto sampleRate = plugin->getSampleRate();
3752 auto blockSize = plugin->getBlockSize();
3755 plugin->releaseResources();
3756 plugin->prepareToPlay (sampleRate >= 8000 ? sampleRate : 44100.0,
3757 blockSize > 0 ? blockSize : 1024);
3760 if (hasFlag (flags, Vst::kLatencyChanged))
3761 if (plugin->processor !=
nullptr)
3762 plugin->setLatencySamples (jmax (0, (
int) plugin->processor->getLatencySamples()));
3764 if (hasFlag (flags, Vst::kMidiCCAssignmentChanged))
3765 plugin->updateMidiMappings();
3767 if (hasFlag (flags, Vst::kParamValuesChanged))
3768 plugin->resetParameters();
3770 plugin->updateHostDisplay (AudioProcessorListener::ChangeDetails().withProgramChanged (
true)
3771 .withParameterInfoChanged (
true));
3777 Array<const Item*> subItemStack;
3778 OwnedArray<PopupMenu> menuStack;
3779 PopupMenu* topLevelMenu = menuStack.add (
new PopupMenu());
3781 for (
int i = 0; i < items.size(); ++i)
3783 auto& item = items.getReference (i).item;
3784 auto* menuToUse = menuStack.getLast();
3786 if (hasFlag (item.flags, Item::kIsGroupStart & ~Item::kIsDisabled))
3788 subItemStack.add (&item);
3789 menuStack.add (
new PopupMenu());
3791 else if (hasFlag (item.flags, Item::kIsGroupEnd))
3793 if (
auto* subItem = subItemStack.getLast())
3795 if (
auto* m = menuStack [menuStack.size() - 2])
3796 m->addSubMenu (toString (subItem->name), *menuToUse,
3797 ! hasFlag (subItem->flags, Item::kIsDisabled),
3799 hasFlag (subItem->flags, Item::kIsChecked));
3801 menuStack.removeLast (1);
3802 subItemStack.removeLast (1);
3805 else if (hasFlag (item.flags, Item::kIsSeparator))
3807 menuToUse->addSeparator();
3811 menuToUse->addItem (item.tag != 0 ? (
int) item.tag : (
int) zeroTagReplacement,
3813 ! hasFlag (item.flags, Item::kIsDisabled),
3814 hasFlag (item.flags, Item::kIsChecked));
3818 PopupMenu::Options options;
3820 if (
auto* ed = owner.getActiveEditor())
3822 #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
3823 if (
auto* peer = ed->getPeer())
3825 auto scale = peer->getPlatformScaleFactor();
3832 options = options.withTargetScreenArea (ed->getScreenBounds().translated ((
int) x, (
int) y).withSize (1, 1));
3835 #if JUCE_MODAL_LOOPS_PERMITTED
3837 handleResult (topLevelMenu->showMenu (options));
3839 topLevelMenu->showMenuAsync (options, ModalCallbackFunction::create (menuFinished, addVSTComSmartPtrOwner (
this)));
3846tresult VST3HostContext::notifyProgramListChange (Vst::ProgramListID, Steinberg::int32)
3848 if (plugin !=
nullptr)
3849 plugin->syncProgramNames();
3856VST3PluginFormat::VST3PluginFormat() =
default;
3857VST3PluginFormat::~VST3PluginFormat() =
default;
3859bool VST3PluginFormat::setStateFromVSTPresetFile (AudioPluginInstance* api,
const MemoryBlock& rawData)
3861 if (
auto vst3 =
dynamic_cast<VST3PluginInstance*
> (api))
3862 return vst3->setStateFromPresetFile (rawData);
3867void VST3PluginFormat::findAllTypesForFile (OwnedArray<PluginDescription>& results,
const String& fileOrIdentifier)
3869 if (! fileMightContainThisPluginType (fileOrIdentifier))
3872 if (
const auto fast = DescriptionLister::findDescriptionsFast (File (fileOrIdentifier)); ! fast.empty())
3874 for (
const auto& d : fast)
3875 results.add (new PluginDescription (d));
3880 for (
const auto& file : getLibraryPaths (fileOrIdentifier))
3888 auto pluginFactory = addVSTComSmartPtrOwner (DLLHandleCache::getInstance()->findOrCreateHandle (file).getPluginFactory());
3890 if (pluginFactory ==
nullptr)
3893 auto host = addVSTComSmartPtrOwner (
new VST3HostContext());
3895 for (
const auto& d : DescriptionLister::findDescriptionsSlow (*host, *pluginFactory, File (file)))
3896 results.add (new PluginDescription (d));
3900void VST3PluginFormat::createARAFactoryAsync (
const PluginDescription& description, ARAFactoryCreationCallback callback)
3902 if (! description.hasARAExtension)
3905 callback ({ {},
"The provided plugin does not support ARA features" });
3908 File file (description.fileOrIdentifier);
3909 auto pluginFactory = addVSTComSmartPtrOwner (DLLHandleCache::getInstance()->findOrCreateHandle (file.getFullPathName()).getPluginFactory());
3910 const auto* pluginName = description.name.toRawUTF8();
3916 const PluginDescription& description,
3919 if (!
format.fileMightContainThisPluginType (description.fileOrIdentifier))
3922 struct ScopedWorkingDirectory
3924 ~ScopedWorkingDirectory() { previousWorkingDirectory.setAsCurrentWorkingDirectory(); }
3925 File previousWorkingDirectory = File::getCurrentWorkingDirectory();
3928 const ScopedWorkingDirectory scope;
3929 file.getParentDirectory().setAsCurrentWorkingDirectory();
3931 const VST3ModuleHandle::Ptr
module { VST3ModuleHandle::findOrCreateModule (file, description) };
3933 if (module ==
nullptr)
3936 auto holder = std::make_unique<VST3ComponentHolder> (module);
3938 if (! holder->initialise())
3941 auto instance = std::make_unique<VST3PluginInstance> (std::move (holder));
3943 if (! instance->initialise())
3949StringArray VST3PluginFormat::getLibraryPaths (
const String& fileOrIdentifier)
3952 if (! File (fileOrIdentifier).existsAsFile())
3955 recursiveFileSearch (files, fileOrIdentifier,
true);
3960 return { fileOrIdentifier };
3963void VST3PluginFormat::createPluginInstance (
const PluginDescription& description,
3964 double,
int, PluginCreationCallback callback)
3966 for (
const auto& file : getLibraryPaths (description.fileOrIdentifier))
3968 if (
auto result = createVST3Instance (*
this, description, file))
3970 callback (std::move (result), {});
3975 callback (
nullptr,
TRANS (
"Unable to load XXX plug-in file").replace (
"XXX",
"VST-3"));
3978bool VST3PluginFormat::requiresUnblockedMessageThreadDuringCreation (
const PluginDescription&)
const
3983bool VST3PluginFormat::fileMightContainThisPluginType (
const String& fileOrIdentifier)
3985 auto f = File::createFileWithoutCheckingPath (fileOrIdentifier);
3987 return f.hasFileExtension (
".vst3") && f.exists();
3990String VST3PluginFormat::getNameOfPluginFromIdentifier (
const String& fileOrIdentifier)
3992 return fileOrIdentifier;
3995bool VST3PluginFormat::pluginNeedsRescanning (
const PluginDescription& description)
3997 return File (description.fileOrIdentifier).getLastModificationTime() != description.lastFileModTime;
4000bool VST3PluginFormat::doesPluginStillExist (
const PluginDescription& description)
4002 return File (description.fileOrIdentifier).exists();
4005StringArray VST3PluginFormat::searchPathsForPlugins (
const FileSearchPath& directoriesToSearch,
const bool recursive,
bool)
4007 StringArray results;
4009 for (
int i = 0; i < directoriesToSearch.getNumPaths(); ++i)
4010 recursiveFileSearch (results, directoriesToSearch[i], recursive);
4015void VST3PluginFormat::recursiveFileSearch (StringArray& results,
const File& directory,
const bool recursive)
4017 for (
const auto& iter : RangedDirectoryIterator (directory, false,
"*", File::findFilesAndDirectories))
4019 auto f = iter.getFile();
4020 bool isPlugin =
false;
4022 if (fileMightContainThisPluginType (f.getFullPathName()))
4025 results.add (f.getFullPathName());
4028 if (recursive && (! isPlugin) && f.isDirectory())
4029 recursiveFileSearch (results, f,
true);
4033FileSearchPath VST3PluginFormat::getDefaultLocationsToSearch()
4036 const auto localAppData = File::getSpecialLocation (File::windowsLocalAppData) .getFullPathName();
4037 const auto programFiles = File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName();
4038 return FileSearchPath (localAppData +
"\\Programs\\Common\\VST3;" + programFiles +
"\\Common Files\\VST3");
4040 return FileSearchPath (
"~/Library/Audio/Plug-Ins/VST3;/Library/Audio/Plug-Ins/VST3");
4042 return FileSearchPath (
"~/.vst3/;/usr/lib/vst3/;/usr/local/lib/vst3/");
@ kIBSeekSet
set absolute seek position
Plug-in view content scale support.
Class factory that any plug-in defines for creating class instances: IPluginFactory.
Memory based Stream for IBStream implementation (using malloc).
char * getData() const
returns the memory pointer
tresult PLUGIN_API seek(int64 pos, int32 mode, int64 *result) SMTG_OVERRIDE
Sets stream read-write position.
void setSize(TSize size)
set the memory size, a realloc will occur if memory already used
TSize getSize() const
returns the current memory size
Component base interface: Vst::IComponent.
static bool loadPreset(IBStream *stream, const FUID &classID, IComponent *component, IEditController *editController=nullptr, std::vector< FUID > *otherClassIDArray=nullptr)
Shortcut helper to load preset with component/controller state.
static bool savePreset(IBStream *stream, const FUID &classID, IComponent *component, IEditController *editController=nullptr, const char *xmlBuffer=nullptr, int32 xmlSize=-1)
Shortcut helper to create preset from component/controller state.
String getFileNameWithoutExtension() const
Returns the last part of the filename, without its file extension.
@ currentApplicationFile
Returns this application's location.
static File JUCE_CALLTYPE getSpecialLocation(const SpecialLocationType type)
Finds the location of a special type of file or directory, such as a home folder or documents folder.
static MessageManager * getInstance()
Returns the global instance of the MessageManager.
static String fromCFString(CFStringRef cfString)
OSX ONLY - Creates a String from an OSX CFString.
static Time JUCE_CALLTYPE getCurrentTime() noexcept
Returns a Time object that is set to the current system time.
T emplace_back(T... args)
char TUID[16]
plain UID type
#define kVstAudioEffectClass
Class Category Name for Audio Processor Component.
#define kVstComponentControllerClass
Class Category Name for Controller Component.
#define JUCE_BEGIN_NO_SANITIZE(warnings)
Disable sanitizers for a range of functions.
void unregisterFdCallback(int fd)
Unregisters a previously registered file descriptor.
#define TRANS(stringLiteral)
Uses the LocalisedStrings class to translate the given string literal.
#define JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
This macro is used to catch unsafe use of functions which expect to only be called on the message thr...
#define JUCE_ASSERT_MESSAGE_THREAD
This macro is used to catch unsafe use of functions which expect to only be called on the message thr...
auto & get(ProcessorChain< Processors... > &chain) noexcept
Non-member equivalent of ProcessorChain::get which avoids awkward member template syntax.
#define JUCE_IMPLEMENT_SINGLETON(Classname)
This is a counterpart to the JUCE_DECLARE_SINGLETON macros.
#define JUCE_DECLARE_SINGLETON(Classname, doNotRecreateAfterDeletion)
Macro to generate the appropriate methods and boilerplate for a singleton class.
std::optional< ModuleInfo > parseJson(std::string_view jsonData, std::ostream *optErrorOutput)
parse a json formatted string to a ModuleInfo struct
uint32 ParamID
parameter identifier
@ kRealtime
realtime processing
@ kOffline
offline processing
uint64 SpeakerArrangement
Bitset of speakers.
@ kSample32
32-bit precision
@ kSample64
64-bit precision
void zerostruct(Type &structure) noexcept
Overwrites a structure or object with zeros.
constexpr auto enumerate(Range &&range, Index startingValue={})
Given a range and an optional starting offset, returns an IteratorPair that holds EnumerateIterators ...
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
RangedDirectoryIterator end(const RangedDirectoryIterator &)
Returns a default-constructed sentinel value.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
signed int int32
A platform-independent 32-bit signed integer type.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
void createARAFactoryAsync(AudioPluginInstance &instance, std::function< void(ARAFactoryWrapper)> cb)
Calls the provided callback with an ARAFactoryWrapper object obtained from the provided AudioPluginIn...
unsigned int uint32
A platform-independent 32-bit unsigned integer type.
int roundToInt(const FloatType value) noexcept
Fast floating-point-to-integer conversion.
constexpr int numElementsInArray(Type(&)[N]) noexcept
Handy function for getting the number of elements in a simple const C array.
std::u16string toString(NumberT value)
convert an number to an UTF-16 string
@ kIsProgramChange
parameter is a program change (unitId gives info about associated unit