JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_VSTPluginFormat.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_VST
27
28//==============================================================================
29#undef PRAGMA_ALIGN_SUPPORTED
30
31
32#if ! JUCE_MINGW && ! JUCE_MSVC
33 #define __cdecl
34#endif
35
36JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wzero-as-null-pointer-constant")
37JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
38
39#define VST_FORCE_DEPRECATED 0
40#define JUCE_VSTINTERFACE_H_INCLUDED 1
41
42namespace Vst2
43{
44struct AEffect;
45
46// If the following files cannot be found then you are probably trying to host
47// VST2 plug-ins. To do this you must have a VST2 SDK in your header search
48// paths or use the "VST (Legacy) SDK Folder" field in the Projucer. The VST2
49// SDK can be obtained from the vstsdk3610_11_06_2018_build_37 (or older) VST3
50// SDK or JUCE version 5.3.2.
51#include <pluginterfaces/vst2.x/aeffect.h>
52#include <pluginterfaces/vst2.x/aeffectx.h>
53}
54
55#include "juce_VSTCommon.h"
56
57JUCE_END_IGNORE_WARNINGS_GCC_LIKE
58JUCE_END_IGNORE_WARNINGS_MSVC
59
60JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
61JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4355)
62
63#include "juce_VSTMidiEventList.h"
64
65#if JUCE_MINGW
66 #ifndef WM_APPCOMMAND
67 #define WM_APPCOMMAND 0x0319
68 #endif
69#elif ! JUCE_WINDOWS
70 static void _fpreset() {}
71 static void _clearfp() {}
72#endif
73
74#ifndef JUCE_VST_WRAPPER_LOAD_CUSTOM_MAIN
75 #define JUCE_VST_WRAPPER_LOAD_CUSTOM_MAIN
76#endif
77
78#ifndef JUCE_VST_WRAPPER_INVOKE_MAIN
79#define JUCE_VST_WRAPPER_INVOKE_MAIN effect = module->moduleMain ((Vst2::audioMasterCallback) &audioMaster);
80#endif
81
82#ifndef JUCE_VST_FALLBACK_HOST_NAME
83 #define JUCE_VST_FALLBACK_HOST_NAME "Juce VST Host"
84#endif
85
86//==============================================================================
87namespace juce
88{
89
90//==============================================================================
91namespace
92{
93 const int fxbVersionNum = 1;
94
95 struct fxProgram
96 {
97 int32 chunkMagic; // 'CcnK'
98 int32 byteSize; // of this chunk, excl. magic + byteSize
99 int32 fxMagic; // 'FxCk'
100 int32 version;
101 int32 fxID; // fx unique id
104 char prgName[28];
105 float params[1]; // variable no. of parameters
106 };
107
108 struct fxSet
109 {
110 int32 chunkMagic; // 'CcnK'
111 int32 byteSize; // of this chunk, excl. magic + byteSize
112 int32 fxMagic; // 'FxBk'
113 int32 version;
114 int32 fxID; // fx unique id
117 char future[128];
118 fxProgram programs[1]; // variable no. of programs
119 };
120
121 struct fxChunkSet
122 {
123 int32 chunkMagic; // 'CcnK'
124 int32 byteSize; // of this chunk, excl. magic + byteSize
125 int32 fxMagic; // 'FxCh', 'FPCh', or 'FBCh'
126 int32 version;
127 int32 fxID; // fx unique id
130 char future[128];
131 int32 chunkSize;
132 char chunk[8]; // variable
133 };
134
135 struct fxProgramSet
136 {
137 int32 chunkMagic; // 'CcnK'
138 int32 byteSize; // of this chunk, excl. magic + byteSize
139 int32 fxMagic; // 'FxCh', 'FPCh', or 'FBCh'
140 int32 version;
141 int32 fxID; // fx unique id
144 char name[28];
145 int32 chunkSize;
146 char chunk[8]; // variable
147 };
148
149 // Compares a magic value in either endianness.
150 static bool compareMagic (int32 magic, const char* name) noexcept
151 {
152 return magic == (int32) ByteOrder::littleEndianInt (name)
153 || magic == (int32) ByteOrder::bigEndianInt (name);
154 }
155
156 static int32 fxbName (const char* name) noexcept { return (int32) ByteOrder::littleEndianInt (name); }
157 static int32 fxbSwap (int32 x) noexcept { return (int32) ByteOrder::swapIfLittleEndian ((uint32) x); }
158
159 static float fxbSwapFloat (const float x) noexcept
160 {
161 #ifdef JUCE_LITTLE_ENDIAN
162 union { uint32 asInt; float asFloat; } n;
163 n.asFloat = x;
164 n.asInt = ByteOrder::swap (n.asInt);
165 return n.asFloat;
166 #else
167 return x;
168 #endif
169 }
170}
171
172//==============================================================================
173namespace
174{
175 static double getVSTHostTimeNanoseconds() noexcept
176 {
177 #if JUCE_WINDOWS
178 return timeGetTime() * 1000000.0;
179 #elif JUCE_LINUX || JUCE_BSD || JUCE_IOS || JUCE_ANDROID
180 timeval micro;
181 gettimeofday (&micro, nullptr);
182 return (double) micro.tv_usec * 1000.0;
183 #elif JUCE_MAC
184 UnsignedWide micro;
185 Microseconds (&micro);
186 return micro.lo * 1000.0;
187 #endif
188 }
189
190 static int shellUIDToCreate = 0;
191 static int insideVSTCallback = 0;
192
194 {
195 IdleCallRecursionPreventer() : isMessageThread (MessageManager::getInstance()->isThisTheMessageThread())
196 {
197 if (isMessageThread)
199 }
200
202 {
203 if (isMessageThread)
205 }
206
207 const bool isMessageThread;
209 };
210
211 #if JUCE_MAC
212 static bool makeFSRefFromPath (FSRef* destFSRef, const String& path)
213 {
214 return FSPathMakeRef (reinterpret_cast<const UInt8*> (path.toRawUTF8()), destFSRef, nullptr) == noErr;
215 }
216 #endif
217}
218
219//==============================================================================
220typedef Vst2::AEffect* (VSTCALLBACK *MainCall) (Vst2::audioMasterCallback);
221static pointer_sized_int VSTCALLBACK audioMaster (Vst2::AEffect*, int32, int32, pointer_sized_int, void*, float);
222
223//==============================================================================
224// Change this to disable logging of various VST activities
225#ifndef VST_LOGGING
226 #define VST_LOGGING 1
227#endif
228
229#if VST_LOGGING
230 #define JUCE_VST_LOG(a) Logger::writeToLog(a);
231#else
232 #define JUCE_VST_LOG(a)
233#endif
234
235//==============================================================================
236#if JUCE_LINUX || JUCE_BSD
237
238namespace
239{
240 using EventProcPtr = void (*)(XEvent*);
241
242 Window getChildWindow (Window windowToCheck)
243 {
244 Window rootWindow, parentWindow;
245 Window* childWindows;
246 unsigned int numChildren = 0;
247
248 X11Symbols::getInstance()->xQueryTree (XWindowSystem::getInstance()->getDisplay(),
249 windowToCheck, &rootWindow, &parentWindow, &childWindows, &numChildren);
250
251 if (numChildren > 0)
252 return childWindows [0];
253
254 return 0;
255 }
256}
257
258#endif
259
260//==============================================================================
261class VSTXMLInfo
262{
263public:
264 static VSTXMLInfo* createFor (const juce::XmlElement& xml)
265 {
266 if (xml.hasTagName ("VSTParametersStructure"))
267 return new VSTXMLInfo (xml);
268
269 if (const auto* x = xml.getChildByName ("VSTParametersStructure"))
270 return new VSTXMLInfo (*x);
271
272 return nullptr;
273 }
274
275 struct Group;
276
277 struct Base
278 {
279 Base() noexcept {}
280 virtual ~Base() {}
281
282 Group* parent = nullptr;
283 };
284
285 struct Param final : public Base
286 {
287 int paramID;
288 juce::String expr, name, label;
289 juce::StringArray shortNames;
290 juce::String type;
291 int numberOfStates;
292 float defaultValue;
293 };
294
295 struct Group final : public Base
296 {
297 juce::String name;
298 juce::OwnedArray<Base> paramTree;
299 };
300
301 struct Range
302 {
303 Range() noexcept {}
304 Range (const juce::String& s) { set (s); }
305
306 void set (const juce::String& s)
307 {
308 inclusiveLow = s.startsWithChar ('[');
309 inclusiveHigh = s.endsWithChar (']');
310
311 auto str = s.removeCharacters ("[]");
312
313 low = str.upToFirstOccurrenceOf (",", false, false).getFloatValue();
314 high = str.fromLastOccurrenceOf (",", false, false).getFloatValue();
315 }
316
317 bool contains (float f) const noexcept
318 {
319 return (inclusiveLow ? (f >= low) : (f > low))
320 && (inclusiveHigh ? (f <= high) : (f < high));
321 }
322
323 float low = 0;
324 float high = 0;
325
326 bool inclusiveLow = false;
327 bool inclusiveHigh = false;
328 };
329
330 struct Entry
331 {
332 juce::String name;
333 Range range;
334 };
335
336 struct ValueType
337 {
338 juce::String name, label;
340 };
341
342 struct Template
343 {
344 juce::String name;
346 };
347
348 const Param* getParamForID (const int paramID, const Group* const grp) const
349 {
350 for (auto item : (grp != nullptr ? grp->paramTree : paramTree))
351 {
352 if (auto param = dynamic_cast<const Param*> (item))
353 if (param->paramID == paramID)
354 return param;
355
356 if (auto group = dynamic_cast<const Group*> (item))
357 if (auto res = getParamForID (paramID, group))
358 return res;
359 }
360
361 return nullptr;
362 }
363
364 const ValueType* getValueType (const juce::String& name) const
365 {
366 for (auto v : valueTypes)
367 if (v->name == name)
368 return v;
369
370 return nullptr;
371 }
372
376
377 ValueType switchValueType;
378
379private:
380 VSTXMLInfo (const juce::XmlElement& xml)
381 {
382 switchValueType.entries.add (new Entry ({ TRANS ("Off"), Range ("[0, 0.5[") }));
383 switchValueType.entries.add (new Entry ({ TRANS ("On"), Range ("[0.5, 1]") }));
384
385 for (auto* item : xml.getChildIterator())
386 {
387 if (item->hasTagName ("Param")) parseParam (*item, nullptr, nullptr);
388 else if (item->hasTagName ("ValueType")) parseValueType (*item);
389 else if (item->hasTagName ("Template")) parseTemplate (*item);
390 else if (item->hasTagName ("Group")) parseGroup (*item, nullptr);
391 }
392 }
393
394 void parseParam (const juce::XmlElement& item, Group* group, Template* temp)
395 {
396 auto param = new Param();
397
398 if (temp != nullptr)
399 param->expr = item.getStringAttribute ("id");
400 else
401 param->paramID = item.getIntAttribute ("id");
402
403 param->name = item.getStringAttribute ("name");
404 param->label = item.getStringAttribute ("label");
405 param->type = item.getStringAttribute ("type");
406 param->numberOfStates = item.getIntAttribute ("numberOfStates");
407 param->defaultValue = (float) item.getDoubleAttribute ("defaultValue");
408
409 param->shortNames.addTokens (item.getStringAttribute ("shortName"), ",", juce::StringRef());
410 param->shortNames.trim();
411 param->shortNames.removeEmptyStrings();
412
413 if (group != nullptr)
414 {
415 group->paramTree.add (param);
416 param->parent = group;
417 }
418 else if (temp != nullptr)
419 {
420 temp->params.add (param);
421 }
422 else
423 {
424 paramTree.add (param);
425 }
426 }
427
428 void parseValueType (const juce::XmlElement& item)
429 {
430 auto vt = new ValueType();
431 valueTypes.add (vt);
432
433 vt->name = item.getStringAttribute ("name");
434 vt->label = item.getStringAttribute ("label");
435
436 int curEntry = 0;
437 const int numEntries = item.getNumChildElements();
438
439 for (auto* entryXml : item.getChildWithTagNameIterator ("Entry"))
440 {
441 auto entry = new Entry();
442 entry->name = entryXml->getStringAttribute ("name");
443
444 if (entryXml->hasAttribute ("value"))
445 {
446 entry->range.set (entryXml->getStringAttribute ("value"));
447 }
448 else
449 {
450 entry->range.low = (float) curEntry / (float) numEntries;
451 entry->range.high = (float) (curEntry + 1) / (float) numEntries;
452
453 entry->range.inclusiveLow = true;
454 entry->range.inclusiveHigh = (curEntry == numEntries - 1);
455 }
456
457 vt->entries.add (entry);
458 ++curEntry;
459 }
460 }
461
462 void parseTemplate (const juce::XmlElement& item)
463 {
464 auto temp = new Template();
465 templates.add (temp);
466 temp->name = item.getStringAttribute ("name");
467
468 for (auto* param : item.getChildIterator())
469 parseParam (*param, nullptr, temp);
470 }
471
472 void parseGroup (const juce::XmlElement& item, Group* parentGroup)
473 {
474 auto group = new Group();
475
476 if (parentGroup)
477 {
478 parentGroup->paramTree.add (group);
479 group->parent = parentGroup;
480 }
481 else
482 {
483 paramTree.add (group);
484 }
485
486 group->name = item.getStringAttribute ("name");
487
488 if (item.hasAttribute ("template"))
489 {
491 variables.addTokens (item.getStringAttribute ("values"), ";", juce::StringRef());
492 variables.trim();
493
494 for (auto temp : templates)
495 {
496 if (temp->name == item.getStringAttribute ("template"))
497 {
498 for (int i = 0; i < temp->params.size(); ++i)
499 {
500 auto param = new Param();
501 group->paramTree.add (param);
502
503 param->parent = group;
504 param->paramID = evaluate (temp->params[i]->expr, variables);
505 param->defaultValue = temp->params[i]->defaultValue;
506 param->label = temp->params[i]->label;
507 param->name = temp->params[i]->name;
508 param->numberOfStates = temp->params[i]->numberOfStates;
509 param->shortNames = temp->params[i]->shortNames;
510 param->type = temp->params[i]->type;
511 }
512 }
513 }
514 }
515 else
516 {
517 for (auto* subItem : item.getChildIterator())
518 {
519 if (subItem->hasTagName ("Param")) parseParam (*subItem, group, nullptr);
520 else if (subItem->hasTagName ("Group")) parseGroup (*subItem, group);
521 }
522 }
523 }
524
525 int evaluate (juce::String expr, const juce::StringArray& variables) const
526 {
527 juce::StringArray names;
529
530 for (auto& v : variables)
531 {
532 if (v.contains ("="))
533 {
534 names.add (v.upToFirstOccurrenceOf ("=", false, false));
535 vals.add (v.fromFirstOccurrenceOf ("=", false, false).getIntValue());
536 }
537 }
538
539 for (int i = 0; i < names.size(); ++i)
540 {
541 for (;;)
542 {
543 const int idx = expr.indexOfWholeWord (names[i]);
544 if (idx < 0)
545 break;
546
547 expr = expr.replaceSection (idx, names[i].length(), juce::String (vals[i]));
548 }
549 }
550
551 expr = expr.retainCharacters ("01234567890-+")
552 .replace ("+", " + ")
553 .replace ("-", " - ");
554
555 juce::StringArray tokens;
556 tokens.addTokens (expr, " ", juce::StringRef());
557
558 bool add = true;
559 int val = 0;
560
561 for (const auto& s : tokens)
562 {
563 if (s == "+")
564 {
565 add = true;
566 }
567 else if (s == "-")
568 {
569 add = false;
570 }
571 else
572 {
573 if (add)
574 val += s.getIntValue();
575 else
576 val -= s.getIntValue();
577 }
578 }
579
580 return val;
581 }
582};
583
584//==============================================================================
585struct ModuleHandle final : public ReferenceCountedObject
586{
587 File file;
589 String pluginName;
591
593
595 {
597 return activeModules;
598 }
599
600 //==============================================================================
601 static Ptr findOrCreateModule (const File& file)
602 {
603 for (auto* module : getActiveModules())
604 if (module->file == file)
606
609 _fpreset();
610
611 JUCE_VST_LOG ("Attempting to load VST: " + file.getFullPathName());
612
613 Ptr m = new ModuleHandle (file, nullptr);
614
615 if (m->open())
616 {
617 _fpreset();
618 return m;
619 }
620
621 return {};
622 }
623
624 //==============================================================================
625 ModuleHandle (const File& f, MainCall customMainCall)
626 : file (f), moduleMain (customMainCall)
627 {
628 getActiveModules().add (this);
629
630 #if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD || JUCE_IOS || JUCE_ANDROID
631 fullParentDirectoryPathName = f.getParentDirectory().getFullPathName();
632 #elif JUCE_MAC
633 FSRef ref;
634 makeFSRefFromPath (&ref, f.getParentDirectory().getFullPathName());
635 FSGetCatalogInfo (&ref, kFSCatInfoNone, nullptr, nullptr, &parentDirFSSpec, nullptr);
636 #endif
637 }
638
640 {
641 getActiveModules().removeFirstMatchingValue (this);
642 close();
643 }
644
645 //==============================================================================
646 #if ! JUCE_MAC
648 #endif
649
650 #if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD || JUCE_ANDROID
651 DynamicLibrary module;
652
653 bool open()
654 {
655 if (moduleMain != nullptr)
656 return true;
657
658 pluginName = file.getFileNameWithoutExtension();
659
660 module.open (file.getFullPathName());
661
662 moduleMain = (MainCall) module.getFunction ("VSTPluginMain");
663
664 if (moduleMain == nullptr)
665 moduleMain = (MainCall) module.getFunction ("main");
666
668
669 if (moduleMain != nullptr)
670 {
671 vstXml = parseXML (file.withFileExtension ("vstxml"));
672
673 #if JUCE_WINDOWS
674 if (vstXml == nullptr)
675 vstXml = parseXML (getDLLResource (file, "VSTXML", 1));
676 #endif
677 }
678
679 return moduleMain != nullptr;
680 }
681
682 void close()
683 {
684 _fpreset(); // (doesn't do any harm)
685
686 module.close();
687 }
688
689 void closeEffect (Vst2::AEffect* eff)
690 {
691 eff->dispatcher (eff, Vst2::effClose, 0, 0, nullptr, 0);
692 }
693
694 #if JUCE_WINDOWS
695 static String getDLLResource (const File& dllFile, const String& type, int resID)
696 {
697 DynamicLibrary dll (dllFile.getFullPathName());
698 auto dllModule = (HMODULE) dll.getNativeHandle();
699
701 {
702 if (auto res = FindResource (dllModule, MAKEINTRESOURCE (resID), type.toWideCharPointer()))
703 {
704 if (auto hGlob = LoadResource (dllModule, res))
705 {
706 auto* data = static_cast<const char*> (LockResource (hGlob));
707 return String::fromUTF8 (data, (int) SizeofResource (dllModule, res));
708 }
709 }
710 }
711
712 return {};
713 }
714 #endif
715 #else
716 Handle resHandle = {};
718
719 #if JUCE_MAC
722 #endif
723
724 bool open()
725 {
726 if (moduleMain != nullptr)
727 return true;
728
729 bool ok = false;
730
731 if (file.hasFileExtension (".vst"))
732 {
733 auto* utf8 = file.getFullPathName().toRawUTF8();
734
736 (CFIndex) strlen (utf8), file.isDirectory())))
737 {
738 bundleRef.reset (CFBundleCreate (kCFAllocatorDefault, url.get()));
739
740 if (bundleRef != nullptr)
741 {
743 {
745
746 if (moduleMain == nullptr)
748
750
751 if (moduleMain != nullptr)
752 {
753 if (CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef.get(), CFSTR ("CFBundleName")))
754 {
755 if (CFGetTypeID (name) == CFStringGetTypeID())
756 {
757 char buffer[1024];
758
759 if (CFStringGetCString ((CFStringRef) name, buffer, sizeof (buffer), CFStringGetSystemEncoding()))
760 pluginName = buffer;
761 }
762 }
763
764 if (pluginName.isEmpty())
765 pluginName = file.getFileNameWithoutExtension();
766
767 #if JUCE_MAC
769 #endif
770
771 ok = true;
772
773 auto vstXmlFiles = file
774 #if JUCE_MAC
775 .getChildFile ("Contents")
776 .getChildFile ("Resources")
777 #endif
778 .findChildFiles (File::findFiles, false, "*.vstxml");
779
780 if (! vstXmlFiles.isEmpty())
781 vstXml = parseXML (vstXmlFiles.getReference (0));
782 }
783 }
784
785 if (! ok)
786 {
788 bundleRef = nullptr;
789 }
790 }
791 }
792 }
793
794 return ok;
795 }
796
797 void close()
798 {
799 if (bundleRef != nullptr)
800 {
801 #if JUCE_MAC
803 #endif
804
805 if (CFGetRetainCount (bundleRef.get()) == 1)
807
808 if (CFGetRetainCount (bundleRef.get()) > 0)
809 bundleRef = nullptr;
810 }
811 }
812
813 void closeEffect (Vst2::AEffect* eff)
814 {
815 eff->dispatcher (eff, Vst2::effClose, 0, 0, nullptr, 0);
816 }
817
818 #endif
819
820private:
822};
823
824static const int defaultVSTSampleRateValue = 44100;
825static const int defaultVSTBlockSizeValue = 512;
826
827JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
828
830{
831public:
832 template <typename T>
833 auto getArrayOfModifiableWritePointers (AudioBuffer<T>& buffer)
834 {
835 auto& pointers = getPointers (Tag<T>{});
836
837 jassert (buffer.getNumChannels() <= static_cast<int> (pointers.capacity()));
838 pointers.resize (jmax (pointers.size(), (size_t) buffer.getNumChannels()));
839
840 std::copy (buffer.getArrayOfWritePointers(),
841 buffer.getArrayOfWritePointers() + buffer.getNumChannels(),
842 pointers.begin());
843
844 return pointers.data();
845 }
846
847private:
848 template <typename> struct Tag {};
849
850 auto& getPointers (Tag<float>) { return floatPointers; }
851 auto& getPointers (Tag<double>) { return doublePointers; }
852
855};
856
857//==============================================================================
858struct VSTPluginInstance final : public AudioPluginInstance,
859 private Timer,
860 private AsyncUpdater
861{
862 struct VSTParameter final : public Parameter
863 {
864 VSTParameter (VSTPluginInstance& parent,
865 const String& paramName,
866 const Array<String>& shortParamNames,
867 float paramDefaultValue,
868 const String& paramLabel,
869 bool paramIsAutomatable,
870 bool paramIsDiscrete,
871 int numParamSteps,
872 bool isBoolSwitch,
873 const StringArray& paramValueStrings,
874 const VSTXMLInfo::ValueType* paramValueType)
875 : pluginInstance (parent),
876 name (paramName),
877 shortNames (shortParamNames),
878 defaultValue (paramDefaultValue),
879 label (paramLabel),
880 automatable (paramIsAutomatable),
881 discrete (paramIsDiscrete),
882 numSteps (numParamSteps),
883 isSwitch (isBoolSwitch),
884 vstValueStrings (paramValueStrings),
885 valueType (paramValueType)
886 {
887 }
888
889 float getValue() const override
890 {
891 if (auto* effect = pluginInstance.vstEffect)
892 {
893 const ScopedLock sl (pluginInstance.lock);
894
895 return effect->getParameter (effect, getParameterIndex());
896 }
897
898 return 0.0f;
899 }
900
901 void setValue (float newValue) override
902 {
903 if (auto* effect = pluginInstance.vstEffect)
904 {
905 const ScopedLock sl (pluginInstance.lock);
906
907 if (! approximatelyEqual (effect->getParameter (effect, getParameterIndex()), newValue))
908 effect->setParameter (effect, getParameterIndex(), newValue);
909 }
910 }
911
912 String getText (float value, int maximumStringLength) const override
913 {
914 if (valueType != nullptr)
915 {
916 for (auto& v : valueType->entries)
917 if (v->range.contains (value))
918 return v->name;
919 }
920
921 return Parameter::getText (value, maximumStringLength);
922 }
923
924 float getValueForText (const String& text) const override
925 {
926 if (valueType != nullptr)
927 {
928 for (auto& v : valueType->entries)
929 if (v->name == text)
930 return (v->range.high + v->range.low) / 2.0f;
931 }
932
933 return Parameter::getValueForText (text);
934 }
935
936 String getCurrentValueAsText() const override
937 {
938 if (valueType != nullptr || ! vstValueStrings.isEmpty())
939 return getText (getValue(), 1024);
940
941 return pluginInstance.getTextForOpcode (getParameterIndex(), Vst2::effGetParamDisplay);
942 }
943
944 float getDefaultValue() const override
945 {
946 return defaultValue;
947 }
948
949 String getName (int maximumStringLength) const override
950 {
951 if (name.isEmpty())
952 return pluginInstance.getTextForOpcode (getParameterIndex(),
953 Vst2::effGetParamName);
954
955 if (name.length() <= maximumStringLength)
956 return name;
957
958 if (! shortNames.isEmpty())
959 {
960 for (auto& n : shortNames)
961 if (n.length() <= maximumStringLength)
962 return n;
963
964 return shortNames.getLast();
965 }
966
967 return name;
968 }
969
970 String getLabel() const override
971 {
972 return label.isEmpty() ? pluginInstance.getTextForOpcode (getParameterIndex(),
973 Vst2::effGetParamLabel)
974 : label;
975 }
976
977 bool isAutomatable() const override
978 {
979 return automatable;
980 }
981
982 bool isDiscrete() const override
983 {
984 return discrete;
985 }
986
987 bool isBoolean() const override
988 {
989 return isSwitch;
990 }
991
992 int getNumSteps() const override
993 {
994 return numSteps;
995 }
996
997 StringArray getAllValueStrings() const override
998 {
999 return vstValueStrings;
1000 }
1001
1002 String getParameterID() const override
1003 {
1004 return String (getParameterIndex());
1005 }
1006
1007 VSTPluginInstance& pluginInstance;
1008
1009 const String name;
1010 const Array<String> shortNames;
1011 const float defaultValue;
1012 const String label;
1013 const bool automatable, discrete;
1014 const int numSteps;
1015 const bool isSwitch;
1016 const StringArray vstValueStrings;
1017 const VSTXMLInfo::ValueType* const valueType;
1018 };
1019
1020 VSTPluginInstance (const ModuleHandle::Ptr& mh, const BusesProperties& ioConfig, Vst2::AEffect* effect,
1021 double sampleRateToUse, int blockSizeToUse)
1022 : AudioPluginInstance (ioConfig),
1023 vstEffect (effect),
1024 vstModule (mh),
1025 name (mh->pluginName),
1027 {
1028 jassert (vstEffect != nullptr);
1029
1030 if (auto* xml = vstModule->vstXml.get())
1031 xmlInfo.reset (VSTXMLInfo::createFor (*xml));
1032
1033 refreshParameterList();
1034
1035 vstSupportsBypass = (pluginCanDo ("bypass") > 0);
1036 setRateAndBufferSizeDetails (sampleRateToUse, blockSizeToUse);
1037 }
1038
1039 void refreshParameterList() override
1040 {
1041 AudioProcessorParameterGroup newParameterTree;
1042
1043 for (int i = 0; i < vstEffect->numParams; ++i)
1044 {
1045 String paramName;
1047 float defaultValue = 0;
1048 String label;
1049 bool isAutomatable = dispatch (Vst2::effCanBeAutomated, i, 0, nullptr, 0) != 0;
1050 bool isDiscrete = false;
1052 bool isBoolSwitch = false;
1053 StringArray parameterValueStrings;
1054 const VSTXMLInfo::ValueType* valueType = nullptr;
1055
1056 if (xmlInfo != nullptr)
1057 {
1058 if (auto* param = xmlInfo->getParamForID (i, nullptr))
1059 {
1060 paramName = param->name;
1061
1062 for (auto& n : param->shortNames)
1063 shortParamNames.add (n);
1064
1065 struct LengthComparator
1066 {
1067 static int compareElements (const juce::String& first, const juce::String& second) noexcept
1068 {
1069 return first.length() - second.length();
1070 }
1071 };
1072
1073 LengthComparator comp;
1074 shortParamNames.sort (comp);
1075
1076 defaultValue = param->defaultValue;
1077 label = param->label;
1078
1079 if (param->type == "switch")
1080 {
1081 isBoolSwitch = true;
1082 numSteps = 2;
1083 valueType = &xmlInfo->switchValueType;
1084 }
1085 else
1086 {
1087 valueType = xmlInfo->getValueType (param->type);
1088 }
1089
1090 if (param->numberOfStates >= 2)
1091 {
1092 numSteps = param->numberOfStates;
1093
1094 if (valueType != nullptr)
1095 {
1096 for (auto* entry : valueType->entries)
1097 parameterValueStrings.add (entry->name);
1098
1099 parameterValueStrings.removeEmptyStrings();
1100 }
1101 }
1102
1103 isDiscrete = (numSteps != AudioProcessor::getDefaultNumParameterSteps());
1104 }
1105 }
1106
1107 newParameterTree.addChild (std::make_unique<VSTParameter> (*this, paramName, shortParamNames, defaultValue,
1108 label, isAutomatable, isDiscrete, numSteps,
1109 isBoolSwitch, parameterValueStrings, valueType));
1110 }
1111
1112 setHostedParameterTree (std::move (newParameterTree));
1113 }
1114
1115 ~VSTPluginInstance() override
1116 {
1117 if (vstEffect != nullptr && vstEffect->magic == 0x56737450 /* 'VstP' */)
1118 callOnMessageThread ([this] { cleanup(); });
1119 }
1120
1121 void cleanup()
1122 {
1123 if (vstEffect != nullptr && vstEffect->magic == 0x56737450 /* 'VstP' */)
1124 {
1125 #if JUCE_MAC
1126 if (vstModule->resFileId != 0)
1127 UseResFile (vstModule->resFileId);
1128 #endif
1129
1130 // Must delete any editors before deleting the plugin instance!
1131 jassert (getActiveEditor() == nullptr);
1132
1133 _fpreset(); // some dodgy plug-ins mess around with this
1134
1135 vstModule->closeEffect (vstEffect);
1136 }
1137
1138 vstModule = nullptr;
1139 vstEffect = nullptr;
1140 }
1141
1142 static VSTPluginInstance* create (const ModuleHandle::Ptr& newModule,
1143 double initialSampleRate,
1144 int initialBlockSize)
1145 {
1146 if (auto* newEffect = constructEffect (newModule))
1147 {
1148 newEffect->resvd2 = 0;
1149
1150 newEffect->dispatcher (newEffect, Vst2::effIdentify, 0, 0, nullptr, 0);
1151
1152 auto blockSize = jmax (32, initialBlockSize);
1153
1154 newEffect->dispatcher (newEffect, Vst2::effSetSampleRate, 0, 0, nullptr, static_cast<float> (initialSampleRate));
1155 newEffect->dispatcher (newEffect, Vst2::effSetBlockSize, 0, blockSize, nullptr, 0);
1156
1157 newEffect->dispatcher (newEffect, Vst2::effOpen, 0, 0, nullptr, 0);
1158 BusesProperties ioConfig = queryBusIO (newEffect);
1159
1161 }
1162
1163 return nullptr;
1164 }
1165
1166 //==============================================================================
1167 void fillInPluginDescription (PluginDescription& desc) const override
1168 {
1169 desc.name = name;
1170
1171 {
1172 char buffer[512] = { 0 };
1173 dispatch (Vst2::effGetEffectName, 0, 0, buffer, 0);
1174
1175 desc.descriptiveName = String::createStringFromData (buffer, (int) sizeof (buffer)).trim();
1176
1177 if (desc.descriptiveName.isEmpty())
1178 desc.descriptiveName = name;
1179 }
1180
1181 desc.fileOrIdentifier = vstModule->file.getFullPathName();
1182 desc.uniqueId = desc.deprecatedUid = getUID();
1183 desc.lastFileModTime = vstModule->file.getLastModificationTime();
1184 desc.lastInfoUpdateTime = Time::getCurrentTime();
1185 desc.pluginFormatName = "VST";
1186 desc.category = getCategory();
1187
1188 {
1189 char buffer[512] = { 0 };
1190 dispatch (Vst2::effGetVendorString, 0, 0, buffer, 0);
1191 desc.manufacturerName = String::createStringFromData (buffer, (int) sizeof (buffer)).trim();
1192 }
1193
1194 desc.version = getVersion();
1195 desc.numInputChannels = getTotalNumInputChannels();
1196 desc.numOutputChannels = getTotalNumOutputChannels();
1197 desc.isInstrument = isSynthPlugin();
1198 }
1199
1201 {
1202 if (vstEffect != nullptr)
1203 {
1205 initialise (initialSampleRate, initialBlockSize);
1206 return true;
1207 }
1208
1209 return false;
1210 }
1211
1212 void initialise (double initialSampleRate, int initialBlockSize)
1213 {
1214 if (initialised || vstEffect == nullptr)
1215 return;
1216
1217 #if JUCE_WINDOWS
1218 // On Windows it's highly advisable to create your plugins using the message thread,
1219 // because many plugins need a chance to create HWNDs that will get their
1220 // messages delivered by the main message thread, and that's not possible from
1221 // a background thread.
1223 #endif
1224
1225 JUCE_VST_LOG ("Initialising VST: " + vstModule->pluginName + " (" + getVersion() + ")");
1226 initialised = true;
1227
1228 setRateAndBufferSizeDetails (initialSampleRate, initialBlockSize);
1229
1230 dispatch (Vst2::effIdentify, 0, 0, nullptr, 0);
1231
1232 if (getSampleRate() > 0)
1233 dispatch (Vst2::effSetSampleRate, 0, 0, nullptr, (float) getSampleRate());
1234
1235 if (getBlockSize() > 0)
1236 dispatch (Vst2::effSetBlockSize, 0, jmax (32, getBlockSize()), nullptr, 0);
1237
1238 dispatch (Vst2::effOpen, 0, 0, nullptr, 0);
1239
1240 setRateAndBufferSizeDetails (getSampleRate(), getBlockSize());
1241
1242 if (getNumPrograms() > 1)
1243 setCurrentProgram (0);
1244 else
1245 dispatch (Vst2::effSetProgram, 0, 0, nullptr, 0);
1246
1247 for (int i = vstEffect->numInputs; --i >= 0;) dispatch (Vst2::effConnectInput, i, 1, nullptr, 0);
1248 for (int i = vstEffect->numOutputs; --i >= 0;) dispatch (Vst2::effConnectOutput, i, 1, nullptr, 0);
1249
1250 if (getVstCategory() != Vst2::kPlugCategShell) // (workaround for Waves 5 plugins which crash during this call)
1252
1253 wantsMidiMessages = pluginCanDo ("receiveVstMidiEvent") > 0 || isSynthPlugin();
1254
1255 setLatencySamples (vstEffect->initialDelay);
1256 }
1257
1258 void getExtensions (ExtensionsVisitor& visitor) const override
1259 {
1260 struct Extensions final : public ExtensionsVisitor::VSTClient
1261 {
1262 explicit Extensions (const VSTPluginInstance* instanceIn) : instance (instanceIn) {}
1263
1264 AEffect* getAEffectPtr() const noexcept override { return reinterpret_cast<AEffect*> (instance->vstEffect); }
1265
1266 const VSTPluginInstance* instance = nullptr;
1267 };
1268
1269 visitor.visitVSTClient (Extensions { this });
1270 }
1271
1272 void* getPlatformSpecificData() override { return vstEffect; }
1273
1274 const String getName() const override
1275 {
1276 if (vstEffect != nullptr)
1277 {
1278 char buffer[512] = { 0 };
1279
1280 if (dispatch (Vst2::effGetProductString, 0, 0, buffer, 0) != 0)
1281 {
1282 String productName = String::createStringFromData (buffer, (int) sizeof (buffer));
1283
1284 if (productName.isNotEmpty())
1285 return productName;
1286 }
1287 }
1288
1289 return name;
1290 }
1291
1292 int getUID() const
1293 {
1294 int uid = vstEffect != nullptr ? vstEffect->uniqueID : 0;
1295
1296 if (uid == 0)
1297 uid = vstModule->file.hashCode();
1298
1299 return uid;
1300 }
1301
1302 double getTailLengthSeconds() const override
1303 {
1304 if (vstEffect == nullptr)
1305 return 0.0;
1306
1307 if ((vstEffect->flags & Vst2::effFlagsNoSoundInStop) != 0)
1308 return 0.0;
1309
1310 auto tailSize = dispatch (Vst2::effGetTailSize, 0, 0, nullptr, 0);
1311 auto sampleRate = getSampleRate();
1312
1313 // remain backward compatible with old JUCE plug-ins: anything larger
1314 // than INT32_MAX is an invalid tail time but old JUCE 64-bit plug-ins
1315 // would return INT64_MAX for infinite tail time. So treat anything
1316 // equal or greater than INT32_MAX as infinite tail time.
1319
1320 if (tailSize >= 0 && sampleRate > 0)
1321 return static_cast<double> (tailSize) / sampleRate;
1322
1323 return 0.0;
1324 }
1325
1326 bool acceptsMidi() const override { return wantsMidiMessages; }
1327 bool producesMidi() const override { return pluginCanDo ("sendVstMidiEvent") > 0; }
1328 bool supportsMPE() const override { return pluginCanDo ("MPE") > 0; }
1329
1330 Vst2::VstPlugCategory getVstCategory() const noexcept { return (Vst2::VstPlugCategory) dispatch (Vst2::effGetPlugCategory, 0, 0, nullptr, 0); }
1331
1332 bool isSynthPlugin() const { return (vstEffect != nullptr && (vstEffect->flags & Vst2::effFlagsIsSynth) != 0); }
1333
1334 int pluginCanDo (const char* text) const { return (int) dispatch (Vst2::effCanDo, 0, 0, (void*) text, 0); }
1335
1336 //==============================================================================
1337 void prepareToPlay (double rate, int samplesPerBlockExpected) override
1338 {
1339 auto numInputBuses = getBusCount (true);
1340 auto numOutputBuses = getBusCount (false);
1341
1342 setRateAndBufferSizeDetails (rate, samplesPerBlockExpected);
1343
1344 if (numInputBuses <= 1 && numOutputBuses <= 1)
1345 {
1346 SpeakerMappings::VstSpeakerConfigurationHolder inArr (getChannelLayoutOfBus (true, 0));
1347 SpeakerMappings::VstSpeakerConfigurationHolder outArr (getChannelLayoutOfBus (false, 0));
1348
1349 dispatch (Vst2::effSetSpeakerArrangement, 0, (pointer_sized_int) &inArr.get(), (void*) &outArr.get(), 0.0f);
1350 }
1351
1352 vstHostTime.tempo = 120.0;
1353 vstHostTime.timeSigNumerator = 4;
1354 vstHostTime.timeSigDenominator = 4;
1355 vstHostTime.sampleRate = rate;
1356 vstHostTime.samplePos = 0;
1357 vstHostTime.flags = Vst2::kVstNanosValid
1358 | Vst2::kVstAutomationWriting
1359 | Vst2::kVstAutomationReading;
1360
1361 initialise (rate, samplesPerBlockExpected);
1362
1363 if (initialised)
1364 {
1365 wantsMidiMessages = wantsMidiMessages || (pluginCanDo ("receiveVstMidiEvent") > 0) || isSynthPlugin();
1366
1368 midiEventsToSend.ensureSize (256);
1369 else
1370 midiEventsToSend.freeEvents();
1371
1372 incomingMidi.clear();
1373
1374 dispatch (Vst2::effSetSampleRate, 0, 0, nullptr, (float) rate);
1375 dispatch (Vst2::effSetBlockSize, 0, jmax (16, samplesPerBlockExpected), nullptr, 0);
1376
1377 if (supportsDoublePrecisionProcessing())
1378 {
1379 int32 vstPrecision = isUsingDoublePrecision() ? Vst2::kVstProcessPrecision64
1380 : Vst2::kVstProcessPrecision32;
1381
1382 dispatch (Vst2::effSetProcessPrecision, 0, (pointer_sized_int) vstPrecision, nullptr, 0);
1383 }
1384
1385 auto maxChannels = jmax (1, jmax (vstEffect->numInputs, vstEffect->numOutputs));
1386
1389
1390 channelBufferFloat .calloc (static_cast<size_t> (maxChannels));
1391 channelBufferDouble.calloc (static_cast<size_t> (maxChannels));
1392
1393 outOfPlaceBuffer.setSize (jmax (1, vstEffect->numOutputs), samplesPerBlockExpected);
1394
1395 if (! isPowerOn)
1396 setPower (true);
1397
1398 // dodgy hack to force some plugins to initialise the sample rate.
1399 if (! hasEditor())
1400 {
1401 if (auto* firstParam = getParameters()[0])
1402 {
1403 auto old = firstParam->getValue();
1404 firstParam->setValue ((old < 0.5f) ? 1.0f : 0.0f);
1405 firstParam->setValue (old);
1406 }
1407 }
1408
1409 dispatch (Vst2::effStartProcess, 0, 0, nullptr, 0);
1410
1411 setLatencySamples (vstEffect->initialDelay);
1412 }
1413 }
1414
1415 void releaseResources() override
1416 {
1417 if (initialised)
1418 {
1419 dispatch (Vst2::effStopProcess, 0, 0, nullptr, 0);
1420 setPower (false);
1421 }
1422
1423 channelBufferFloat.free();
1424 tmpBufferFloat.setSize (0, 0);
1425
1426 channelBufferDouble.free();
1427 tmpBufferDouble.setSize (0, 0);
1428
1429 outOfPlaceBuffer.setSize (1, 1);
1430 incomingMidi.clear();
1431
1432 midiEventsToSend.freeEvents();
1433 }
1434
1435 void reset() override
1436 {
1437 if (isPowerOn)
1438 {
1439 setPower (false);
1440 setPower (true);
1441 }
1442 }
1443
1444 //==============================================================================
1445 void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
1446 {
1447 jassert (! isUsingDoublePrecision());
1449 }
1450
1451 void processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages) override
1452 {
1453 jassert (isUsingDoublePrecision());
1455 }
1456
1457 void processBlockBypassed (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
1458 {
1459 jassert (! isUsingDoublePrecision());
1461 }
1462
1463 void processBlockBypassed (AudioBuffer<double>& buffer, MidiBuffer& midiMessages) override
1464 {
1465 jassert (isUsingDoublePrecision());
1467 }
1468
1469 //==============================================================================
1470 bool supportsDoublePrecisionProcessing() const override
1471 {
1472 return ((vstEffect->flags & Vst2::effFlagsCanReplacing) != 0
1473 && (vstEffect->flags & Vst2::effFlagsCanDoubleReplacing) != 0);
1474 }
1475
1476 AudioProcessorParameter* getBypassParameter() const override { return vstSupportsBypass ? bypassParam.get() : nullptr; }
1477
1478 //==============================================================================
1479 bool canAddBus (bool) const override { return false; }
1480 bool canRemoveBus (bool) const override { return false; }
1481
1482 bool isBusesLayoutSupported (const BusesLayout& layouts) const override
1483 {
1484 auto numInputBuses = getBusCount (true);
1485 auto numOutputBuses = getBusCount (false);
1486
1487 // it's not possible to change layout if there are sidechains/aux buses
1488 if (numInputBuses > 1 || numOutputBuses > 1)
1489 return (layouts == getBusesLayout());
1490
1491 return (layouts.getNumChannels (true, 0) <= vstEffect->numInputs
1492 && layouts.getNumChannels (false, 0) <= vstEffect->numOutputs);
1493 }
1494
1495 //==============================================================================
1496 #if JUCE_IOS || JUCE_ANDROID
1497 bool hasEditor() const override { return false; }
1498 #else
1499 bool hasEditor() const override { return vstEffect != nullptr && (vstEffect->flags & Vst2::effFlagsHasEditor) != 0; }
1500 #endif
1501
1502 AudioProcessorEditor* createEditor() override;
1503
1504 //==============================================================================
1505 const String getInputChannelName (int index) const override
1506 {
1507 if (isValidChannel (index, true))
1508 {
1509 Vst2::VstPinProperties pinProps;
1510 if (dispatch (Vst2::effGetInputProperties, index, 0, &pinProps, 0.0f) != 0)
1511 return String (pinProps.label, sizeof (pinProps.label));
1512 }
1513
1514 return {};
1515 }
1516
1517 bool isInputChannelStereoPair (int index) const override
1518 {
1519 if (! isValidChannel (index, true))
1520 return false;
1521
1522 Vst2::VstPinProperties pinProps;
1523 if (dispatch (Vst2::effGetInputProperties, index, 0, &pinProps, 0.0f) != 0)
1524 return (pinProps.flags & Vst2::kVstPinIsStereo) != 0;
1525
1526 return true;
1527 }
1528
1529 const String getOutputChannelName (int index) const override
1530 {
1531 if (isValidChannel (index, false))
1532 {
1533 Vst2::VstPinProperties pinProps;
1534 if (dispatch (Vst2::effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0)
1535 return String (pinProps.label, sizeof (pinProps.label));
1536 }
1537
1538 return {};
1539 }
1540
1541 bool isOutputChannelStereoPair (int index) const override
1542 {
1543 if (! isValidChannel (index, false))
1544 return false;
1545
1546 Vst2::VstPinProperties pinProps;
1547 if (dispatch (Vst2::effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0)
1548 return (pinProps.flags & Vst2::kVstPinIsStereo) != 0;
1549
1550 return true;
1551 }
1552
1553 bool isValidChannel (int index, bool isInput) const noexcept
1554 {
1555 return isPositiveAndBelow (index, isInput ? getTotalNumInputChannels()
1556 : getTotalNumOutputChannels());
1557 }
1558
1559 //==============================================================================
1560 int getNumPrograms() override { return vstEffect != nullptr ? jmax (0, vstEffect->numPrograms) : 0; }
1561
1562 // NB: some plugs return negative numbers from this function.
1563 int getCurrentProgram() override { return (int) dispatch (Vst2::effGetProgram, 0, 0, nullptr, 0); }
1564
1565 void setCurrentProgram (int newIndex) override
1566 {
1567 if (getNumPrograms() > 0 && newIndex != getCurrentProgram())
1568 dispatch (Vst2::effSetProgram, 0, jlimit (0, getNumPrograms() - 1, newIndex), nullptr, 0);
1569 }
1570
1571 const String getProgramName (int index) override
1572 {
1573 if (index >= 0)
1574 {
1575 if (index == getCurrentProgram())
1576 return getCurrentProgramName();
1577
1578 if (vstEffect != nullptr)
1579 {
1580 char nm[264] = { 0 };
1581
1582 if (dispatch (Vst2::effGetProgramNameIndexed, jlimit (0, getNumPrograms() - 1, index), -1, nm, 0) != 0)
1583 return String::fromUTF8 (nm).trim();
1584 }
1585 }
1586
1587 return {};
1588 }
1589
1590 void changeProgramName (int index, const String& newName) override
1591 {
1592 if (index >= 0 && index == getCurrentProgram())
1593 {
1594 if (getNumPrograms() > 0 && newName != getCurrentProgramName())
1595 dispatch (Vst2::effSetProgramName, 0, 0, (void*) newName.substring (0, 24).toRawUTF8(), 0.0f);
1596 }
1597 else
1598 {
1599 jassertfalse; // xxx not implemented!
1600 }
1601 }
1602
1603 //==============================================================================
1604 void getStateInformation (MemoryBlock& mb) override { saveToFXBFile (mb, true); }
1605 void getCurrentProgramStateInformation (MemoryBlock& mb) override { saveToFXBFile (mb, false); }
1606
1607 void setStateInformation (const void* data, int size) override { loadFromFXBFile (data, (size_t) size); }
1608 void setCurrentProgramStateInformation (const void* data, int size) override { loadFromFXBFile (data, (size_t) size); }
1609
1610 //==============================================================================
1611 void timerCallback() override
1612 {
1613 if (dispatch (Vst2::effIdle, 0, 0, nullptr, 0) == 0)
1614 stopTimer();
1615 }
1616
1617 void handleAsyncUpdate() override
1618 {
1619 updateHostDisplay (AudioProcessorListener::ChangeDetails().withProgramChanged (true)
1620 .withParameterInfoChanged (true));
1621 }
1622
1623 pointer_sized_int handleCallback (int32 opcode, int32 index, pointer_sized_int value, void* ptr, float opt)
1624 {
1625 switch (opcode)
1626 {
1627 case Vst2::audioMasterAutomate:
1628 if (auto* param = getParameters()[index])
1629 param->sendValueChangedMessageToListeners (opt);
1630 else
1631 jassertfalse; // Invalid parameter index!
1632
1633 break;
1634
1635 case Vst2::audioMasterProcessEvents: handleMidiFromPlugin ((const Vst2::VstEvents*) ptr); break;
1636 case Vst2::audioMasterGetTime: return getVSTTime();
1637 case Vst2::audioMasterIdle: handleIdle(); break;
1638 case Vst2::audioMasterSizeWindow: setWindowSize (index, (int) value); return 1;
1639 case Vst2::audioMasterUpdateDisplay: triggerAsyncUpdate(); break;
1640 case Vst2::audioMasterIOChanged: setLatencySamples (vstEffect->initialDelay); break;
1641 case Vst2::audioMasterNeedIdle: startTimer (50); break;
1642
1643 case Vst2::audioMasterGetSampleRate: return (pointer_sized_int) (getSampleRate() > 0 ? getSampleRate() : defaultVSTSampleRateValue);
1644 case Vst2::audioMasterGetBlockSize: return (pointer_sized_int) (getBlockSize() > 0 ? getBlockSize() : defaultVSTBlockSizeValue);
1645 case Vst2::audioMasterWantMidi: wantsMidiMessages = true; break;
1646 case Vst2::audioMasterGetDirectory: return getVstDirectory();
1647
1648 case Vst2::audioMasterTempoAt: return (pointer_sized_int) (extraFunctions != nullptr ? extraFunctions->getTempoAt ((int64) value) : 0);
1649 case Vst2::audioMasterGetAutomationState: return (pointer_sized_int) (extraFunctions != nullptr ? extraFunctions->getAutomationState() : 0);
1650
1651 case Vst2::audioMasterBeginEdit:
1652 if (auto* param = getParameters()[index])
1653 param->beginChangeGesture();
1654 else
1655 jassertfalse; // Invalid parameter index!
1656
1657 break;
1658
1659 case Vst2::audioMasterEndEdit:
1660 if (auto* param = getParameters()[index])
1661 param->endChangeGesture();
1662 else
1663 jassertfalse; // Invalid parameter index!
1664
1665 break;
1666
1667 case Vst2::audioMasterPinConnected: return isValidChannel (index, value == 0) ? 0 : 1; // (yes, 0 = true)
1668 case Vst2::audioMasterGetCurrentProcessLevel: return isNonRealtime() ? 4 : 0;
1669
1670 // none of these are handled (yet)...
1671 case Vst2::audioMasterSetTime:
1672 case Vst2::audioMasterGetParameterQuantization:
1673 case Vst2::audioMasterGetInputLatency:
1674 case Vst2::audioMasterGetOutputLatency:
1675 case Vst2::audioMasterGetPreviousPlug:
1676 case Vst2::audioMasterGetNextPlug:
1677 case Vst2::audioMasterWillReplaceOrAccumulate:
1678 case Vst2::audioMasterOfflineStart:
1679 case Vst2::audioMasterOfflineRead:
1680 case Vst2::audioMasterOfflineWrite:
1681 case Vst2::audioMasterOfflineGetCurrentPass:
1682 case Vst2::audioMasterOfflineGetCurrentMetaPass:
1683 case Vst2::audioMasterGetOutputSpeakerArrangement:
1684 case Vst2::audioMasterVendorSpecific:
1685 case Vst2::audioMasterSetIcon:
1686 case Vst2::audioMasterGetLanguage:
1687 case Vst2::audioMasterOpenWindow:
1688 case Vst2::audioMasterCloseWindow:
1689 break;
1690
1691 default:
1692 return handleGeneralCallback (opcode, index, value, ptr, opt);
1693 }
1694
1695 return 0;
1696 }
1697
1698 // handles non plugin-specific callbacks.
1699 static pointer_sized_int handleGeneralCallback (int32 opcode, int32 /*index*/, pointer_sized_int /*value*/, void* ptr, float /*opt*/)
1700 {
1701 switch (opcode)
1702 {
1703 case Vst2::audioMasterCanDo: return handleCanDo ((const char*) ptr);
1704 case Vst2::audioMasterVersion: return 2400;
1705 case Vst2::audioMasterCurrentId: return shellUIDToCreate;
1706 case Vst2::audioMasterGetNumAutomatableParameters: return 0;
1707 case Vst2::audioMasterGetAutomationState: return 1;
1708 case Vst2::audioMasterGetVendorVersion: return 0x0101;
1709
1710 case Vst2::audioMasterGetVendorString:
1711 case Vst2::audioMasterGetProductString: return getHostName ((char*) ptr);
1712
1713 case Vst2::audioMasterGetSampleRate: return (pointer_sized_int) defaultVSTSampleRateValue;
1714 case Vst2::audioMasterGetBlockSize: return (pointer_sized_int) defaultVSTBlockSizeValue;
1715 case Vst2::audioMasterSetOutputSampleRate: return 0;
1716
1717 default:
1718 DBG ("*** Unhandled VST Callback: " + String ((int) opcode));
1719 break;
1720 }
1721
1722 return 0;
1723 }
1724
1725 //==============================================================================
1726 pointer_sized_int dispatch (int opcode, int index, pointer_sized_int value, void* const ptr, float opt) const
1727 {
1728 pointer_sized_int result = 0;
1729
1730 if (vstEffect != nullptr)
1731 {
1732 const ScopedLock sl (lock);
1734
1735 try
1736 {
1737 #if JUCE_MAC
1738 auto oldResFile = CurResFile();
1739
1740 if (vstModule->resFileId != 0)
1741 UseResFile (vstModule->resFileId);
1742 #endif
1743
1744 result = vstEffect->dispatcher (vstEffect, opcode, index, value, ptr, opt);
1745
1746 #if JUCE_MAC
1747 auto newResFile = CurResFile();
1748
1749 if (newResFile != oldResFile) // avoid confusing the parent app's resource file with the plug-in's
1750 {
1751 vstModule->resFileId = newResFile;
1753 }
1754 #endif
1755 }
1756 catch (...)
1757 {}
1758 }
1759
1760 return result;
1761 }
1762
1763 bool loadFromFXBFile (const void* const data, const size_t dataSize)
1764 {
1765 if (dataSize < 28)
1766 return false;
1767
1768 auto set = (const fxSet*) data;
1769
1770 if ((! compareMagic (set->chunkMagic, "CcnK")) || fxbSwap (set->version) > fxbVersionNum)
1771 return false;
1772
1773 if (compareMagic (set->fxMagic, "FxBk"))
1774 {
1775 // bank of programs
1776 if (fxbSwap (set->numPrograms) >= 0)
1777 {
1778 auto oldProg = getCurrentProgram();
1779 auto numParams = fxbSwap (((const fxProgram*) (set->programs))->numParams);
1780 auto progLen = (int) sizeof (fxProgram) + (numParams - 1) * (int) sizeof (float);
1781
1782 for (int i = 0; i < fxbSwap (set->numPrograms); ++i)
1783 {
1784 if (i != oldProg)
1785 {
1786 auto prog = addBytesToPointer (set->programs, i * progLen);
1787
1788 if (getAddressDifference (prog, set) >= (int) dataSize)
1789 return false;
1790
1791 if (fxbSwap (set->numPrograms) > 0)
1792 setCurrentProgram (i);
1793
1795 return false;
1796 }
1797 }
1798
1799 if (fxbSwap (set->numPrograms) > 0)
1800 setCurrentProgram (oldProg);
1801
1802 auto prog = addBytesToPointer (set->programs, oldProg * progLen);
1803
1804 if (getAddressDifference (prog, set) >= (int) dataSize)
1805 return false;
1806
1808 return false;
1809 }
1810 }
1811 else if (compareMagic (set->fxMagic, "FxCk"))
1812 {
1813 // single program
1814 auto prog = (const fxProgram*) data;
1815
1816 if (! compareMagic (prog->chunkMagic, "CcnK"))
1817 return false;
1818
1819 changeProgramName (getCurrentProgram(), prog->prgName);
1820
1821 for (int i = 0; i < fxbSwap (prog->numParams); ++i)
1822 if (auto* param = getParameters()[i])
1823 param->setValue (fxbSwapFloat (prog->params[i]));
1824 }
1825 else if (compareMagic (set->fxMagic, "FBCh"))
1826 {
1827 // non-preset chunk
1828 auto cset = (const fxChunkSet*) data;
1829
1830 if ((size_t) fxbSwap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (size_t) dataSize)
1831 return false;
1832
1833 setChunkData (cset->chunk, fxbSwap (cset->chunkSize), false);
1834 }
1835 else if (compareMagic (set->fxMagic, "FPCh"))
1836 {
1837 // preset chunk
1838 auto cset = (const fxProgramSet*) data;
1839
1840 if ((size_t) fxbSwap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (size_t) dataSize)
1841 return false;
1842
1843 setChunkData (cset->chunk, fxbSwap (cset->chunkSize), true);
1844
1845 changeProgramName (getCurrentProgram(), cset->name);
1846 }
1847 else
1848 {
1849 return false;
1850 }
1851
1852 return true;
1853 }
1854
1855 bool saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB = 128)
1856 {
1857 auto numPrograms = getNumPrograms();
1858 auto numParams = getParameters().size();
1859
1860 if (usesChunks())
1861 {
1862 MemoryBlock chunk;
1863 getChunkData (chunk, ! isFXB, maxSizeMB);
1864
1865 if (isFXB)
1866 {
1867 auto totalLen = sizeof (fxChunkSet) + chunk.getSize() - 8;
1868 dest.setSize (totalLen, true);
1869
1870 auto set = (fxChunkSet*) dest.getData();
1871 set->chunkMagic = fxbName ("CcnK");
1872 set->byteSize = 0;
1873 set->fxMagic = fxbName ("FBCh");
1874 set->version = fxbSwap (fxbVersionNum);
1875 set->fxID = fxbSwap (getUID());
1876 set->fxVersion = fxbSwap (getVersionNumber());
1877 set->numPrograms = fxbSwap (numPrograms);
1878 set->chunkSize = fxbSwap ((int32) chunk.getSize());
1879
1880 chunk.copyTo (set->chunk, 0, chunk.getSize());
1881 }
1882 else
1883 {
1884 auto totalLen = sizeof (fxProgramSet) + chunk.getSize() - 8;
1885 dest.setSize (totalLen, true);
1886
1887 auto set = (fxProgramSet*) dest.getData();
1888 set->chunkMagic = fxbName ("CcnK");
1889 set->byteSize = 0;
1890 set->fxMagic = fxbName ("FPCh");
1891 set->version = fxbSwap (fxbVersionNum);
1892 set->fxID = fxbSwap (getUID());
1893 set->fxVersion = fxbSwap (getVersionNumber());
1894 set->numPrograms = fxbSwap (numPrograms);
1895 set->chunkSize = fxbSwap ((int32) chunk.getSize());
1896
1897 getCurrentProgramName().copyToUTF8 (set->name, sizeof (set->name) - 1);
1898 chunk.copyTo (set->chunk, 0, chunk.getSize());
1899 }
1900 }
1901 else
1902 {
1903 if (isFXB)
1904 {
1905 auto progLen = (int) sizeof (fxProgram) + (numParams - 1) * (int) sizeof (float);
1906 auto len = (size_t) (progLen * jmax (1, numPrograms)) + (sizeof (fxSet) - sizeof (fxProgram));
1907 dest.setSize (len, true);
1908
1909 auto set = (fxSet*) dest.getData();
1910 set->chunkMagic = fxbName ("CcnK");
1911 set->byteSize = 0;
1912 set->fxMagic = fxbName ("FxBk");
1913 set->version = fxbSwap (fxbVersionNum);
1914 set->fxID = fxbSwap (getUID());
1915 set->fxVersion = fxbSwap (getVersionNumber());
1916 set->numPrograms = fxbSwap (numPrograms);
1917
1918 MemoryBlock oldSettings;
1920
1921 auto oldProgram = getCurrentProgram();
1922
1923 if (oldProgram >= 0)
1925
1926 for (int i = 0; i < numPrograms; ++i)
1927 {
1928 if (i != oldProgram)
1929 {
1930 setCurrentProgram (i);
1931 setParamsInProgramBlock (addBytesToPointer (set->programs, i * progLen));
1932 }
1933 }
1934
1935 if (oldProgram >= 0)
1936 setCurrentProgram (oldProgram);
1937
1939 }
1940 else
1941 {
1942 dest.setSize ((size_t) ((numParams - 1) * (int) sizeof (float)) + sizeof (fxProgram), true);
1943 setParamsInProgramBlock ((fxProgram*) dest.getData());
1944 }
1945 }
1946
1947 return true;
1948 }
1949
1950 bool usesChunks() const noexcept { return vstEffect != nullptr && (vstEffect->flags & Vst2::effFlagsProgramChunks) != 0; }
1951
1952 bool getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const
1953 {
1954 if (usesChunks())
1955 {
1956 void* data = nullptr;
1957 auto bytes = (size_t) dispatch (Vst2::effGetChunk, isPreset ? 1 : 0, 0, &data, 0.0f);
1958
1959 if (data != nullptr && bytes <= (size_t) maxSizeMB * 1024 * 1024)
1960 {
1961 mb.setSize (bytes);
1962 mb.copyFrom (data, 0, bytes);
1963
1964 return true;
1965 }
1966 }
1967
1968 return false;
1969 }
1970
1971 bool setChunkData (const void* data, const int size, bool isPreset)
1972 {
1973 if (size > 0 && usesChunks())
1974 {
1975 dispatch (Vst2::effSetChunk, isPreset ? 1 : 0, size, (void*) data, 0.0f);
1976
1977 if (! isPreset)
1979
1980 return true;
1981 }
1982
1983 return false;
1984 }
1985
1986 bool updateSizeFromEditor (int w, int h);
1987
1988 Vst2::AEffect* vstEffect;
1989 ModuleHandle::Ptr vstModule;
1990
1992
1993private:
1994 //==============================================================================
1995 struct VST2BypassParameter final : public Parameter
1996 {
1997 VST2BypassParameter (VSTPluginInstance& effectToUse)
1998 : parent (effectToUse),
1999 vstOnStrings (TRANS ("on"), TRANS ("yes"), TRANS ("true")),
2000 vstOffStrings (TRANS ("off"), TRANS ("no"), TRANS ("false")),
2001 values (TRANS ("Off"), TRANS ("On"))
2002 {
2003 }
2004
2005 void setValue (float newValue) override
2006 {
2007 currentValue = (! approximatelyEqual (newValue, 0.0f));
2008
2009 if (parent.vstSupportsBypass)
2010 parent.dispatch (Vst2::effSetBypass, 0, currentValue ? 1 : 0, nullptr, 0.0f);
2011 }
2012
2013 float getValueForText (const String& text) const override
2014 {
2015 String lowercaseText (text.toLowerCase());
2016
2017 for (auto& testText : vstOnStrings)
2018 if (lowercaseText == testText)
2019 return 1.0f;
2020
2021 for (auto& testText : vstOffStrings)
2022 if (lowercaseText == testText)
2023 return 0.0f;
2024
2025 return text.getIntValue() != 0 ? 1.0f : 0.0f;
2026 }
2027
2028 float getValue() const override { return currentValue; }
2029 float getDefaultValue() const override { return 0.0f; }
2030 String getName (int /*maximumStringLength*/) const override { return "Bypass"; }
2031 String getText (float value, int) const override { return (! approximatelyEqual (value, 0.0f) ? TRANS ("On") : TRANS ("Off")); }
2032 bool isAutomatable() const override { return true; }
2033 bool isDiscrete() const override { return true; }
2034 bool isBoolean() const override { return true; }
2035 int getNumSteps() const override { return 2; }
2036 StringArray getAllValueStrings() const override { return values; }
2037 String getLabel() const override { return {}; }
2038 String getParameterID() const override { return {}; }
2039
2040 VSTPluginInstance& parent;
2041 bool currentValue = false;
2042 StringArray vstOnStrings, vstOffStrings, values;
2043 };
2044
2045 //==============================================================================
2046 String name;
2047 CriticalSection lock;
2049 bool initialised = false;
2050 std::atomic<bool> isPowerOn { false };
2052 mutable StringArray programNames;
2053 AudioBuffer<float> outOfPlaceBuffer;
2055
2056 CriticalSection midiInLock;
2057 MidiBuffer incomingMidi;
2059 Vst2::VstTimeInfo vstHostTime;
2060
2061 AudioBuffer<float> tmpBufferFloat;
2062 HeapBlock<float*> channelBufferFloat;
2063
2064 AudioBuffer<double> tmpBufferDouble;
2067
2069
2070 static pointer_sized_int handleCanDo (const char* name)
2071 {
2072 static const char* canDos[] = { "supplyIdle",
2073 "sendVstEvents",
2074 "sendVstMidiEvent",
2075 "sendVstTimeInfo",
2076 "receiveVstEvents",
2077 "receiveVstMidiEvent",
2078 "supportShell",
2079 "sizeWindow",
2080 "shellCategory" };
2081
2082 for (int i = 0; i < numElementsInArray (canDos); ++i)
2083 if (strcmp (canDos[i], name) == 0)
2084 return 1;
2085
2086 return 0;
2087 }
2088
2089 static pointer_sized_int getHostName (char* name)
2090 {
2091 String hostName (JUCE_VST_FALLBACK_HOST_NAME);
2092
2094 hostName = app->getApplicationName();
2095
2096 hostName.copyToUTF8 (name, (size_t) jmin (Vst2::kVstMaxVendorStrLen, Vst2::kVstMaxProductStrLen) - 1);
2097 return 1;
2098 }
2099
2100 pointer_sized_int getVSTTime() noexcept
2101 {
2102 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4311)
2103
2105
2106 JUCE_END_IGNORE_WARNINGS_MSVC
2107 }
2108
2109 void handleIdle()
2110 {
2111 if (insideVSTCallback == 0 && MessageManager::getInstance()->isThisTheMessageThread())
2112 {
2114
2115 #if JUCE_MAC
2116 if (getActiveEditor() != nullptr)
2117 dispatch (Vst2::effEditIdle, 0, 0, nullptr, 0);
2118 #endif
2119
2121 handleUpdateNowIfNeeded();
2122
2123 for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
2124 if (auto* p = ComponentPeer::getPeer (i))
2125 p->performAnyPendingRepaintsNow();
2126 }
2127 }
2128
2129 void setWindowSize (int width, int height)
2130 {
2131 #if JUCE_LINUX || JUCE_BSD
2132 const MessageManagerLock mmLock;
2133 #endif
2134
2135 updateSizeFromEditor (width, height);
2136 }
2137
2138 //==============================================================================
2139 static Vst2::AEffect* constructEffect (const ModuleHandle::Ptr& module)
2140 {
2141 Vst2::AEffect* effect = nullptr;
2142 try
2143 {
2145 _fpreset();
2146
2147 JUCE_VST_LOG ("Creating VST instance: " + module->pluginName);
2148
2149 #if JUCE_MAC
2150 if (module->resFileId != 0)
2151 UseResFile (module->resFileId);
2152 #endif
2153
2154 {
2156 }
2157
2158 if (effect != nullptr && effect->magic == 0x56737450 /* 'VstP' */)
2159 {
2160 jassert (effect->resvd2 == 0);
2161 jassert (effect->object != nullptr);
2162
2163 _fpreset(); // some dodgy plugs mess around with this
2164 }
2165 else
2166 {
2167 effect = nullptr;
2168 }
2169 }
2170 catch (...)
2171 {}
2172
2173 return effect;
2174 }
2175
2176 static BusesProperties queryBusIO (Vst2::AEffect* effect)
2177 {
2178 BusesProperties returnValue;
2179
2180 if (effect->numInputs == 0 && effect->numOutputs == 0)
2181 return returnValue;
2182
2183 // Workaround for old broken JUCE plug-ins which would return an invalid
2184 // speaker arrangement if the host didn't ask for a specific arrangement
2185 // beforehand.
2186 // Check if the plug-in reports any default layouts. If it doesn't, then
2187 // try setting a default layout compatible with the number of pins this
2188 // plug-in is reporting.
2190 {
2191 SpeakerMappings::VstSpeakerConfigurationHolder canonicalIn (AudioChannelSet::canonicalChannelSet (effect->numInputs));
2192 SpeakerMappings::VstSpeakerConfigurationHolder canonicalOut (AudioChannelSet::canonicalChannelSet (effect->numOutputs));
2193
2194 effect->dispatcher (effect, Vst2::effSetSpeakerArrangement, 0,
2195 (pointer_sized_int) &canonicalIn.get(), (void*) &canonicalOut.get(), 0.0f);
2196 }
2197
2198 const auto arrangement = getSpeakerArrangementWrapper (effect);
2199
2200 for (int dir = 0; dir < 2; ++dir)
2201 {
2202 const bool isInput = (dir == 0);
2203 const int opcode = (isInput ? Vst2::effGetInputProperties : Vst2::effGetOutputProperties);
2204 const int maxChannels = (isInput ? effect->numInputs : effect->numOutputs);
2205 const auto* arr = (isInput ? arrangement.in : arrangement.out);
2206 bool busAdded = false;
2207
2208 Vst2::VstPinProperties pinProps;
2209 AudioChannelSet layout;
2210
2211 for (int ch = 0; ch < maxChannels; ch += layout.size())
2212 {
2213 if (effect->dispatcher (effect, opcode, ch, 0, &pinProps, 0.0f) == 0)
2214 break;
2215
2216 if ((pinProps.flags & Vst2::kVstPinUseSpeaker) != 0)
2217 {
2218 layout = SpeakerMappings::vstArrangementTypeToChannelSet (pinProps.arrangementType, 0);
2219
2220 if (layout.isDisabled())
2221 break;
2222 }
2223 else if (arr == nullptr)
2224 {
2225 layout = ((pinProps.flags & Vst2::kVstPinIsStereo) != 0 ? AudioChannelSet::stereo() : AudioChannelSet::mono());
2226 }
2227 else
2228 break;
2229
2230 busAdded = true;
2231 returnValue.addBus (isInput, pinProps.label, layout, true);
2232 }
2233
2234 // no buses?
2235 if (! busAdded && maxChannels > 0)
2236 {
2237 String busName = (isInput ? "Input" : "Output");
2238
2239 if (effect->dispatcher (effect, opcode, 0, 0, &pinProps, 0.0f) != 0)
2240 busName = pinProps.label;
2241
2242 if (arr != nullptr)
2243 layout = SpeakerMappings::vstArrangementTypeToChannelSet (*arr);
2244 else
2246
2247 returnValue.addBus (isInput, busName, layout, true);
2248 }
2249 }
2250
2251 return returnValue;
2252 }
2253
2254 static bool pluginHasDefaultChannelLayouts (Vst2::AEffect* effect)
2255 {
2256 if (getSpeakerArrangementWrapper (effect).isValid())
2257 return true;
2258
2259 for (int dir = 0; dir < 2; ++dir)
2260 {
2261 const bool isInput = (dir == 0);
2262 const int opcode = (isInput ? Vst2::effGetInputProperties : Vst2::effGetOutputProperties);
2263 const int maxChannels = (isInput ? effect->numInputs : effect->numOutputs);
2264
2265 int channels = 1;
2266
2267 for (int ch = 0; ch < maxChannels; ch += channels)
2268 {
2269 Vst2::VstPinProperties pinProps;
2270
2271 if (effect->dispatcher (effect, opcode, ch, 0, &pinProps, 0.0f) == 0)
2272 return false;
2273
2274 if ((pinProps.flags & Vst2::kVstPinUseSpeaker) != 0)
2275 return true;
2276
2277 channels = (pinProps.flags & Vst2::kVstPinIsStereo) != 0 ? 2 : 1;
2278 }
2279 }
2280
2281 return false;
2282 }
2283
2284 struct SpeakerArrangements
2285 {
2286 const Vst2::VstSpeakerArrangement* in;
2287 const Vst2::VstSpeakerArrangement* out;
2288
2289 bool isValid() const noexcept { return in != nullptr && out != nullptr; }
2290 };
2291
2293 {
2294 // Workaround: unfortunately old JUCE VST-2 plug-ins had a bug and would crash if
2295 // you try to get the speaker arrangement when there are no input channels present.
2296 // Hopefully, one day (when there are no more old JUCE plug-ins around), we can
2297 // comment out the next two lines.
2298 if (effect->numInputs == 0)
2299 return { nullptr, nullptr };
2300
2301 SpeakerArrangements result { nullptr, nullptr };
2302 const auto dispatchResult = effect->dispatcher (effect,
2303 Vst2::effGetSpeakerArrangement,
2304 0,
2305 reinterpret_cast<pointer_sized_int> (&result.in),
2306 &result.out,
2307 0.0f);
2308
2309 if (dispatchResult != 0)
2310 return result;
2311
2312 return { nullptr, nullptr };
2313 }
2314
2315 template <typename Member, typename Value>
2316 void setFromOptional (Member& target, Optional<Value> opt, int32_t flag)
2317 {
2318 if (opt.hasValue())
2319 {
2320 target = static_cast<Member> (*opt);
2321 vstHostTime.flags |= flag;
2322 }
2323 else
2324 {
2325 vstHostTime.flags &= ~flag;
2326 }
2327 }
2328
2329 //==============================================================================
2330 template <typename FloatType>
2331 void processAudio (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages,
2332 AudioBuffer<FloatType>& tmpBuffer,
2335 {
2337 {
2339 }
2341 {
2342 // if this vst does not support bypass then we will have to do this ourselves
2344 return;
2345 }
2346
2347 auto numSamples = buffer.getNumSamples();
2348 auto numChannels = buffer.getNumChannels();
2349
2350 if (initialised)
2351 {
2352 if (auto* currentPlayHead = getPlayHead())
2353 {
2354 if (const auto position = currentPlayHead->getPosition())
2355 {
2356 if (const auto samplePos = position->getTimeInSamples())
2357 vstHostTime.samplePos = (double) *samplePos;
2358 else
2359 jassertfalse; // VST hosts *must* call setTimeInSamples on the audio playhead
2360
2361 if (auto sig = position->getTimeSignature())
2362 {
2363 vstHostTime.flags |= Vst2::kVstTimeSigValid;
2364 vstHostTime.timeSigNumerator = sig->numerator;
2365 vstHostTime.timeSigDenominator = sig->denominator;
2366 }
2367 else
2368 {
2370 }
2371
2372 setFromOptional (vstHostTime.ppqPos, position->getPpqPosition(), Vst2::kVstPpqPosValid);
2373 setFromOptional (vstHostTime.barStartPos, position->getPpqPositionOfLastBarStart(), Vst2::kVstBarsValid);
2374 setFromOptional (vstHostTime.nanoSeconds, position->getHostTimeNs(), Vst2::kVstNanosValid);
2375 setFromOptional (vstHostTime.tempo, position->getBpm(), Vst2::kVstTempoValid);
2376
2378 if (position->getIsPlaying()) newTransportFlags |= Vst2::kVstTransportPlaying;
2379 if (position->getIsRecording()) newTransportFlags |= Vst2::kVstTransportRecording;
2380
2381 if (newTransportFlags != (vstHostTime.flags & (Vst2::kVstTransportPlaying
2382 | Vst2::kVstTransportRecording)))
2383 vstHostTime.flags = (vstHostTime.flags & ~(Vst2::kVstTransportPlaying | Vst2::kVstTransportRecording)) | newTransportFlags | Vst2::kVstTransportChanged;
2384 else
2386
2387 const auto optionalFrameRate = [fr = position->getFrameRate()]() -> Optional<Vst2::VstInt32>
2388 {
2389 if (! fr.hasValue())
2390 return {};
2391
2392 switch (fr->getBaseRate())
2393 {
2394 case 24: return fr->isPullDown() ? Vst2::kVstSmpte239fps : Vst2::kVstSmpte24fps;
2395 case 25: return fr->isPullDown() ? Vst2::kVstSmpte249fps : Vst2::kVstSmpte25fps;
2396 case 30: return fr->isPullDown() ? (fr->isDrop() ? Vst2::kVstSmpte2997dfps : Vst2::kVstSmpte2997fps)
2397 : (fr->isDrop() ? Vst2::kVstSmpte30dfps : Vst2::kVstSmpte30fps);
2398 case 60: return fr->isPullDown() ? Vst2::kVstSmpte599fps : Vst2::kVstSmpte60fps;
2399 }
2400
2401 return {};
2402 }();
2403
2404 vstHostTime.flags |= optionalFrameRate ? Vst2::kVstSmpteValid : 0;
2405 vstHostTime.smpteFrameRate = optionalFrameRate.orFallback (Vst2::VstSmpteFrameRate{});
2406 const auto effectiveRate = position->getFrameRate().hasValue() ? position->getFrameRate()->getEffectiveRate() : 0.0;
2407 vstHostTime.smpteOffset = (int32) (position->getTimeInSeconds().orFallback (0.0) * 80.0 * effectiveRate + 0.5);
2408
2409 if (const auto loop = position->getLoopPoints())
2410 {
2411 vstHostTime.flags |= Vst2::kVstCyclePosValid;
2412 vstHostTime.cycleStartPos = loop->ppqStart;
2413 vstHostTime.cycleEndPos = loop->ppqEnd;
2414 }
2415 else
2416 {
2418 }
2419
2420 if (position->getIsLooping())
2421 vstHostTime.flags |= Vst2::kVstTransportCycleActive;
2422 else
2424 }
2425 }
2426
2427 vstHostTime.nanoSeconds = getVSTHostTimeNanoseconds();
2428
2430 {
2431 midiEventsToSend.clear();
2432 midiEventsToSend.ensureSize (1);
2433
2434 for (const auto metadata : midiMessages)
2435 midiEventsToSend.addEvent (metadata.data, metadata.numBytes,
2436 jlimit (0, numSamples - 1, metadata.samplePosition));
2437
2438 vstEffect->dispatcher (vstEffect, Vst2::effProcessEvents, 0, 0, midiEventsToSend.events, 0);
2439 }
2440
2441 _clearfp();
2442
2443 // always ensure that the buffer is at least as large as the maximum number of channels
2444 auto maxChannels = jmax (vstEffect->numInputs, vstEffect->numOutputs);
2445 auto channels = channelBuffer.get();
2446
2447 if (numChannels < maxChannels)
2448 {
2449 if (numSamples > tmpBuffer.getNumSamples())
2450 tmpBuffer.setSize (tmpBuffer.getNumChannels(), numSamples);
2451
2452 tmpBuffer.clear();
2453 }
2454
2455 for (int ch = 0; ch < maxChannels; ++ch)
2456 channels[ch] = (ch < numChannels ? buffer.getWritePointer (ch) : tmpBuffer.getWritePointer (ch));
2457
2458 {
2459 AudioBuffer<FloatType> processBuffer (channels, maxChannels, numSamples);
2460
2462 }
2463 }
2464 else
2465 {
2466 // Not initialised, so just bypass.
2467 for (int i = getTotalNumOutputChannels(); --i >= 0;)
2468 buffer.clear (i, 0, buffer.getNumSamples());
2469 }
2470
2471 {
2472 // copy any incoming midi.
2473 const ScopedLock sl (midiInLock);
2474
2475 midiMessages.swapWith (incomingMidi);
2476 incomingMidi.clear();
2477 }
2478 }
2479
2480 //==============================================================================
2481 inline void invokeProcessFunction (AudioBuffer<float>& buffer, int32 sampleFrames)
2482 {
2483 if ((vstEffect->flags & Vst2::effFlagsCanReplacing) != 0)
2484 {
2487 }
2488 else
2489 {
2490 outOfPlaceBuffer.setSize (vstEffect->numOutputs, sampleFrames);
2491 outOfPlaceBuffer.clear();
2492
2495
2496 for (int i = vstEffect->numOutputs; --i >= 0;)
2497 buffer.copyFrom (i, 0, outOfPlaceBuffer.getReadPointer (i), sampleFrames);
2498 }
2499 }
2500
2501 inline void invokeProcessFunction (AudioBuffer<double>& buffer, int32 sampleFrames)
2502 {
2503 vstEffect->processDoubleReplacing (vstEffect, tempChannelPointers[0].getArrayOfModifiableWritePointers (buffer),
2505 }
2506
2507 //==============================================================================
2508 bool restoreProgramSettings (const fxProgram* const prog)
2509 {
2510 if (compareMagic (prog->chunkMagic, "CcnK")
2511 && compareMagic (prog->fxMagic, "FxCk"))
2512 {
2513 changeProgramName (getCurrentProgram(), prog->prgName);
2514
2515 for (int i = 0; i < fxbSwap (prog->numParams); ++i)
2516 if (auto* param = getParameters()[i])
2517 param->setValue (fxbSwapFloat (prog->params[i]));
2518
2519 return true;
2520 }
2521
2522 return false;
2523 }
2524
2525 String getTextForOpcode (const int index, const int opcode) const
2526 {
2527 if (vstEffect == nullptr)
2528 return {};
2529
2530 jassert (index >= 0 && index < vstEffect->numParams);
2531 char nm[256] = { 0 };
2532 dispatch (opcode, index, 0, nm, 0);
2533 return String::createStringFromData (nm, (int) sizeof (nm)).trim();
2534 }
2535
2536 String getCurrentProgramName()
2537 {
2538 String progName;
2539
2540 if (vstEffect != nullptr)
2541 {
2542 {
2543 char nm[256] = { 0 };
2544 dispatch (Vst2::effGetProgramName, 0, 0, nm, 0);
2545 progName = String::createStringFromData (nm, (int) sizeof (nm)).trim();
2546 }
2547
2548 const int index = getCurrentProgram();
2549
2550 if (index >= 0 && programNames[index].isEmpty())
2551 {
2552 while (programNames.size() < index)
2553 programNames.add (String());
2554
2555 programNames.set (index, progName);
2556 }
2557 }
2558
2559 return progName;
2560 }
2561
2563 {
2564 auto numParams = getParameters().size();
2565
2566 prog->chunkMagic = fxbName ("CcnK");
2567 prog->byteSize = 0;
2568 prog->fxMagic = fxbName ("FxCk");
2569 prog->version = fxbSwap (fxbVersionNum);
2570 prog->fxID = fxbSwap (getUID());
2571 prog->fxVersion = fxbSwap (getVersionNumber());
2572 prog->numParams = fxbSwap (numParams);
2573
2574 getCurrentProgramName().copyToUTF8 (prog->prgName, sizeof (prog->prgName) - 1);
2575
2576 for (int i = 0; i < numParams; ++i)
2577 if (auto* param = getParameters()[i])
2578 prog->params[i] = fxbSwapFloat (param->getValue());
2579 }
2580
2582 {
2583 if (vstEffect != nullptr && getNumPrograms() > 0)
2584 {
2585 char nm[256] = { 0 };
2586
2587 // only do this if the plugin can't use indexed names.
2588 if (dispatch (Vst2::effGetProgramNameIndexed, 0, -1, nm, 0) == 0)
2589 {
2590 auto oldProgram = getCurrentProgram();
2591 MemoryBlock oldSettings;
2593
2594 for (int i = 0; i < getNumPrograms(); ++i)
2595 {
2596 setCurrentProgram (i);
2597 getCurrentProgramName(); // (this updates the list)
2598 }
2599
2600 setCurrentProgram (oldProgram);
2602 }
2603 }
2604 }
2605
2606 void handleMidiFromPlugin (const Vst2::VstEvents* events)
2607 {
2608 if (events != nullptr)
2609 {
2610 const ScopedLock sl (midiInLock);
2611 VSTMidiEventList::addEventsToMidiBuffer (events, incomingMidi);
2612 }
2613 }
2614
2615 //==============================================================================
2616 void createTempParameterStore (MemoryBlock& dest)
2617 {
2618 auto numParameters = getParameters().size();
2619 dest.setSize (64 + 4 * (size_t) numParameters);
2620 dest.fillWith (0);
2621
2622 getCurrentProgramName().copyToUTF8 ((char*) dest.getData(), 63);
2623
2624 auto p = unalignedPointerCast<float*> (((char*) dest.getData()) + 64);
2625
2626 for (int i = 0; i < numParameters; ++i)
2627 if (auto* param = getParameters()[i])
2628 p[i] = param->getValue();
2629 }
2630
2631 void restoreFromTempParameterStore (const MemoryBlock& m)
2632 {
2633 changeProgramName (getCurrentProgram(), (const char*) m.getData());
2634
2635 auto p = unalignedPointerCast<float*> (((char*) m.getData()) + 64);
2636 auto numParameters = getParameters().size();
2637
2638 for (int i = 0; i < numParameters; ++i)
2639 if (auto* param = getParameters()[i])
2640 param->setValue (p[i]);
2641 }
2642
2644 {
2645 #if JUCE_MAC
2646 return (pointer_sized_int) (void*) &vstModule->parentDirFSSpec;
2647 #else
2648 return (pointer_sized_int) (pointer_sized_uint) vstModule->fullParentDirectoryPathName.toRawUTF8();
2649 #endif
2650 }
2651
2652 //==============================================================================
2653 int getVersionNumber() const noexcept { return vstEffect != nullptr ? vstEffect->version : 0; }
2654
2655 String getVersion() const
2656 {
2657 auto v = (unsigned int) dispatch (Vst2::effGetVendorVersion, 0, 0, nullptr, 0);
2658
2659 String s;
2660
2661 if (v == 0 || (int) v == -1)
2662 v = (unsigned int) getVersionNumber();
2663
2664 if (v != 0)
2665 {
2666 // See yfede's post for the rational on this encoding
2667 // https://forum.juce.com/t/issues-with-version-integer-reported-by-vst2/23867/6
2668
2669 unsigned int major = 0, minor = 0, bugfix = 0, build = 0;
2670
2671 if (v < 10) // Encoding A
2672 {
2673 major = v;
2674 }
2675 else if (v < 10000) // Encoding B
2676 {
2677 major = (v / 1000);
2678 minor = (v % 1000) / 100;
2679 bugfix = (v % 100) / 10;
2680 build = (v % 10);
2681 }
2682 else if (v < 0x10000) // Encoding C
2683 {
2684 major = (v / 10000);
2685 minor = (v % 10000) / 1000;
2686 bugfix = (v % 1000) / 100;
2687 build = (v % 100) / 10;
2688 }
2689 else if (v < 0x650000) // Encoding D
2690 {
2691 major = (v >> 16) & 0xff;
2692 minor = (v >> 8) & 0xff;
2693 bugfix = (v >> 0) & 0xff;
2694 }
2695 else // Encoding E
2696 {
2697 major = (v / 10000000);
2698 minor = (v % 10000000) / 100000;
2699 bugfix = (v % 100000) / 1000;
2700 build = (v % 1000);
2701 }
2702
2703 s << (int) major << '.' << (int) minor << '.' << (int) bugfix << '.' << (int) build;
2704 }
2705
2706 return s;
2707 }
2708
2709 const char* getCategory() const
2710 {
2711 switch (getVstCategory())
2712 {
2713 case Vst2::kPlugCategEffect: return "Effect";
2714 case Vst2::kPlugCategSynth: return "Synth";
2715 case Vst2::kPlugCategAnalysis: return "Analysis";
2716 case Vst2::kPlugCategMastering: return "Mastering";
2717 case Vst2::kPlugCategSpacializer: return "Spacial";
2718 case Vst2::kPlugCategRoomFx: return "Reverb";
2719 case Vst2::kPlugSurroundFx: return "Surround";
2720 case Vst2::kPlugCategRestoration: return "Restoration";
2721 case Vst2::kPlugCategGenerator: return "Tone generation";
2722 case Vst2::kPlugCategOfflineProcess: return "Offline Process";
2723 case Vst2::kPlugCategShell: return "Shell";
2724 case Vst2::kPlugCategUnknown: return "Unknown";
2725 case Vst2::kPlugCategMaxCount:
2726 default: break;
2727 }
2728
2729 return nullptr;
2730 }
2731
2732 void setPower (const bool on)
2733 {
2734 dispatch (Vst2::effMainsChanged, 0, on ? 1 : 0, nullptr, 0);
2735 isPowerOn = on;
2736 }
2737
2738 //==============================================================================
2740 {
2742 {
2744 bypassParam->setValue (1.0f);
2745 }
2746 else
2747 {
2749 bypassParam->setValue (0.0f);
2750 }
2751
2753 }
2754
2756};
2757
2758//==============================================================================
2759#if ! (JUCE_IOS || JUCE_ANDROID)
2760struct VSTPluginWindow;
2762
2763//==============================================================================
2764struct VSTPluginWindow final : public AudioProcessorEditor,
2765 #if ! JUCE_MAC
2766 private ComponentMovementWatcher,
2767 #endif
2768 private Timer
2769{
2770public:
2771 VSTPluginWindow (VSTPluginInstance& plug)
2772 : AudioProcessorEditor (&plug),
2773 #if ! JUCE_MAC
2774 ComponentMovementWatcher (this),
2775 #endif
2776 plugin (plug)
2777 {
2778 #if JUCE_LINUX || JUCE_BSD
2779 pluginWindow = None;
2780 ignoreUnused (pluginRefusesToResize, alreadyInside);
2781 #elif JUCE_MAC
2782 ignoreUnused (recursiveResize, pluginRefusesToResize, alreadyInside);
2783
2784 cocoaWrapper.reset (new NSViewComponentWithParent (plugin));
2785 addAndMakeVisible (cocoaWrapper.get());
2786 #endif
2787
2788 activeVSTWindows.add (this);
2789
2790 Vst2::ERect* rect = nullptr;
2791 dispatch (Vst2::effEditGetRect, 0, 0, &rect, 0);
2792
2793 if (rect != nullptr)
2794 updateSizeFromEditor (rect->right - rect->left, rect->bottom - rect->top);
2795 else
2796 updateSizeFromEditor (1, 1);
2797
2798 setOpaque (true);
2799 setVisible (true);
2800
2801 #if JUCE_WINDOWS
2802 addAndMakeVisible (embeddedComponent);
2803 #endif
2804 }
2805
2806 ~VSTPluginWindow() override
2807 {
2808 activeVSTWindows.removeFirstMatchingValue (this);
2809
2810 closePluginWindow();
2811
2812 #if JUCE_MAC
2813 cocoaWrapper.reset();
2814 #endif
2815
2816 plugin.editorBeingDeleted (this);
2817 }
2818
2819 //==============================================================================
2820 /* Convert from the hosted VST's coordinate system to the component's coordinate system. */
2821 Rectangle<int> vstToComponentRect (Component& editor, const Rectangle<int>& vr) const
2822 {
2823 return editor.getLocalArea (nullptr, vr / (nativeScaleFactor * getDesktopScaleFactor()));
2824 }
2825
2826 Rectangle<int> componentToVstRect (Component& editor, const Rectangle<int>& vr) const
2827 {
2828 if (auto* tl = editor.getTopLevelComponent())
2829 return tl->getLocalArea (&editor, vr) * nativeScaleFactor * tl->getDesktopScaleFactor();
2830
2831 return {};
2832 }
2833
2834 bool updateSizeFromEditor (int w, int h)
2835 {
2836 const auto correctedBounds = vstToComponentRect (*this, { w, h });
2837 setSize (correctedBounds.getWidth(), correctedBounds.getHeight());
2838
2839 #if JUCE_MAC
2840 if (cocoaWrapper != nullptr)
2841 cocoaWrapper->setSize (correctedBounds.getWidth(), correctedBounds.getHeight());
2842 #endif
2843
2844 return true;
2845 }
2846
2847 #if JUCE_MAC
2848 void paint (Graphics& g) override
2849 {
2850 g.fillAll (Colours::black);
2851 }
2852
2853 void visibilityChanged() override
2854 {
2855 if (isShowing())
2856 openPluginWindow ((NSView*) cocoaWrapper->getView());
2857 else
2858 closePluginWindow();
2859 }
2860
2861 void childBoundsChanged (Component*) override
2862 {
2863 auto w = cocoaWrapper->getWidth();
2864 auto h = cocoaWrapper->getHeight();
2865
2866 if (w != getWidth() || h != getHeight())
2867 setSize (w, h);
2868 }
2869
2870 void parentHierarchyChanged() override { visibilityChanged(); }
2871 #else
2872 float getEffectiveScale() const
2873 {
2874 return nativeScaleFactor * userScaleFactor;
2875 }
2876
2877 void paint (Graphics& g) override
2878 {
2879 #if JUCE_LINUX || JUCE_BSD
2880 if (isOpen)
2881 {
2882 if (pluginWindow != 0)
2883 {
2884 auto clip = componentToVstRect (*this, g.getClipBounds().toNearestInt());
2885
2886 X11Symbols::getInstance()->xClearArea (display, pluginWindow, clip.getX(), clip.getY(),
2887 static_cast<unsigned int> (clip.getWidth()),
2888 static_cast<unsigned int> (clip.getHeight()), True);
2889 }
2890 }
2891 else
2892 #endif
2893 {
2894 g.fillAll (Colours::black);
2895 }
2896 }
2897
2898 void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
2899 {
2900 if (recursiveResize)
2901 return;
2902
2903 if (getPeer() != nullptr)
2904 {
2905 const ScopedValueSetter<bool> recursiveResizeSetter (recursiveResize, true);
2906
2907 #if JUCE_WINDOWS
2908 embeddedComponent.setBounds (getLocalBounds());
2909 #elif JUCE_LINUX || JUCE_BSD
2910 const auto pos = componentToVstRect (*this, getLocalBounds());
2911
2912 if (pluginWindow != 0)
2913 {
2914 auto* symbols = X11Symbols::getInstance();
2915 symbols->xMoveResizeWindow (display,
2916 pluginWindow,
2917 pos.getX(),
2918 pos.getY(),
2919 (unsigned int) pos.getWidth(),
2920 (unsigned int) pos.getHeight());
2921 symbols->xMapRaised (display, pluginWindow);
2922 symbols->xFlush (display);
2923 }
2924 #endif
2925 }
2926 }
2927
2928 using ComponentMovementWatcher::componentMovedOrResized;
2929
2930 void componentVisibilityChanged() override
2931 {
2932 if (isShowing())
2933 openPluginWindow();
2934 else if (! shouldAvoidDeletingWindow())
2935 closePluginWindow();
2936
2937 setContentScaleFactor();
2938
2939 #if JUCE_LINUX || JUCE_BSD
2940 MessageManager::callAsync ([safeThis = SafePointer<VSTPluginWindow> { this }]
2941 {
2942 if (safeThis != nullptr)
2943 safeThis->componentMovedOrResized (true, true);
2944 });
2945 #else
2946 componentMovedOrResized (true, true);
2947 #endif
2948 }
2949
2950 using ComponentMovementWatcher::componentVisibilityChanged;
2951
2952 void componentPeerChanged() override
2953 {
2954 closePluginWindow();
2955
2956 if (getPeer() != nullptr)
2957 {
2958 openPluginWindow();
2959 componentMovedOrResized (true, true);
2960 }
2961 }
2962
2963 void setContentScaleFactor()
2964 {
2965 if (pluginRespondsToDPIChanges)
2966 dispatch (Vst2::effVendorSpecific,
2967 (int) ByteOrder::bigEndianInt ("PreS"),
2968 (int) ByteOrder::bigEndianInt ("AeCs"),
2969 nullptr, getEffectiveScale());
2970 }
2971 #endif
2972
2973 void setScaleFactor (float scale) override
2974 {
2975 userScaleFactor = scale;
2976
2977 #if ! JUCE_MAC
2978 setContentScaleFactor();
2979 #endif
2980
2981 #if JUCE_WINDOWS
2982 resizeToFit();
2983 #endif
2984 }
2985
2986 //==============================================================================
2987 bool keyStateChanged (bool) override { return pluginWantsKeys; }
2988 bool keyPressed (const juce::KeyPress&) override { return pluginWantsKeys; }
2989
2990 //==============================================================================
2991 void timerCallback() override
2992 {
2993 if (isShowing())
2994 {
2995 #if JUCE_WINDOWS
2996 if (--sizeCheckCount <= 0)
2997 {
2998 sizeCheckCount = 10;
2999 checkPluginWindowSize();
3000 }
3001 #endif
3002
3003 static bool reentrantGuard = false;
3004
3005 if (! reentrantGuard)
3006 {
3007 reentrantGuard = true;
3008
3009 #if JUCE_WINDOWS
3010 // Some plugins may draw/resize inside their idle callback, so ensure that
3011 // DPI awareness is set correctly inside this call.
3012 ScopedThreadDPIAwarenessSetter scope (getPluginHWND());
3013 #endif
3014 plugin.dispatch (Vst2::effEditIdle, 0, 0, nullptr, 0);
3015
3016 reentrantGuard = false;
3017 }
3018
3019 #if JUCE_LINUX || JUCE_BSD
3020 if (pluginWindow == 0)
3021 {
3022 updatePluginWindowHandle();
3023
3024 if (pluginWindow != 0)
3025 componentMovedOrResized (true, true);
3026 }
3027 #endif
3028 }
3029 }
3030
3031 //==============================================================================
3032 void mouseDown ([[maybe_unused]] const MouseEvent& e) override
3033 {
3034 #if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD
3035 toFront (true);
3036 #endif
3037 }
3038
3039 void broughtToFront() override
3040 {
3041 if (activeVSTWindows.removeFirstMatchingValue (this) != -1)
3042 activeVSTWindows.add (this);
3043
3044 #if JUCE_MAC
3045 dispatch (Vst2::effEditTop, 0, 0, nullptr, 0);
3046 #endif
3047 }
3048
3049private:
3050 //==============================================================================
3051 // This is a workaround for old Mackie plugins that crash if their
3052 // window is deleted more than once.
3053 bool shouldAvoidDeletingWindow() const
3054 {
3055 return plugin.getPluginDescription()
3056 .manufacturerName.containsIgnoreCase ("Loud Technologies");
3057 }
3058
3059 // This is an old workaround for some plugins that need a repaint when their
3060 // windows are first created, but it breaks some Izotope plugins.
3061 bool shouldRepaintCarbonWindowWhenCreated()
3062 {
3063 return ! plugin.getName().containsIgnoreCase ("izotope");
3064 }
3065
3066 //==============================================================================
3067 #if JUCE_MAC
3068 void openPluginWindow (void* parentWindow)
3069 {
3070 if (isOpen || parentWindow == nullptr)
3071 return;
3072
3073 isOpen = true;
3074
3075 Vst2::ERect* rect = nullptr;
3076 dispatch (Vst2::effEditGetRect, 0, 0, &rect, 0);
3077 dispatch (Vst2::effEditOpen, 0, 0, parentWindow, 0);
3078
3079 // do this before and after like in the steinberg example
3080 dispatch (Vst2::effEditGetRect, 0, 0, &rect, 0);
3081 dispatch (Vst2::effGetProgram, 0, 0, nullptr, 0); // also in steinberg code
3082
3083 // Install keyboard hooks
3084 pluginWantsKeys = (dispatch (Vst2::effKeysRequired, 0, 0, nullptr, 0) == 0);
3085
3086 // double-check it's not too tiny
3087 int w = 250, h = 150;
3088
3089 if (rect != nullptr)
3090 {
3091 w = rect->right - rect->left;
3092 h = rect->bottom - rect->top;
3093
3094 if (w == 0 || h == 0)
3095 {
3096 w = 250;
3097 h = 150;
3098 }
3099 }
3100
3101 w = jmax (w, 32);
3102 h = jmax (h, 32);
3103
3104 updateSizeFromEditor (w, h);
3105
3106 startTimer (18 + juce::Random::getSystemRandom().nextInt (5));
3107 repaint();
3108 }
3109 #else
3110 void openPluginWindow()
3111 {
3112 if (isOpen)
3113 return;
3114
3115 JUCE_VST_LOG ("Opening VST UI: " + plugin.getName());
3116 isOpen = true;
3117
3118 pluginRespondsToDPIChanges = plugin.pluginCanDo ("supportsViewDpiScaling") > 0;
3119
3120 setContentScaleFactor();
3121
3122 Vst2::ERect* rect = nullptr;
3123
3124 dispatch (Vst2::effEditGetRect, 0, 0, &rect, 0);
3125
3126 #if JUCE_WINDOWS
3127 auto* handle = embeddedComponent.getHWND();
3128 #else
3129 auto* handle = getWindowHandle();
3130 #endif
3131 dispatch (Vst2::effEditOpen, 0, 0, handle, 0);
3132 dispatch (Vst2::effEditGetRect, 0, 0, &rect, 0); // do this before and after like in the steinberg example
3133 dispatch (Vst2::effGetProgram, 0, 0, nullptr, 0); // also in steinberg code
3134
3135 pluginWantsKeys = (dispatch (Vst2::effKeysRequired, 0, 0, nullptr, 0) == 0);
3136
3137 #if JUCE_WINDOWS
3138 originalWndProc = nullptr;
3139 auto* pluginHWND = getPluginHWND();
3140
3141 if (pluginHWND == nullptr)
3142 {
3143 isOpen = false;
3144 setSize (300, 150);
3145 return;
3146 }
3147
3148 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4244)
3149
3150 if (! pluginWantsKeys)
3151 {
3152 originalWndProc = (void*) GetWindowLongPtr (pluginHWND, GWLP_WNDPROC);
3153 SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) vstHookWndProc);
3154 }
3155
3156 JUCE_END_IGNORE_WARNINGS_MSVC
3157
3158 RECT r;
3159
3160 {
3161 ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { pluginHWND };
3162 GetWindowRect (pluginHWND, &r);
3163 }
3164
3165 auto w = (int) (r.right - r.left);
3166 auto h = (int) (r.bottom - r.top);
3167
3168 if (rect != nullptr)
3169 {
3170 auto rw = rect->right - rect->left;
3171 auto rh = rect->bottom - rect->top;
3172
3173 if ((rw > 50 && rh > 50 && rw < 2000 && rh < 2000 && (! isWithin (w, rw, 2) || ! isWithin (h, rh, 2)))
3174 || ((w == 0 && rw > 0) || (h == 0 && rh > 0)))
3175 {
3176 // very dodgy logic to decide which size is right.
3177 if (std::abs (rw - w) > 350 || std::abs (rh - h) > 350)
3178 {
3179 ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { pluginHWND };
3180
3181 SetWindowPos (pluginHWND, nullptr,
3182 0, 0, rw, rh,
3183 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
3184
3185 GetWindowRect (pluginHWND, &r);
3186
3187 w = r.right - r.left;
3188 h = r.bottom - r.top;
3189
3190 pluginRefusesToResize = (w != rw) || (h != rh);
3191
3192 w = rw;
3193 h = rh;
3194 }
3195 }
3196 }
3197 #elif JUCE_LINUX || JUCE_BSD
3198 updatePluginWindowHandle();
3199
3200 int w = 250, h = 150;
3201
3202 if (rect != nullptr)
3203 {
3204 w = rect->right - rect->left;
3205 h = rect->bottom - rect->top;
3206
3207 if (w == 0 || h == 0)
3208 {
3209 w = 250;
3210 h = 150;
3211 }
3212 }
3213
3214 if (pluginWindow != 0)
3215 X11Symbols::getInstance()->xMapRaised (display, pluginWindow);
3216 #endif
3217
3218 // double-check it's not too tiny
3219 w = jmax (w, 32);
3220 h = jmax (h, 32);
3221
3222 updateSizeFromEditor (w, h);
3223
3224 #if JUCE_WINDOWS
3225 checkPluginWindowSize();
3226 #endif
3227
3228 startTimer (18 + juce::Random::getSystemRandom().nextInt (5));
3229 repaint();
3230 }
3231 #endif
3232
3233 //==============================================================================
3234 void closePluginWindow()
3235 {
3236 if (isOpen)
3237 {
3238 // You shouldn't end up hitting this assertion unless the host is trying to do GUI
3239 // cleanup on a non-GUI thread. If it does that, bad things could happen in here.
3241
3242 JUCE_VST_LOG ("Closing VST UI: " + plugin.getName());
3243 isOpen = false;
3244 dispatch (Vst2::effEditClose, 0, 0, nullptr, 0);
3245 stopTimer();
3246
3247 #if JUCE_WINDOWS
3248 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4244)
3249 auto* pluginHWND = getPluginHWND();
3250
3251 if (originalWndProc != nullptr && pluginHWND != nullptr && IsWindow (pluginHWND))
3252 SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) originalWndProc);
3253 JUCE_END_IGNORE_WARNINGS_MSVC
3254
3255 originalWndProc = nullptr;
3256 #elif JUCE_LINUX || JUCE_BSD
3257 pluginWindow = 0;
3258 #endif
3259 }
3260 }
3261
3262 //==============================================================================
3263 pointer_sized_int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt)
3264 {
3265 return plugin.dispatch (opcode, index, value, ptr, opt);
3266 }
3267
3268 //==============================================================================
3269 #if JUCE_WINDOWS
3270 bool isWindowSizeCorrectForPlugin (int w, int h)
3271 {
3272 if (pluginRefusesToResize)
3273 return true;
3274
3275 const auto converted = vstToComponentRect (*this, { w, h });
3276 return (isWithin (converted.getWidth(), getWidth(), 5) && isWithin (converted.getHeight(), getHeight(), 5));
3277 }
3278
3279 void resizeToFit()
3280 {
3281 Vst2::ERect* rect = nullptr;
3282 dispatch (Vst2::effEditGetRect, 0, 0, &rect, 0);
3283
3284 auto w = rect->right - rect->left;
3285 auto h = rect->bottom - rect->top;
3286
3287 if (! isWindowSizeCorrectForPlugin (w, h))
3288 {
3289 updateSizeFromEditor (w, h);
3290 embeddedComponent.updateHWNDBounds();
3291 sizeCheckCount = 0;
3292 }
3293 }
3294
3295 void checkPluginWindowSize()
3296 {
3297 if (! pluginRespondsToDPIChanges)
3298 resizeToFit();
3299 }
3300
3301 // hooks to get keyboard events from VST windows.
3302 static LRESULT CALLBACK vstHookWndProc (HWND hW, UINT message, WPARAM wParam, LPARAM lParam)
3303 {
3304 for (int i = activeVSTWindows.size(); --i >= 0;)
3305 {
3306 Component::SafePointer<VSTPluginWindow> w (activeVSTWindows[i]);
3307
3308 auto* pluginHWND = w->getPluginHWND();
3309
3310 if (w != nullptr && pluginHWND == hW)
3311 {
3312 if (message == WM_CHAR
3313 || message == WM_KEYDOWN
3314 || message == WM_SYSKEYDOWN
3315 || message == WM_KEYUP
3316 || message == WM_SYSKEYUP
3317 || message == WM_APPCOMMAND)
3318 {
3319 SendMessage ((HWND) w->getTopLevelComponent()->getWindowHandle(),
3320 message, wParam, lParam);
3321 }
3322
3323 if (w != nullptr) // (may have been deleted in SendMessage callback)
3324 return CallWindowProc ((WNDPROC) w->originalWndProc,
3325 (HWND) pluginHWND,
3326 message, wParam, lParam);
3327 }
3328 }
3329
3330 return DefWindowProc (hW, message, wParam, lParam);
3331 }
3332 #endif
3333
3334 #if JUCE_LINUX || JUCE_BSD
3335 void updatePluginWindowHandle()
3336 {
3337 pluginWindow = getChildWindow ((Window) getWindowHandle());
3338 }
3339 #endif
3340
3341 //==============================================================================
3342 #if JUCE_MAC
3344
3345 void resized() override
3346 {
3347 }
3348 #endif
3349
3350 //==============================================================================
3351 VSTPluginInstance& plugin;
3352 float userScaleFactor = 1.0f;
3353 bool isOpen = false, recursiveResize = false;
3354 bool pluginWantsKeys = false, pluginRefusesToResize = false, alreadyInside = false;
3355
3356 #if ! JUCE_MAC
3357 bool pluginRespondsToDPIChanges = false;
3358
3359 float nativeScaleFactor = 1.0f;
3360
3361 struct ScaleNotifierCallback
3362 {
3363 VSTPluginWindow& window;
3364
3365 void operator() (float platformScale) const
3366 {
3367 MessageManager::callAsync ([ref = Component::SafePointer<VSTPluginWindow> (&window), platformScale]
3368 {
3369 if (auto* r = ref.getComponent())
3370 {
3371 r->nativeScaleFactor = platformScale;
3372 r->setContentScaleFactor();
3373
3374 #if JUCE_WINDOWS
3375 r->resizeToFit();
3376 r->embeddedComponent.updateHWNDBounds();
3377 #endif
3378 r->componentMovedOrResized (true, true);
3379 }
3380 });
3381 }
3382 };
3383
3384 NativeScaleFactorNotifier scaleNotifier { this, ScaleNotifierCallback { *this } };
3385
3386 #if JUCE_WINDOWS
3387 struct ViewComponent final : public HWNDComponent
3388 {
3389 ViewComponent()
3390 {
3391 setOpaque (true);
3392 inner.addToDesktop (0);
3393
3394 if (auto* peer = inner.getPeer())
3395 setHWND (peer->getNativeHandle());
3396 }
3397
3398 void paint (Graphics& g) override { g.fillAll (Colours::black); }
3399
3400 private:
3401 struct Inner final : public Component
3402 {
3403 Inner() { setOpaque (true); }
3404 void paint (Graphics& g) override { g.fillAll (Colours::black); }
3405 };
3406
3407 Inner inner;
3408 };
3409
3410 HWND getPluginHWND() const
3411 {
3412 return GetWindow ((HWND) embeddedComponent.getHWND(), GW_CHILD);
3413 }
3414
3415 ViewComponent embeddedComponent;
3416 void* originalWndProc = {};
3417 int sizeCheckCount = 0;
3418 #elif JUCE_LINUX || JUCE_BSD
3419 ::Display* display = XWindowSystem::getInstance()->getDisplay();
3420 Window pluginWindow = 0;
3421 #endif
3422 #else
3423 static constexpr auto nativeScaleFactor = 1.0f;
3424 #endif
3425
3426 //==============================================================================
3428};
3429#endif
3430
3431JUCE_END_IGNORE_WARNINGS_MSVC
3432
3433//==============================================================================
3434AudioProcessorEditor* VSTPluginInstance::createEditor()
3435{
3436 #if JUCE_IOS || JUCE_ANDROID
3437 return nullptr;
3438 #else
3439 return hasEditor() ? new VSTPluginWindow (*this)
3440 : nullptr;
3441 #endif
3442}
3443
3444bool VSTPluginInstance::updateSizeFromEditor ([[maybe_unused]] int w, [[maybe_unused]] int h)
3445{
3446 #if ! JUCE_IOS && ! JUCE_ANDROID
3447 if (auto* editor = dynamic_cast<VSTPluginWindow*> (getActiveEditor()))
3448 return editor->updateSizeFromEditor (w, h);
3449 #endif
3450
3451 return false;
3452}
3453
3454//==============================================================================
3455// entry point for all callbacks from the plugin
3456static pointer_sized_int VSTCALLBACK audioMaster (Vst2::AEffect* effect, int32 opcode, int32 index, pointer_sized_int value, void* ptr, float opt)
3457{
3458 if (effect != nullptr)
3459 if (auto* instance = (VSTPluginInstance*) (effect->resvd2))
3460 return instance->handleCallback (opcode, index, value, ptr, opt);
3461
3462 return VSTPluginInstance::handleGeneralCallback (opcode, index, value, ptr, opt);
3463}
3464
3465//==============================================================================
3466VSTPluginFormat::VSTPluginFormat() {}
3467VSTPluginFormat::~VSTPluginFormat() {}
3468
3469static std::unique_ptr<VSTPluginInstance> createAndUpdateDesc (VSTPluginFormat& format, PluginDescription& desc)
3470{
3471 if (auto p = format.createInstanceFromDescription (desc, 44100.0, 512))
3472 {
3473 if (auto instance = dynamic_cast<VSTPluginInstance*> (p.release()))
3474 {
3475 #if JUCE_MAC
3476 if (instance->vstModule->resFileId != 0)
3477 UseResFile (instance->vstModule->resFileId);
3478 #endif
3479
3480 instance->fillInPluginDescription (desc);
3481 return std::unique_ptr<VSTPluginInstance> (instance);
3482 }
3483
3485 }
3486
3487 return {};
3488}
3489
3491 const String& fileOrIdentifier)
3492{
3493 if (! fileMightContainThisPluginType (fileOrIdentifier))
3494 return;
3495
3496 PluginDescription desc;
3497 desc.fileOrIdentifier = fileOrIdentifier;
3498 desc.uniqueId = desc.deprecatedUid = 0;
3499
3500 auto instance = createAndUpdateDesc (*this, desc);
3501
3502 if (instance == nullptr)
3503 return;
3504
3505 if (instance->getVstCategory() != Vst2::kPlugCategShell)
3506 {
3507 // Normal plugin...
3508 results.add (new PluginDescription (desc));
3509
3510 instance->dispatch (Vst2::effOpen, 0, 0, nullptr, 0);
3511 }
3512 else
3513 {
3514 // It's a shell plugin, so iterate all the subtypes...
3515 for (;;)
3516 {
3517 char shellEffectName [256] = { 0 };
3518 auto uid = (int) instance->dispatch (Vst2::effShellGetNextPlugin, 0, 0, shellEffectName, 0);
3519
3520 if (uid == 0)
3521 break;
3522
3523 desc.uniqueId = desc.deprecatedUid = uid;
3524 desc.name = shellEffectName;
3525
3526 aboutToScanVSTShellPlugin (desc);
3527
3529
3530 if (shellInstance != nullptr)
3531 {
3532 jassert (desc.deprecatedUid == uid);
3533 jassert (desc.uniqueId == uid);
3534 desc.hasSharedContainer = true;
3535 desc.name = shellEffectName;
3536
3537 if (! arrayContainsPlugin (results, desc))
3538 results.add (new PluginDescription (desc));
3539 }
3540 }
3541 }
3542}
3543
3544void VSTPluginFormat::createPluginInstance (const PluginDescription& desc,
3545 double sampleRate, int blockSize,
3546 PluginCreationCallback callback)
3547{
3549
3550 if (fileMightContainThisPluginType (desc.fileOrIdentifier))
3551 {
3552 File file (desc.fileOrIdentifier);
3553
3555 file.getParentDirectory().setAsCurrentWorkingDirectory();
3556
3557 if (auto module = ModuleHandle::findOrCreateModule (file))
3558 {
3559 shellUIDToCreate = desc.uniqueId != 0 ? desc.uniqueId : desc.deprecatedUid;
3560
3561 result.reset (VSTPluginInstance::create (module, sampleRate, blockSize));
3562
3563 if (result != nullptr && ! result->initialiseEffect (sampleRate, blockSize))
3564 result.reset();
3565 }
3566
3567 previousWorkingDirectory.setAsCurrentWorkingDirectory();
3568 }
3569
3570 String errorMsg;
3571
3572 if (result == nullptr)
3573 errorMsg = TRANS ("Unable to load XXX plug-in file").replace ("XXX", "VST-2");
3574
3575 callback (std::move (result), errorMsg);
3576}
3577
3578bool VSTPluginFormat::requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const
3579{
3580 return false;
3581}
3582
3583bool VSTPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier)
3584{
3585 auto f = File::createFileWithoutCheckingPath (fileOrIdentifier);
3586
3587 #if JUCE_MAC || JUCE_IOS
3588 return f.isDirectory() && f.hasFileExtension (".vst");
3589 #elif JUCE_WINDOWS
3590 return f.existsAsFile() && f.hasFileExtension (".dll");
3591 #elif JUCE_LINUX || JUCE_BSD || JUCE_ANDROID
3592 return f.existsAsFile() && f.hasFileExtension (".so");
3593 #endif
3594}
3595
3596String VSTPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier)
3597{
3598 return fileOrIdentifier;
3599}
3600
3601bool VSTPluginFormat::pluginNeedsRescanning (const PluginDescription& desc)
3602{
3603 return File (desc.fileOrIdentifier).getLastModificationTime() != desc.lastFileModTime;
3604}
3605
3606bool VSTPluginFormat::doesPluginStillExist (const PluginDescription& desc)
3607{
3608 return File (desc.fileOrIdentifier).exists();
3609}
3610
3611StringArray VSTPluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive, bool)
3612{
3613 StringArray results;
3614
3615 for (int j = 0; j < directoriesToSearch.getNumPaths(); ++j)
3616 recursiveFileSearch (results, directoriesToSearch [j], recursive);
3617
3618 return results;
3619}
3620
3621void VSTPluginFormat::recursiveFileSearch (StringArray& results, const File& dir, const bool recursive)
3622{
3623 // avoid allowing the dir iterator to be recursive, because we want to avoid letting it delve inside
3624 // .component or .vst directories.
3625 for (const auto& iter : RangedDirectoryIterator (dir, false, "*", File::findFilesAndDirectories))
3626 {
3627 auto f = iter.getFile();
3628 bool isPlugin = false;
3629
3630 if (fileMightContainThisPluginType (f.getFullPathName()))
3631 {
3632 isPlugin = true;
3633 results.add (f.getFullPathName());
3634 }
3635
3636 if (recursive && (! isPlugin) && f.isDirectory())
3637 recursiveFileSearch (results, f, true);
3638 }
3639}
3640
3642{
3643 #if JUCE_MAC
3644 return FileSearchPath ("~/Library/Audio/Plug-Ins/VST;/Library/Audio/Plug-Ins/VST");
3645 #elif JUCE_LINUX || JUCE_BSD || JUCE_ANDROID
3646 return FileSearchPath (SystemStats::getEnvironmentVariable ("VST_PATH",
3647 "/usr/lib/vst;/usr/local/lib/vst;~/.vst")
3648 .replace (":", ";"));
3649 #elif JUCE_WINDOWS
3651
3652 FileSearchPath paths;
3653 paths.add (WindowsRegistry::getValue ("HKEY_LOCAL_MACHINE\\Software\\VST\\VSTPluginsPath"));
3654 paths.addIfNotAlreadyThere (programFiles + "\\Steinberg\\VstPlugins");
3655 paths.addIfNotAlreadyThere (programFiles + "\\VstPlugins");
3656 paths.removeRedundantPaths();
3657 return paths;
3658 #elif JUCE_IOS
3659 // on iOS you can only load plug-ins inside the hosts bundle folder
3662
3664 FileSearchPath retval (String (CFStringGetCStringPtr (path.get(), kCFStringEncodingUTF8)));
3665 return retval;
3666 #endif
3667}
3668
3669const XmlElement* VSTPluginFormat::getVSTXML (AudioPluginInstance* plugin)
3670{
3671 if (auto* vst = dynamic_cast<VSTPluginInstance*> (plugin))
3672 if (vst->vstModule != nullptr)
3673 return vst->vstModule->vstXml.get();
3674
3675 return nullptr;
3676}
3677
3678bool VSTPluginFormat::loadFromFXBFile (AudioPluginInstance* plugin, const void* data, size_t dataSize)
3679{
3680 if (auto* vst = dynamic_cast<VSTPluginInstance*> (plugin))
3681 return vst->loadFromFXBFile (data, dataSize);
3682
3683 return false;
3684}
3685
3686bool VSTPluginFormat::saveToFXBFile (AudioPluginInstance* plugin, MemoryBlock& dest, bool asFXB)
3687{
3688 if (auto* vst = dynamic_cast<VSTPluginInstance*> (plugin))
3689 return vst->saveToFXBFile (dest, asFXB);
3690
3691 return false;
3692}
3693
3694bool VSTPluginFormat::getChunkData (AudioPluginInstance* plugin, MemoryBlock& result, bool isPreset)
3695{
3696 if (auto* vst = dynamic_cast<VSTPluginInstance*> (plugin))
3697 return vst->getChunkData (result, isPreset, 128);
3698
3699 return false;
3700}
3701
3702bool VSTPluginFormat::setChunkData (AudioPluginInstance* plugin, const void* data, int size, bool isPreset)
3703{
3704 if (auto* vst = dynamic_cast<VSTPluginInstance*> (plugin))
3705 return vst->setChunkData (data, size, isPreset);
3706
3707 return false;
3708}
3709
3712{
3713 ModuleHandle::Ptr module = new ModuleHandle (File(), (MainCall) entryPointFunction);
3714
3715 if (module->open())
3716 {
3718
3719 if (result != nullptr)
3720 if (result->initialiseEffect (initialSampleRate, initialBufferSize))
3721 return result.release();
3722 }
3723
3724 return nullptr;
3725}
3726
3727void VSTPluginFormat::setExtraFunctions (AudioPluginInstance* plugin, ExtraFunctions* functions)
3728{
3729 std::unique_ptr<ExtraFunctions> f (functions);
3730
3731 if (auto* vst = dynamic_cast<VSTPluginInstance*> (plugin))
3732 std::swap (vst->extraFunctions, f);
3733}
3734
3736{
3737 if (auto* vstAEffect = reinterpret_cast<Vst2::AEffect*> (aEffect))
3738 if (auto* instanceVST = reinterpret_cast<VSTPluginInstance*> (vstAEffect->resvd2))
3739 return dynamic_cast<AudioPluginInstance*> (instanceVST);
3740
3741 return nullptr;
3742}
3743
3744pointer_sized_int JUCE_CALLTYPE VSTPluginFormat::dispatcher (AudioPluginInstance* plugin, int32 opcode, int32 index, pointer_sized_int value, void* ptr, float opt)
3745{
3746 if (auto* vst = dynamic_cast<VSTPluginInstance*> (plugin))
3747 return vst->dispatch (opcode, index, value, ptr, opt);
3748
3749 return {};
3750}
3751
3752void VSTPluginFormat::aboutToScanVSTShellPlugin (const PluginDescription&) {}
3753
3754} // namespace juce
3755
3756JUCE_END_IGNORE_WARNINGS_GCC_LIKE
3757JUCE_END_IGNORE_WARNINGS_MSVC
3758
3759#endif
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:56
static AudioChannelSet JUCE_CALLTYPE mono()
Creates a one-channel mono set (centre).
static AudioChannelSet JUCE_CALLTYPE stereo()
Creates a set containing a stereo set (left, right).
static AudioChannelSet JUCE_CALLTYPE canonicalChannelSet(int numChannels)
Create a canonical channel set for a given number of channels.
virtual void processBlockBypassed(AudioBuffer< float > &buffer, MidiBuffer &midiMessages)
Renders the next block when the processor is being bypassed.
static int getDefaultNumParameterSteps() noexcept
Returns the default number of steps for a parameter.
static constexpr uint32 bigEndianInt(const void *bytes) noexcept
Turns 4 bytes into a big-endian integer.
static constexpr uint32 littleEndianInt(const void *bytes) noexcept
Turns 4 bytes into a little-endian integer.
static Type swapIfLittleEndian(Type value) noexcept
Swaps the byte order of a signed or unsigned integer if the CPU is little-endian.
static constexpr uint16 swap(uint16 value) noexcept
Swaps the upper and lower bytes of a 16-bit integer.
static int getNumPeers() noexcept
Returns the number of currently-active peers.
static ComponentPeer * getPeer(int index) noexcept
Returns one of the currently-active peers.
const String & getFullPathName() const noexcept
Returns the complete, absolute path of this file.
Definition juce_File.h:153
static File getCurrentWorkingDirectory()
Returns the current working directory.
@ globalApplicationsDirectory
The directory in which applications normally get installed.
Definition juce_File.h:963
@ findFiles
Use this flag to indicate that you want to find files.
Definition juce_File.h:566
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 File createFileWithoutCheckingPath(const String &absolutePath) noexcept
Creates a file that simply contains this string, without doing the sanity-checking that the normal co...
Definition juce_File.cpp:31
static JUCEApplicationBase * getInstance() noexcept
Returns the global instance of the application object that's running.
Represents a key press, including any modifier keys that are needed.
static MessageManager * getInstance()
Returns the global instance of the MessageManager.
An array designed for holding objects.
ObjectClass * add(ObjectClass *newObject)
Appends a new object to the end of the array.
static Random & getSystemRandom() noexcept
The overhead of creating a new Random object is fairly small, but if you want to avoid it,...
A special array for holding a list of strings.
int size() const noexcept
Returns the number of strings in the array.
void add(String stringToAdd)
Appends a string at the end of the array.
int addTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
Breaks up a string into tokens and adds them to this array.
A simple class for holding temporary references to a string literal or String.
The JUCE String class!
Definition juce_String.h:53
String upToFirstOccurrenceOf(StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const
Returns the start of this string, up to the first occurrence of a substring.
String trim() const
Returns a copy of this string with any whitespace characters removed from the start and end.
bool endsWithChar(juce_wchar character) const noexcept
Tests whether the string ends with a particular character.
float getFloatValue() const noexcept
Parses this string as a floating point number.
bool startsWithChar(juce_wchar character) const noexcept
Tests whether the string begins with a particular character.
String removeCharacters(StringRef charactersToRemove) const
Returns a version of this string with a set of characters removed.
static String createStringFromData(const void *data, int size)
Creates a string from data in an unknown format.
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
Creates a String from a UTF-8 encoded buffer.
int getIntValue() const noexcept
Reads the value of the string as a decimal number (up to 32 bits in size).
static String getEnvironmentVariable(const String &name, const String &defaultValue)
Returns an environment variable.
static Time JUCE_CALLTYPE getCurrentTime() noexcept
Returns a Time object that is set to the current system time.
static void JUCE_CALLTYPE callPendingTimersSynchronously()
For internal use only: invokes any timers that need callbacks.
static AudioPluginInstance * getPluginInstanceFromVstEffectInterface(void *aEffect)
Given a VstEffectInterface* (aka vst::AEffect*), this will return the juce AudioPluginInstance that i...
bool fileMightContainThisPluginType(const String &fileOrIdentifier) override
Should do a quick check to see if this file or directory might be a plugin of this format.
bool doesPluginStillExist(const PluginDescription &) override
Checks whether this plugin could possibly be loaded.
static bool getChunkData(AudioPluginInstance *plugin, MemoryBlock &result, bool isPreset)
Attempts to get a VST's state as a chunk of memory.
static const XmlElement * getVSTXML(AudioPluginInstance *plugin)
Attempts to retrieve the VSTXML data from a plugin.
StringArray searchPathsForPlugins(const FileSearchPath &, bool recursive, bool) override
Searches a suggested set of directories for any plugins in this format.
static pointer_sized_int JUCE_CALLTYPE dispatcher(AudioPluginInstance *, int32, int32, pointer_sized_int, void *, float)
This simply calls directly to the VST's AEffect::dispatcher() function.
static bool setChunkData(AudioPluginInstance *plugin, const void *data, int size, bool isPreset)
Attempts to set a VST's state from a chunk of memory.
String getNameOfPluginFromIdentifier(const String &fileOrIdentifier) override
Returns a readable version of the name of the plugin that this identifier refers to.
static AudioPluginInstance * createCustomVSTFromMainCall(void *entryPointFunction, double initialSampleRate, int initialBufferSize)
Given a suitable function pointer to a VSTPluginMain function, this will attempt to instantiate and r...
void findAllTypesForFile(OwnedArray< PluginDescription > &, const String &fileOrIdentifier) override
This tries to create descriptions for all the plugin types available in a binary module file.
static void setExtraFunctions(AudioPluginInstance *plugin, ExtraFunctions *functions)
Provides an ExtraFunctions callback object for a plugin to use.
bool pluginNeedsRescanning(const PluginDescription &) override
Returns true if this plugin's version or date has changed and it should be re-checked.
static bool saveToFXBFile(AudioPluginInstance *plugin, MemoryBlock &result, bool asFXB)
Attempts to save a VST's state to some FXP or FXB data.
static bool loadFromFXBFile(AudioPluginInstance *plugin, const void *data, size_t dataSize)
Attempts to reload a VST plugin's state from some FXB or FXP data.
virtual void aboutToScanVSTShellPlugin(const PluginDescription &)
Can be overridden to receive a callback when each member of a shell plugin is about to be tested duri...
FileSearchPath getDefaultLocationsToSearch() override
Returns the typical places to look for this kind of plugin.
static String JUCE_CALLTYPE getValue(const String &regValuePath, const String &defaultValue=String(), WoW64Mode mode=WoW64_Default)
Returns a string from the registry.
Used to build a tree of elements representing an XML document.
int getNumChildElements() const noexcept
Returns the number of sub-elements in this element.
XmlElement * getChildByName(StringRef tagNameToLookFor) const noexcept
Returns the first sub-element with a given tag-name.
double getDoubleAttribute(StringRef attributeName, double defaultReturnValue=0.0) const
Returns the value of a named attribute as floating-point.
bool hasAttribute(StringRef attributeName) const noexcept
Checks whether the element contains an attribute with a certain name.
bool hasTagName(StringRef possibleTagName) const noexcept
Tests whether this element has a particular tag name.
int getIntAttribute(StringRef attributeName, int defaultReturnValue=0) const
Returns the value of a named attribute as an integer.
const String & getStringAttribute(StringRef attributeName) const noexcept
Returns the value of a named attribute.
close
T copy(T... args)
T data(T... args)
T format(T... args)
gettimeofday
T infinity(T... args)
#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...
#define jassert(expression)
Platform-independent assertion macro.
#define DBG(textToWrite)
Writes a string to the standard error stream.
#define JUCE_DECLARE_NON_COPYABLE(className)
This is a shorthand macro for deleting a class's copy constructor and copy assignment operator.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
#define jassertfalse
This will always cause an assertion failure.
#define JUCE_CALLTYPE
This macro defines the C calling convention used as the standard for JUCE calls.
typedef int
T lock(T... args)
typedef float
JUCE Namespace.
CriticalSection::ScopedLockType ScopedLock
Automatically locks and unlocks a CriticalSection object.
int pointer_sized_int
A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it.
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
Returns true if the two floating-point numbers are approximately equal.
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.
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.
int getAddressDifference(Type1 *pointer1, Type2 *pointer2) noexcept
A handy function which returns the difference between any two pointers, in bytes.
Definition juce_Memory.h:54
std::unique_ptr< XmlElement > parseXML(const String &textToParse)
Attempts to parse some XML text, returning a new XmlElement if it was valid.
void ignoreUnused(Types &&...) noexcept
Handy function for avoiding unused variables warning.
unsigned int pointer_sized_uint
An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it...
Type * addBytesToPointer(Type *basePointer, IntegerType bytes) noexcept
A handy function which adds a number of bytes to any type of pointer and returns the result.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Returns true if a value is at least zero, and also below a specified upper limit.
unsigned int uint32
A platform-independent 32-bit unsigned integer type.
constexpr int numElementsInArray(Type(&)[N]) noexcept
Handy function for getting the number of elements in a simple const C array.
long long int64
A platform-independent 64-bit integer type.
open
T ref(T... args)
T reset(T... args)
T size(T... args)
T swap(T... args)
typedef size_t