1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - 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 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-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 #include <juce_core/system/juce_CompilerWarnings.h>
27 #include <juce_core/system/juce_TargetPlatform.h>
28 #include "../utility/juce_CheckSettingMacros.h"
29 
30 #if JucePlugin_Build_VST
31 
32 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996 4100)
33 
34 #include "../utility/juce_IncludeSystemHeaders.h"
35 #include <juce_core/juce_core.h>
36 
37 #if JucePlugin_VersionCode < 0x010000   // Major < 0
38 
39  #if (JucePlugin_VersionCode & 0x00FF00) > (9 * 0x100) // check if Minor number exceeds 9
40   JUCE_COMPILER_WARNING ("When version has 'major' = 0, VST2 has trouble displaying 'minor' exceeding 9")
41  #endif
42 
43  #if (JucePlugin_VersionCode & 0xFF) > 9   // check if Bugfix number exceeds 9
44   JUCE_COMPILER_WARNING ("When version has 'major' = 0, VST2 has trouble displaying 'bugfix' exceeding 9")
45  #endif
46 
47 #elif JucePlugin_VersionCode >= 0x650000   // Major >= 101
48 
49  #if (JucePlugin_VersionCode & 0x00FF00) > (99 * 0x100) // check if Minor number exceeds 99
50   JUCE_COMPILER_WARNING ("When version has 'major' > 100, VST2 has trouble displaying 'minor' exceeding 99")
51  #endif
52 
53  #if (JucePlugin_VersionCode & 0xFF) > 99  // check if Bugfix number exceeds 99
54   JUCE_COMPILER_WARNING ("When version has 'major' > 100, VST2 has trouble displaying 'bugfix' exceeding 99")
55  #endif
56 
57 #endif
58 
59 #ifdef PRAGMA_ALIGN_SUPPORTED
60  #undef PRAGMA_ALIGN_SUPPORTED
61  #define PRAGMA_ALIGN_SUPPORTED 1
62 #endif
63 
64 #if ! JUCE_MSVC
65  #define __cdecl
66 #endif
67 
68 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wconversion",
69                                      "-Wshadow",
70                                      "-Wdeprecated-register",
71                                      "-Wunused-parameter",
72                                      "-Wdeprecated-writable-strings",
73                                      "-Wnon-virtual-dtor",
74                                      "-Wzero-as-null-pointer-constant")
75 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4458)
76 
77 #define VST_FORCE_DEPRECATED 0
78 
79 namespace Vst2
80 {
81 // If the following files cannot be found then you are probably trying to build
82 // a VST2 plug-in or a VST2-compatible VST3 plug-in. To do this you must have a
83 // VST2 SDK in your header search paths or use the "VST (Legacy) SDK Folder"
84 // field in the Projucer. The VST2 SDK can be obtained from the
85 // vstsdk3610_11_06_2018_build_37 (or older) VST3 SDK or JUCE version 5.3.2. You
86 // also need a VST2 license from Steinberg to distribute VST2 plug-ins.
87 #include "pluginterfaces/vst2.x/aeffect.h"
88 #include "pluginterfaces/vst2.x/aeffectx.h"
89 }
90 
91 JUCE_END_IGNORE_WARNINGS_MSVC
92 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
93 
94 //==============================================================================
95 #if JUCE_MSVC
96  #pragma pack (push, 8)
97 #endif
98 
99 #define JUCE_VSTINTERFACE_H_INCLUDED 1
100 #define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1
101 
102 #include "../utility/juce_IncludeModuleHeaders.h"
103 
104 using namespace juce;
105 
106 #include "../utility/juce_FakeMouseMoveGenerator.h"
107 #include "../utility/juce_WindowsHooks.h"
108 
109 #include <juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp>
110 #include <juce_audio_processors/format_types/juce_VSTCommon.h>
111 
112 #ifdef JUCE_MSVC
113  #pragma pack (pop)
114 #endif
115 
116 #undef MemoryBlock
117 
118 class JuceVSTWrapper;
119 static bool recursionCheck = false;
120 
121 namespace juce
122 {
123  #if JUCE_MAC
124   extern JUCE_API void initialiseMacVST();
125   extern JUCE_API void* attachComponentToWindowRefVST (Component*, void* parent, bool isNSView);
126   extern JUCE_API void detachComponentFromWindowRefVST (Component*, void* window, bool isNSView);
127   extern JUCE_API void setNativeHostWindowSizeVST (void* window, Component*, int newWidth, int newHeight, bool isNSView);
128   extern JUCE_API void checkWindowVisibilityVST (void* window, Component*, bool isNSView);
129   extern JUCE_API bool forwardCurrentKeyEventToHostVST (Component*, bool isNSView);
130  #if ! JUCE_64BIT
131   extern JUCE_API void updateEditorCompBoundsVST (Component*);
132  #endif
133  #endif
134 
135 #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
136   extern JUCE_API double getScaleFactorForWindow (HWND);
137 #endif
138 
139   extern JUCE_API bool handleManufacturerSpecificVST2Opcode (int32, pointer_sized_int, void*, float);
140 }
141 
142 
143 //==============================================================================
144 #if JUCE_WINDOWS
145 
146 namespace
147 {
148     // Returns the actual container window, unlike GetParent, which can also return a separate owner window.
getWindowParent(HWND w)149     static HWND getWindowParent (HWND w) noexcept    { return GetAncestor (w, GA_PARENT); }
150 
findMDIParentOf(HWND w)151     static HWND findMDIParentOf (HWND w)
152     {
153         const int frameThickness = GetSystemMetrics (SM_CYFIXEDFRAME);
154 
155         while (w != 0)
156         {
157             auto parent = getWindowParent (w);
158 
159             if (parent == 0)
160                 break;
161 
162             TCHAR windowType[32] = { 0 };
163             GetClassName (parent, windowType, 31);
164 
165             if (String (windowType).equalsIgnoreCase ("MDIClient"))
166                 return parent;
167 
168             RECT windowPos, parentPos;
169             GetWindowRect (w, &windowPos);
170             GetWindowRect (parent, &parentPos);
171 
172             auto dw = (parentPos.right - parentPos.left) - (windowPos.right - windowPos.left);
173             auto dh = (parentPos.bottom - parentPos.top) - (windowPos.bottom - windowPos.top);
174 
175             if (dw > 100 || dh > 100)
176                 break;
177 
178             w = parent;
179 
180             if (dw == 2 * frameThickness)
181                 break;
182         }
183 
184         return w;
185     }
186 
187     static bool messageThreadIsDefinitelyCorrect = false;
188 }
189 
190 //==============================================================================
191 #elif JUCE_LINUX || JUCE_BSD
192 
193 struct SharedMessageThread  : public Thread
194 {
SharedMessageThreadSharedMessageThread195     SharedMessageThread()  : Thread ("VstMessageThread")
196     {
197         startThread (7);
198 
199         while (! initialised)
200             sleep (1);
201     }
202 
~SharedMessageThreadSharedMessageThread203     ~SharedMessageThread() override
204     {
205         signalThreadShouldExit();
206         JUCEApplicationBase::quit();
207         waitForThreadToExit (5000);
208         clearSingletonInstance();
209     }
210 
runSharedMessageThread211     void run() override
212     {
213         initialiseJuce_GUI();
214         initialised = true;
215 
216         MessageManager::getInstance()->setCurrentThreadAsMessageThread();
217 
218         XWindowSystem::getInstance();
219 
220         while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250))
221         {}
222     }
223 
224     JUCE_DECLARE_SINGLETON (SharedMessageThread, false)
225 
226     bool initialised = false;
227 };
228 
229 JUCE_IMPLEMENT_SINGLETON (SharedMessageThread)
230 
231 #endif
232 
233 static Array<void*> activePlugins;
234 
235 //==============================================================================
236 // Ableton Live host specific commands
237 struct AbletonLiveHostSpecific
238 {
239     enum
240     {
241         KCantBeSuspended = (1 << 2)
242     };
243 
244     uint32 magic;        // 'AbLi'
245     int cmd;             // 5 = realtime properties
246     size_t commandSize;  // sizeof (int)
247     int flags;           // KCantBeSuspended = (1 << 2)
248 };
249 
250 //==============================================================================
251 /**
252     This is an AudioEffectX object that holds and wraps our AudioProcessor...
253 */
254 class JuceVSTWrapper  : public AudioProcessorListener,
255                         public AudioPlayHead,
256                         private Timer,
257                         private AudioProcessorParameter::Listener
258 {
259 private:
260     //==============================================================================
261     template <typename FloatType>
262     struct VstTempBuffers
263     {
VstTempBuffersJuceVSTWrapper::VstTempBuffers264         VstTempBuffers() {}
~VstTempBuffersJuceVSTWrapper::VstTempBuffers265         ~VstTempBuffers() { release(); }
266 
releaseJuceVSTWrapper::VstTempBuffers267         void release() noexcept
268         {
269             for (auto* c : tempChannels)
270                 delete[] c;
271 
272             tempChannels.clear();
273         }
274 
275         HeapBlock<FloatType*> channels;
276         Array<FloatType*> tempChannels;  // see note in processReplacing()
277         juce::AudioBuffer<FloatType> processTempBuffer;
278     };
279 
280     /** Use the same names as the VST SDK. */
281     struct VstOpCodeArguments
282     {
283         int32 index;
284         pointer_sized_int value;
285         void* ptr;
286         float opt;
287     };
288 
289 public:
290     //==============================================================================
JuceVSTWrapper(Vst2::audioMasterCallback cb,AudioProcessor * af)291     JuceVSTWrapper (Vst2::audioMasterCallback cb, AudioProcessor* af)
292        : hostCallback (cb),
293          processor (af)
294     {
295         inParameterChangedCallback = false;
296 
297         // VST-2 does not support disabling buses: so always enable all of them
298         processor->enableAllBuses();
299 
300         findMaxTotalChannels (maxNumInChannels, maxNumOutChannels);
301 
302         // You must at least have some channels
303         jassert (processor->isMidiEffect() || (maxNumInChannels > 0 || maxNumOutChannels > 0));
304 
305         if (processor->isMidiEffect())
306             maxNumInChannels = maxNumOutChannels = 2;
307 
308        #ifdef JucePlugin_PreferredChannelConfigurations
309         processor->setPlayConfigDetails (maxNumInChannels, maxNumOutChannels, 44100.0, 1024);
310        #endif
311 
312         processor->setRateAndBufferSizeDetails (0, 0);
313         processor->setPlayHead (this);
314         processor->addListener (this);
315 
316         if (auto* juceParam = processor->getBypassParameter())
317             juceParam->addListener (this);
318 
319         juceParameters.update (*processor, false);
320 
321         memset (&vstEffect, 0, sizeof (vstEffect));
322         vstEffect.magic = 0x56737450 /* 'VstP' */;
323         vstEffect.dispatcher = (Vst2::AEffectDispatcherProc) dispatcherCB;
324         vstEffect.process = nullptr;
325         vstEffect.setParameter = (Vst2::AEffectSetParameterProc) setParameterCB;
326         vstEffect.getParameter = (Vst2::AEffectGetParameterProc) getParameterCB;
327         vstEffect.numPrograms = jmax (1, af->getNumPrograms());
328         vstEffect.numParams = juceParameters.getNumParameters();
329         vstEffect.numInputs = maxNumInChannels;
330         vstEffect.numOutputs = maxNumOutChannels;
331         vstEffect.initialDelay = processor->getLatencySamples();
332         vstEffect.object = this;
333         vstEffect.uniqueID = JucePlugin_VSTUniqueID;
334 
335        #ifdef JucePlugin_VSTChunkStructureVersion
336         vstEffect.version = JucePlugin_VSTChunkStructureVersion;
337        #else
338         vstEffect.version = JucePlugin_VersionCode;
339        #endif
340 
341         vstEffect.processReplacing = (Vst2::AEffectProcessProc) processReplacingCB;
342         vstEffect.processDoubleReplacing = (Vst2::AEffectProcessDoubleProc) processDoubleReplacingCB;
343 
344         vstEffect.flags |= Vst2::effFlagsHasEditor;
345 
346         vstEffect.flags |= Vst2::effFlagsCanReplacing;
347         if (processor->supportsDoublePrecisionProcessing())
348             vstEffect.flags |= Vst2::effFlagsCanDoubleReplacing;
349 
350         vstEffect.flags |= Vst2::effFlagsProgramChunks;
351 
352        #if JucePlugin_IsSynth
353         vstEffect.flags |= Vst2::effFlagsIsSynth;
354        #else
355         if (processor->getTailLengthSeconds() == 0.0)
356             vstEffect.flags |= Vst2::effFlagsNoSoundInStop;
357        #endif
358 
359         activePlugins.add (this);
360     }
361 
~JuceVSTWrapper()362     ~JuceVSTWrapper() override
363     {
364         JUCE_AUTORELEASEPOOL
365         {
366             {
367                #if JUCE_LINUX || JUCE_BSD
368                 MessageManagerLock mmLock;
369                #endif
370                 stopTimer();
371                 deleteEditor (false);
372 
373                 hasShutdown = true;
374 
375                 delete processor;
376                 processor = nullptr;
377 
378                 jassert (editorComp == nullptr);
379 
380                 deleteTempChannels();
381 
382                 jassert (activePlugins.contains (this));
383                 activePlugins.removeFirstMatchingValue (this);
384             }
385 
386             if (activePlugins.size() == 0)
387             {
388                #if JUCE_LINUX || JUCE_BSD
389                 SharedMessageThread::deleteInstance();
390                #endif
391                 shutdownJuce_GUI();
392 
393                #if JUCE_WINDOWS
394                 messageThreadIsDefinitelyCorrect = false;
395                #endif
396             }
397         }
398     }
399 
getAEffect()400     Vst2::AEffect* getAEffect() noexcept    { return &vstEffect; }
401 
402     template <typename FloatType>
internalProcessReplacing(FloatType ** inputs,FloatType ** outputs,int32 numSamples,VstTempBuffers<FloatType> & tmpBuffers)403     void internalProcessReplacing (FloatType** inputs, FloatType** outputs,
404                                    int32 numSamples, VstTempBuffers<FloatType>& tmpBuffers)
405     {
406         const bool isMidiEffect = processor->isMidiEffect();
407 
408         if (firstProcessCallback)
409         {
410             firstProcessCallback = false;
411 
412             // if this fails, the host hasn't called resume() before processing
413             jassert (isProcessing);
414 
415             // (tragically, some hosts actually need this, although it's stupid to have
416             //  to do it here..)
417             if (! isProcessing)
418                 resume();
419 
420             processor->setNonRealtime (isProcessLevelOffline());
421 
422            #if JUCE_WINDOWS
423             if (getHostType().isWavelab())
424             {
425                 int priority = GetThreadPriority (GetCurrentThread());
426 
427                 if (priority <= THREAD_PRIORITY_NORMAL && priority >= THREAD_PRIORITY_LOWEST)
428                     processor->setNonRealtime (true);
429             }
430            #endif
431         }
432 
433        #if JUCE_DEBUG && ! (JucePlugin_ProducesMidiOutput || JucePlugin_IsMidiEffect)
434         const int numMidiEventsComingIn = midiEvents.getNumEvents();
435        #endif
436 
437         jassert (activePlugins.contains (this));
438 
439         {
440             const int numIn  = processor->getTotalNumInputChannels();
441             const int numOut = processor->getTotalNumOutputChannels();
442 
443             const ScopedLock sl (processor->getCallbackLock());
444 
445             if (processor->isSuspended())
446             {
447                 for (int i = 0; i < numOut; ++i)
448                     if (outputs[i] != nullptr)
449                         FloatVectorOperations::clear (outputs[i], numSamples);
450             }
451             else
452             {
453                 int i;
454                 for (i = 0; i < numOut; ++i)
455                 {
456                     auto* chan = tmpBuffers.tempChannels.getUnchecked(i);
457 
458                     if (chan == nullptr)
459                     {
460                         chan = outputs[i];
461 
462                         bool bufferPointerReusedForOtherChannels = false;
463 
464                         for (int j = i; --j >= 0;)
465                         {
466                             if (outputs[j] == chan)
467                             {
468                                 bufferPointerReusedForOtherChannels = true;
469                                 break;
470                             }
471                         }
472 
473                         // if some output channels are disabled, some hosts supply the same buffer
474                         // for multiple channels or supply a nullptr - this buggers up our method
475                         // of copying the inputs over the outputs, so we need to create unique temp
476                         // buffers in this case..
477                         if (bufferPointerReusedForOtherChannels || chan == nullptr)
478                         {
479                             chan = new FloatType [(size_t) blockSize * 2];
480                             tmpBuffers.tempChannels.set (i, chan);
481                         }
482                     }
483 
484                     if (i < numIn)
485                     {
486                         if (chan != inputs[i])
487                             memcpy (chan, inputs[i], (size_t) numSamples * sizeof (FloatType));
488                     }
489                     else
490                     {
491                         FloatVectorOperations::clear (chan, numSamples);
492                     }
493 
494                     tmpBuffers.channels[i] = chan;
495                 }
496 
497                 for (; i < numIn; ++i)
498                     tmpBuffers.channels[i] = inputs[i];
499 
500                 {
501                     const int numChannels = jmax (numIn, numOut);
502                     AudioBuffer<FloatType> chans (tmpBuffers.channels, isMidiEffect ? 0 : numChannels, numSamples);
503 
504                     if (isBypassed)
505                         processor->processBlockBypassed (chans, midiEvents);
506                     else
507                         processor->processBlock (chans, midiEvents);
508                 }
509 
510                 // copy back any temp channels that may have been used..
511                 for (i = 0; i < numOut; ++i)
512                     if (auto* chan = tmpBuffers.tempChannels.getUnchecked(i))
513                         if (auto* dest = outputs[i])
514                             memcpy (dest, chan, (size_t) numSamples * sizeof (FloatType));
515             }
516         }
517 
518         if (! midiEvents.isEmpty())
519         {
520            #if JucePlugin_ProducesMidiOutput || JucePlugin_IsMidiEffect
521             auto numEvents = midiEvents.getNumEvents();
522 
523             outgoingEvents.ensureSize (numEvents);
524             outgoingEvents.clear();
525 
526             for (const auto metadata : midiEvents)
527             {
528                 jassert (metadata.samplePosition >= 0 && metadata.samplePosition < numSamples);
529 
530                 outgoingEvents.addEvent (metadata.data, metadata.numBytes, metadata.samplePosition);
531             }
532 
533             // Send VST events to the host.
534             if (hostCallback != nullptr)
535                 hostCallback (&vstEffect, Vst2::audioMasterProcessEvents, 0, 0, outgoingEvents.events, 0);
536            #elif JUCE_DEBUG
537             /*  This assertion is caused when you've added some events to the
538                 midiMessages array in your processBlock() method, which usually means
539                 that you're trying to send them somewhere. But in this case they're
540                 getting thrown away.
541 
542                 If your plugin does want to send midi messages, you'll need to set
543                 the JucePlugin_ProducesMidiOutput macro to 1 in your
544                 JucePluginCharacteristics.h file.
545 
546                 If you don't want to produce any midi output, then you should clear the
547                 midiMessages array at the end of your processBlock() method, to
548                 indicate that you don't want any of the events to be passed through
549                 to the output.
550             */
551             jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn);
552            #endif
553 
554             midiEvents.clear();
555         }
556     }
557 
processReplacing(float ** inputs,float ** outputs,int32 sampleFrames)558     void processReplacing (float** inputs, float** outputs, int32 sampleFrames)
559     {
560         jassert (! processor->isUsingDoublePrecision());
561         internalProcessReplacing (inputs, outputs, sampleFrames, floatTempBuffers);
562     }
563 
processReplacingCB(Vst2::AEffect * vstInterface,float ** inputs,float ** outputs,int32 sampleFrames)564     static void processReplacingCB (Vst2::AEffect* vstInterface, float** inputs, float** outputs, int32 sampleFrames)
565     {
566         getWrapper (vstInterface)->processReplacing (inputs, outputs, sampleFrames);
567     }
568 
processDoubleReplacing(double ** inputs,double ** outputs,int32 sampleFrames)569     void processDoubleReplacing (double** inputs, double** outputs, int32 sampleFrames)
570     {
571         jassert (processor->isUsingDoublePrecision());
572         internalProcessReplacing (inputs, outputs, sampleFrames, doubleTempBuffers);
573     }
574 
processDoubleReplacingCB(Vst2::AEffect * vstInterface,double ** inputs,double ** outputs,int32 sampleFrames)575     static void processDoubleReplacingCB (Vst2::AEffect* vstInterface, double** inputs, double** outputs, int32 sampleFrames)
576     {
577         getWrapper (vstInterface)->processDoubleReplacing (inputs, outputs, sampleFrames);
578     }
579 
580     //==============================================================================
resume()581     void resume()
582     {
583         if (processor != nullptr)
584         {
585             isProcessing = true;
586 
587             auto numInAndOutChannels = static_cast<size_t> (vstEffect.numInputs + vstEffect.numOutputs);
588             floatTempBuffers .channels.calloc (numInAndOutChannels);
589             doubleTempBuffers.channels.calloc (numInAndOutChannels);
590 
591             auto currentRate = sampleRate;
592             auto currentBlockSize = blockSize;
593 
594             firstProcessCallback = true;
595 
596             processor->setNonRealtime (isProcessLevelOffline());
597             processor->setRateAndBufferSizeDetails (currentRate, currentBlockSize);
598 
599             deleteTempChannels();
600 
601             processor->prepareToPlay (currentRate, currentBlockSize);
602 
603             midiEvents.ensureSize (2048);
604             midiEvents.clear();
605 
606             vstEffect.initialDelay = processor->getLatencySamples();
607 
608             /** If this plug-in is a synth or it can receive midi events we need to tell the
609                 host that we want midi. In the SDK this method is marked as deprecated, but
610                 some hosts rely on this behaviour.
611             */
612             if (vstEffect.flags & Vst2::effFlagsIsSynth || JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect)
613             {
614                 if (hostCallback != nullptr)
615                     hostCallback (&vstEffect, Vst2::audioMasterWantMidi, 0, 1, nullptr, 0);
616             }
617 
618             if (getHostType().isAbletonLive()
619                  && hostCallback != nullptr
620                  && processor->getTailLengthSeconds() == std::numeric_limits<double>::infinity())
621             {
622                 AbletonLiveHostSpecific hostCmd;
623 
624                 hostCmd.magic = 0x41624c69; // 'AbLi'
625                 hostCmd.cmd = 5;
626                 hostCmd.commandSize = sizeof (int);
627                 hostCmd.flags = AbletonLiveHostSpecific::KCantBeSuspended;
628 
629                 hostCallback (&vstEffect, Vst2::audioMasterVendorSpecific, 0, 0, &hostCmd, 0.0f);
630             }
631 
632            #if JucePlugin_ProducesMidiOutput || JucePlugin_IsMidiEffect
633             outgoingEvents.ensureSize (512);
634            #endif
635         }
636     }
637 
suspend()638     void suspend()
639     {
640         if (processor != nullptr)
641         {
642             processor->releaseResources();
643             outgoingEvents.freeEvents();
644 
645             isProcessing = false;
646             floatTempBuffers.channels.free();
647             doubleTempBuffers.channels.free();
648 
649             deleteTempChannels();
650         }
651     }
652 
653     //==============================================================================
getCurrentPosition(AudioPlayHead::CurrentPositionInfo & info)654     bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info) override
655     {
656         const Vst2::VstTimeInfo* ti = nullptr;
657 
658         if (hostCallback != nullptr)
659         {
660             int32 flags = Vst2::kVstPpqPosValid  | Vst2::kVstTempoValid
661                         | Vst2::kVstBarsValid    | Vst2::kVstCyclePosValid
662                         | Vst2::kVstTimeSigValid | Vst2::kVstSmpteValid
663                         | Vst2::kVstClockValid;
664 
665             auto result = hostCallback (&vstEffect, Vst2::audioMasterGetTime, 0, flags, nullptr, 0);
666             ti = reinterpret_cast<Vst2::VstTimeInfo*> (result);
667         }
668 
669         if (ti == nullptr || ti->sampleRate <= 0)
670             return false;
671 
672         info.bpm = (ti->flags & Vst2::kVstTempoValid) != 0 ? ti->tempo : 0.0;
673 
674         if ((ti->flags & Vst2::kVstTimeSigValid) != 0)
675         {
676             info.timeSigNumerator   = ti->timeSigNumerator;
677             info.timeSigDenominator = ti->timeSigDenominator;
678         }
679         else
680         {
681             info.timeSigNumerator   = 4;
682             info.timeSigDenominator = 4;
683         }
684 
685         info.timeInSamples = (int64) (ti->samplePos + 0.5);
686         info.timeInSeconds = ti->samplePos / ti->sampleRate;
687         info.ppqPosition = (ti->flags & Vst2::kVstPpqPosValid) != 0 ? ti->ppqPos : 0.0;
688         info.ppqPositionOfLastBarStart = (ti->flags & Vst2::kVstBarsValid) != 0 ? ti->barStartPos : 0.0;
689 
690         if ((ti->flags & Vst2::kVstSmpteValid) != 0)
691         {
692             AudioPlayHead::FrameRateType rate = AudioPlayHead::fpsUnknown;
693             double fps = 1.0;
694 
695             switch (ti->smpteFrameRate)
696             {
697                 case Vst2::kVstSmpte239fps:       rate = AudioPlayHead::fps23976;    fps = 24.0 * 1000.0 / 1001.0; break;
698                 case Vst2::kVstSmpte24fps:        rate = AudioPlayHead::fps24;       fps = 24.0;  break;
699                 case Vst2::kVstSmpte25fps:        rate = AudioPlayHead::fps25;       fps = 25.0;  break;
700                 case Vst2::kVstSmpte2997fps:      rate = AudioPlayHead::fps2997;     fps = 30.0 * 1000.0 / 1001.0; break;
701                 case Vst2::kVstSmpte30fps:        rate = AudioPlayHead::fps30;       fps = 30.0;  break;
702                 case Vst2::kVstSmpte2997dfps:     rate = AudioPlayHead::fps2997drop; fps = 30.0 * 1000.0 / 1001.0; break;
703                 case Vst2::kVstSmpte30dfps:       rate = AudioPlayHead::fps30drop;   fps = 30.0;  break;
704 
705                 case Vst2::kVstSmpteFilm16mm:
706                 case Vst2::kVstSmpteFilm35mm:     fps = 24.0; break;
707 
708                 case Vst2::kVstSmpte249fps:       fps = 25.0 * 1000.0 / 1001.0; break;
709                 case Vst2::kVstSmpte599fps:       fps = 60.0 * 1000.0 / 1001.0; break;
710                 case Vst2::kVstSmpte60fps:        fps = 60; break;
711 
712                 default:                          jassertfalse; // unknown frame-rate..
713             }
714 
715             info.frameRate = rate;
716             info.editOriginTime = ti->smpteOffset / (80.0 * fps);
717         }
718         else
719         {
720             info.frameRate = AudioPlayHead::fpsUnknown;
721             info.editOriginTime = 0;
722         }
723 
724         info.isRecording = (ti->flags & Vst2::kVstTransportRecording) != 0;
725         info.isPlaying   = (ti->flags & (Vst2::kVstTransportRecording | Vst2::kVstTransportPlaying)) != 0;
726         info.isLooping   = (ti->flags & Vst2::kVstTransportCycleActive) != 0;
727 
728         if ((ti->flags & Vst2::kVstCyclePosValid) != 0)
729         {
730             info.ppqLoopStart = ti->cycleStartPos;
731             info.ppqLoopEnd   = ti->cycleEndPos;
732         }
733         else
734         {
735             info.ppqLoopStart = 0;
736             info.ppqLoopEnd = 0;
737         }
738 
739         return true;
740     }
741 
742     //==============================================================================
getParameter(int32 index) const743     float getParameter (int32 index) const
744     {
745         if (auto* param = juceParameters.getParamForIndex (index))
746             return param->getValue();
747 
748         return 0.0f;
749     }
750 
getParameterCB(Vst2::AEffect * vstInterface,int32 index)751     static float getParameterCB (Vst2::AEffect* vstInterface, int32 index)
752     {
753         return getWrapper (vstInterface)->getParameter (index);
754     }
755 
setParameter(int32 index,float value)756     void setParameter (int32 index, float value)
757     {
758         if (auto* param = juceParameters.getParamForIndex (index))
759         {
760             param->setValue (value);
761 
762             inParameterChangedCallback = true;
763             param->sendValueChangedMessageToListeners (value);
764         }
765     }
766 
setParameterCB(Vst2::AEffect * vstInterface,int32 index,float value)767     static void setParameterCB (Vst2::AEffect* vstInterface, int32 index, float value)
768     {
769         getWrapper (vstInterface)->setParameter (index, value);
770     }
771 
audioProcessorParameterChanged(AudioProcessor *,int index,float newValue)772     void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue) override
773     {
774         if (inParameterChangedCallback.get())
775         {
776             inParameterChangedCallback = false;
777             return;
778         }
779 
780         if (hostCallback != nullptr)
781             hostCallback (&vstEffect, Vst2::audioMasterAutomate, index, 0, nullptr, newValue);
782     }
783 
audioProcessorParameterChangeGestureBegin(AudioProcessor *,int index)784     void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index) override
785     {
786         if (hostCallback != nullptr)
787             hostCallback (&vstEffect, Vst2::audioMasterBeginEdit, index, 0, nullptr, 0);
788     }
789 
audioProcessorParameterChangeGestureEnd(AudioProcessor *,int index)790     void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index) override
791     {
792         if (hostCallback != nullptr)
793             hostCallback (&vstEffect, Vst2::audioMasterEndEdit, index, 0, nullptr, 0);
794     }
795 
parameterValueChanged(int,float newValue)796     void parameterValueChanged (int, float newValue) override
797     {
798         // this can only come from the bypass parameter
799         isBypassed = (newValue != 0.0f);
800     }
801 
parameterGestureChanged(int,bool)802     void parameterGestureChanged (int, bool) override {}
803 
audioProcessorChanged(AudioProcessor *,const ChangeDetails & details)804     void audioProcessorChanged (AudioProcessor*, const ChangeDetails& details) override
805     {
806         hostChangeUpdater.update (details);
807     }
808 
getPinProperties(Vst2::VstPinProperties & properties,bool direction,int index) const809     bool getPinProperties (Vst2::VstPinProperties& properties, bool direction, int index) const
810     {
811         if (processor->isMidiEffect())
812             return false;
813 
814         int channelIdx, busIdx;
815 
816         // fill with default
817         properties.flags = 0;
818         properties.label[0] = 0;
819         properties.shortLabel[0] = 0;
820         properties.arrangementType = Vst2::kSpeakerArrEmpty;
821 
822         if ((channelIdx = processor->getOffsetInBusBufferForAbsoluteChannelIndex (direction, index, busIdx)) >= 0)
823         {
824             auto& bus = *processor->getBus (direction, busIdx);
825             auto& channelSet = bus.getCurrentLayout();
826             auto channelType = channelSet.getTypeOfChannel (channelIdx);
827 
828             properties.flags = Vst2::kVstPinIsActive | Vst2::kVstPinUseSpeaker;
829             properties.arrangementType = SpeakerMappings::channelSetToVstArrangementType (channelSet);
830             String label = bus.getName();
831 
832            #ifdef JucePlugin_PreferredChannelConfigurations
833             label += " " + String (channelIdx);
834            #else
835             if (channelSet.size() > 1)
836                 label += " " + AudioChannelSet::getAbbreviatedChannelTypeName (channelType);
837            #endif
838 
839             label.copyToUTF8 (properties.label, (size_t) (Vst2::kVstMaxLabelLen + 1));
840             label.copyToUTF8 (properties.shortLabel, (size_t) (Vst2::kVstMaxShortLabelLen + 1));
841 
842             if (channelType == AudioChannelSet::left
843                 || channelType == AudioChannelSet::leftSurround
844                 || channelType == AudioChannelSet::leftCentre
845                 || channelType == AudioChannelSet::leftSurroundSide
846                 || channelType == AudioChannelSet::topFrontLeft
847                 || channelType == AudioChannelSet::topRearLeft
848                 || channelType == AudioChannelSet::leftSurroundRear
849                 || channelType == AudioChannelSet::wideLeft)
850                 properties.flags |= Vst2::kVstPinIsStereo;
851 
852             return true;
853         }
854 
855         return false;
856     }
857 
858     //==============================================================================
timerCallback()859     void timerCallback() override
860     {
861         if (shouldDeleteEditor)
862         {
863             shouldDeleteEditor = false;
864             deleteEditor (true);
865         }
866 
867         {
868             ScopedLock lock (stateInformationLock);
869 
870             if (chunkMemoryTime > 0
871                  && chunkMemoryTime < juce::Time::getApproximateMillisecondCounter() - 2000
872                  && ! recursionCheck)
873             {
874                 chunkMemory.reset();
875                 chunkMemoryTime = 0;
876             }
877         }
878 
879         if (editorComp != nullptr)
880             editorComp->checkVisibility();
881     }
882 
setHasEditorFlag(bool shouldSetHasEditor)883     void setHasEditorFlag (bool shouldSetHasEditor)
884     {
885         auto hasEditor = (vstEffect.flags & Vst2::effFlagsHasEditor) != 0;
886 
887         if (shouldSetHasEditor == hasEditor)
888             return;
889 
890         if (shouldSetHasEditor)
891             vstEffect.flags |= Vst2::effFlagsHasEditor;
892         else
893             vstEffect.flags &= ~Vst2::effFlagsHasEditor;
894     }
895 
createEditorComp()896     void createEditorComp()
897     {
898         if (hasShutdown || processor == nullptr)
899             return;
900 
901         if (editorComp == nullptr)
902         {
903             if (auto* ed = processor->createEditorIfNeeded())
904             {
905                 setHasEditorFlag (true);
906                 editorComp.reset (new EditorCompWrapper (*this, *ed));
907             }
908             else
909             {
910                 setHasEditorFlag (false);
911             }
912         }
913 
914         shouldDeleteEditor = false;
915     }
916 
deleteEditor(bool canDeleteLaterIfModal)917     void deleteEditor (bool canDeleteLaterIfModal)
918     {
919         JUCE_AUTORELEASEPOOL
920         {
921             PopupMenu::dismissAllActiveMenus();
922 
923             jassert (! recursionCheck);
924             ScopedValueSetter<bool> svs (recursionCheck, true, false);
925 
926             if (editorComp != nullptr)
927             {
928                 if (auto* modalComponent = Component::getCurrentlyModalComponent())
929                 {
930                     modalComponent->exitModalState (0);
931 
932                     if (canDeleteLaterIfModal)
933                     {
934                         shouldDeleteEditor = true;
935                         return;
936                     }
937                 }
938 
939                 editorComp->detachHostWindow();
940 
941                 if (auto* ed = editorComp->getEditorComp())
942                     processor->editorBeingDeleted (ed);
943 
944                 editorComp = nullptr;
945 
946                 // there's some kind of component currently modal, but the host
947                 // is trying to delete our plugin. You should try to avoid this happening..
948                 jassert (Component::getCurrentlyModalComponent() == nullptr);
949             }
950         }
951     }
952 
dispatcher(int32 opCode,VstOpCodeArguments args)953     pointer_sized_int dispatcher (int32 opCode, VstOpCodeArguments args)
954     {
955         if (hasShutdown)
956             return 0;
957 
958         switch (opCode)
959         {
960             case Vst2::effOpen:                     return handleOpen (args);
961             case Vst2::effClose:                    return handleClose (args);
962             case Vst2::effSetProgram:               return handleSetCurrentProgram (args);
963             case Vst2::effGetProgram:               return handleGetCurrentProgram (args);
964             case Vst2::effSetProgramName:           return handleSetCurrentProgramName (args);
965             case Vst2::effGetProgramName:           return handleGetCurrentProgramName (args);
966             case Vst2::effGetParamLabel:            return handleGetParameterLabel (args);
967             case Vst2::effGetParamDisplay:          return handleGetParameterText (args);
968             case Vst2::effGetParamName:             return handleGetParameterName (args);
969             case Vst2::effSetSampleRate:            return handleSetSampleRate (args);
970             case Vst2::effSetBlockSize:             return handleSetBlockSize (args);
971             case Vst2::effMainsChanged:             return handleResumeSuspend (args);
972             case Vst2::effEditGetRect:              return handleGetEditorBounds (args);
973             case Vst2::effEditOpen:                 return handleOpenEditor (args);
974             case Vst2::effEditClose:                return handleCloseEditor (args);
975             case Vst2::effIdentify:                 return (pointer_sized_int) ByteOrder::bigEndianInt ("NvEf");
976             case Vst2::effGetChunk:                 return handleGetData (args);
977             case Vst2::effSetChunk:                 return handleSetData (args);
978             case Vst2::effProcessEvents:            return handlePreAudioProcessingEvents (args);
979             case Vst2::effCanBeAutomated:           return handleIsParameterAutomatable (args);
980             case Vst2::effString2Parameter:         return handleParameterValueForText (args);
981             case Vst2::effGetProgramNameIndexed:    return handleGetProgramName (args);
982             case Vst2::effGetInputProperties:       return handleGetInputPinProperties (args);
983             case Vst2::effGetOutputProperties:      return handleGetOutputPinProperties (args);
984             case Vst2::effGetPlugCategory:          return handleGetPlugInCategory (args);
985             case Vst2::effSetSpeakerArrangement:    return handleSetSpeakerConfiguration (args);
986             case Vst2::effSetBypass:                return handleSetBypass (args);
987             case Vst2::effGetEffectName:            return handleGetPlugInName (args);
988             case Vst2::effGetProductString:         return handleGetPlugInName (args);
989             case Vst2::effGetVendorString:          return handleGetManufacturerName (args);
990             case Vst2::effGetVendorVersion:         return handleGetManufacturerVersion (args);
991             case Vst2::effVendorSpecific:           return handleManufacturerSpecific (args);
992             case Vst2::effCanDo:                    return handleCanPlugInDo (args);
993             case Vst2::effGetTailSize:              return handleGetTailSize (args);
994             case Vst2::effKeysRequired:             return handleKeyboardFocusRequired (args);
995             case Vst2::effGetVstVersion:            return handleGetVstInterfaceVersion (args);
996             case Vst2::effGetCurrentMidiProgram:    return handleGetCurrentMidiProgram (args);
997             case Vst2::effGetSpeakerArrangement:    return handleGetSpeakerConfiguration (args);
998             case Vst2::effSetTotalSampleToProcess:  return handleSetNumberOfSamplesToProcess (args);
999             case Vst2::effSetProcessPrecision:      return handleSetSampleFloatType (args);
1000             case Vst2::effGetNumMidiInputChannels:  return handleGetNumMidiInputChannels();
1001             case Vst2::effGetNumMidiOutputChannels: return handleGetNumMidiOutputChannels();
1002             default:                                return 0;
1003         }
1004     }
1005 
dispatcherCB(Vst2::AEffect * vstInterface,int32 opCode,int32 index,pointer_sized_int value,void * ptr,float opt)1006     static pointer_sized_int dispatcherCB (Vst2::AEffect* vstInterface, int32 opCode, int32 index,
1007                                            pointer_sized_int value, void* ptr, float opt)
1008     {
1009         auto* wrapper = getWrapper (vstInterface);
1010         VstOpCodeArguments args = { index, value, ptr, opt };
1011 
1012         if (opCode == Vst2::effClose)
1013         {
1014             wrapper->dispatcher (opCode, args);
1015             delete wrapper;
1016             return 1;
1017         }
1018 
1019         return wrapper->dispatcher (opCode, args);
1020     }
1021 
1022     //==============================================================================
1023     // A component to hold the AudioProcessorEditor, and cope with some housekeeping
1024     // chores when it changes or repaints.
1025     struct EditorCompWrapper  : public Component
1026                              #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
1027                               , public Timer
1028                              #endif
1029     {
EditorCompWrapperJuceVSTWrapper::EditorCompWrapper1030         EditorCompWrapper (JuceVSTWrapper& w, AudioProcessorEditor& editor)
1031             : wrapper (w)
1032         {
1033             editor.setOpaque (true);
1034             addAndMakeVisible (editor);
1035 
1036             auto editorBounds = getSizeToContainChild();
1037             setSize (editorBounds.getWidth(), editorBounds.getHeight());
1038 
1039            #if JUCE_WINDOWS
1040             if (! getHostType().isReceptor())
1041                 addMouseListener (this, true);
1042            #endif
1043 
1044             setOpaque (true);
1045             ignoreUnused (fakeMouseGenerator);
1046         }
1047 
~EditorCompWrapperJuceVSTWrapper::EditorCompWrapper1048         ~EditorCompWrapper() override
1049         {
1050             deleteAllChildren(); // note that we can't use a std::unique_ptr because the editor may
1051                                  // have been transferred to another parent which takes over ownership.
1052         }
1053 
paintJuceVSTWrapper::EditorCompWrapper1054         void paint (Graphics& g) override
1055         {
1056             g.fillAll (Colours::black);
1057         }
1058 
getEditorBoundsJuceVSTWrapper::EditorCompWrapper1059         void getEditorBounds (Vst2::ERect& bounds)
1060         {
1061             auto editorBounds = getSizeToContainChild();
1062             bounds = convertToHostBounds ({ 0, 0, (int16) editorBounds.getHeight(), (int16) editorBounds.getWidth() });
1063         }
1064 
attachToHostJuceVSTWrapper::EditorCompWrapper1065         void attachToHost (VstOpCodeArguments args)
1066         {
1067             setVisible (false);
1068 
1069            #if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD
1070             addToDesktop (0, args.ptr);
1071             hostWindow = (HostWindowType) args.ptr;
1072 
1073             #if JUCE_LINUX || JUCE_BSD
1074              X11Symbols::getInstance()->xReparentWindow (display,
1075                                                          (Window) getWindowHandle(),
1076                                                          (HostWindowType) hostWindow,
1077                                                          0, 0);
1078             #elif JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
1079              checkHostWindowScaleFactor();
1080              startTimer (500);
1081             #endif
1082            #elif JUCE_MAC
1083             hostWindow = attachComponentToWindowRefVST (this, args.ptr, wrapper.useNSView);
1084            #endif
1085 
1086             setVisible (true);
1087         }
1088 
detachHostWindowJuceVSTWrapper::EditorCompWrapper1089         void detachHostWindow()
1090         {
1091            #if JUCE_MAC
1092             if (hostWindow != nullptr)
1093                 detachComponentFromWindowRefVST (this, hostWindow, wrapper.useNSView);
1094            #endif
1095 
1096             hostWindow = {};
1097         }
1098 
checkVisibilityJuceVSTWrapper::EditorCompWrapper1099         void checkVisibility()
1100         {
1101            #if JUCE_MAC
1102             if (hostWindow != nullptr)
1103                 checkWindowVisibilityVST (hostWindow, this, wrapper.useNSView);
1104            #endif
1105         }
1106 
getEditorCompJuceVSTWrapper::EditorCompWrapper1107         AudioProcessorEditor* getEditorComp() const noexcept
1108         {
1109             return dynamic_cast<AudioProcessorEditor*> (getChildComponent (0));
1110         }
1111 
resizedJuceVSTWrapper::EditorCompWrapper1112         void resized() override
1113         {
1114             if (auto* pluginEditor = getEditorComp())
1115             {
1116                 if (! resizingParent)
1117                 {
1118                     auto newBounds = getLocalBounds();
1119 
1120                     {
1121                         const ScopedValueSetter<bool> resizingChildSetter (resizingChild, true);
1122                         pluginEditor->setBounds (pluginEditor->getLocalArea (this, newBounds).withPosition (0, 0));
1123                     }
1124 
1125                     lastBounds = newBounds;
1126                 }
1127 
1128                 updateWindowSize();
1129             }
1130 
1131            #if JUCE_MAC && ! JUCE_64BIT
1132             if (! wrapper.useNSView)
1133                 updateEditorCompBoundsVST (this);
1134            #endif
1135         }
1136 
parentSizeChangedJuceVSTWrapper::EditorCompWrapper1137         void parentSizeChanged() override
1138         {
1139             updateWindowSize();
1140         }
1141 
childBoundsChangedJuceVSTWrapper::EditorCompWrapper1142         void childBoundsChanged (Component*) override
1143         {
1144             if (resizingChild)
1145                 return;
1146 
1147             auto newBounds = getSizeToContainChild();
1148 
1149             if (newBounds != lastBounds)
1150             {
1151                 updateWindowSize();
1152                 lastBounds = newBounds;
1153             }
1154         }
1155 
getSizeToContainChildJuceVSTWrapper::EditorCompWrapper1156         juce::Rectangle<int> getSizeToContainChild()
1157         {
1158             if (auto* pluginEditor = getEditorComp())
1159                 return getLocalArea (pluginEditor, pluginEditor->getLocalBounds());
1160 
1161             return {};
1162         }
1163 
updateWindowSizeJuceVSTWrapper::EditorCompWrapper1164         void updateWindowSize()
1165         {
1166             if (! resizingParent
1167                 && getEditorComp() != nullptr
1168                 && hostWindow != HostWindowType{})
1169             {
1170                 auto editorBounds = getSizeToContainChild();
1171 
1172                 resizeHostWindow (editorBounds.getWidth(), editorBounds.getHeight());
1173 
1174                 {
1175                     const ScopedValueSetter<bool> resizingParentSetter (resizingParent, true);
1176 
1177                    #if JUCE_LINUX || JUCE_BSD // setSize() on linux causes renoise and energyxt to fail.
1178                     auto rect = convertToHostBounds ({ 0, 0, (int16) editorBounds.getHeight(), (int16) editorBounds.getWidth() });
1179 
1180                     X11Symbols::getInstance()->xResizeWindow (display, (Window) getWindowHandle(),
1181                                                               static_cast<unsigned int> (rect.right - rect.left),
1182                                                               static_cast<unsigned int> (rect.bottom - rect.top));
1183                    #else
1184                     setSize (editorBounds.getWidth(), editorBounds.getHeight());
1185                    #endif
1186                 }
1187 
1188                #if JUCE_MAC
1189                 resizeHostWindow (editorBounds.getWidth(), editorBounds.getHeight()); // (doing this a second time seems to be necessary in tracktion)
1190                #endif
1191             }
1192         }
1193 
resizeHostWindowJuceVSTWrapper::EditorCompWrapper1194         void resizeHostWindow (int newWidth, int newHeight)
1195         {
1196             auto rect = convertToHostBounds ({ 0, 0, (int16) newHeight, (int16) newWidth });
1197             newWidth = rect.right - rect.left;
1198             newHeight = rect.bottom - rect.top;
1199 
1200             bool sizeWasSuccessful = false;
1201 
1202             if (auto host = wrapper.hostCallback)
1203             {
1204                 auto status = host (wrapper.getAEffect(), Vst2::audioMasterCanDo, 0, 0, const_cast<char*> ("sizeWindow"), 0);
1205 
1206                 if (status == (pointer_sized_int) 1 || getHostType().isAbletonLive())
1207                 {
1208                     const ScopedValueSetter<bool> resizingParentSetter (resizingParent, true);
1209 
1210                     sizeWasSuccessful = (host (wrapper.getAEffect(), Vst2::audioMasterSizeWindow,
1211                                                newWidth, newHeight, nullptr, 0) != 0);
1212                 }
1213             }
1214 
1215             // some hosts don't support the sizeWindow call, so do it manually..
1216             if (! sizeWasSuccessful)
1217             {
1218                 const ScopedValueSetter<bool> resizingParentSetter (resizingParent, true);
1219 
1220                #if JUCE_MAC
1221                 setNativeHostWindowSizeVST (hostWindow, this, newWidth, newHeight, wrapper.useNSView);
1222                #elif JUCE_LINUX || JUCE_BSD
1223                 // (Currently, all linux hosts support sizeWindow, so this should never need to happen)
1224                 setSize (newWidth, newHeight);
1225                #else
1226                 int dw = 0;
1227                 int dh = 0;
1228                 const int frameThickness = GetSystemMetrics (SM_CYFIXEDFRAME);
1229 
1230                 HWND w = (HWND) getWindowHandle();
1231 
1232                 while (w != 0)
1233                 {
1234                     HWND parent = getWindowParent (w);
1235 
1236                     if (parent == 0)
1237                         break;
1238 
1239                     TCHAR windowType [32] = { 0 };
1240                     GetClassName (parent, windowType, 31);
1241 
1242                     if (String (windowType).equalsIgnoreCase ("MDIClient"))
1243                         break;
1244 
1245                     RECT windowPos, parentPos;
1246                     GetWindowRect (w, &windowPos);
1247                     GetWindowRect (parent, &parentPos);
1248 
1249                     if (w != (HWND) getWindowHandle())
1250                         SetWindowPos (w, 0, 0, 0, newWidth + dw, newHeight + dh,
1251                                       SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
1252 
1253                     dw = (parentPos.right - parentPos.left) - (windowPos.right - windowPos.left);
1254                     dh = (parentPos.bottom - parentPos.top) - (windowPos.bottom - windowPos.top);
1255 
1256                     w = parent;
1257 
1258                     if (dw == 2 * frameThickness)
1259                         break;
1260 
1261                     if (dw > 100 || dh > 100)
1262                         w = 0;
1263                 }
1264 
1265                 if (w != 0)
1266                     SetWindowPos (w, 0, 0, 0, newWidth + dw, newHeight + dh,
1267                                   SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
1268                #endif
1269             }
1270         }
1271 
setContentScaleFactorJuceVSTWrapper::EditorCompWrapper1272         void setContentScaleFactor (float scale)
1273         {
1274             if (! approximatelyEqual (scale, editorScaleFactor))
1275             {
1276                 editorScaleFactor = scale;
1277 
1278                 if (auto* pluginEditor = getEditorComp())
1279                 {
1280                     auto prevEditorBounds = pluginEditor->getLocalArea (this, lastBounds);
1281 
1282                     {
1283                         const ScopedValueSetter<bool> resizingChildSetter (resizingChild, true);
1284 
1285                         pluginEditor->setScaleFactor (editorScaleFactor);
1286                         pluginEditor->setBounds (prevEditorBounds.withPosition (0, 0));
1287                     }
1288 
1289                     lastBounds = getSizeToContainChild();
1290                     updateWindowSize();
1291                 }
1292             }
1293         }
1294 
1295        #if JUCE_WINDOWS
mouseDownJuceVSTWrapper::EditorCompWrapper1296         void mouseDown (const MouseEvent&) override
1297         {
1298             broughtToFront();
1299         }
1300 
broughtToFrontJuceVSTWrapper::EditorCompWrapper1301         void broughtToFront() override
1302         {
1303             // for hosts like nuendo, need to also pop the MDI container to the
1304             // front when our comp is clicked on.
1305             if (! isCurrentlyBlockedByAnotherModalComponent())
1306                 if (HWND parent = findMDIParentOf ((HWND) getWindowHandle()))
1307                     SetWindowPos (parent, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
1308         }
1309 
1310         #if JUCE_WIN_PER_MONITOR_DPI_AWARE
checkHostWindowScaleFactorJuceVSTWrapper::EditorCompWrapper1311          void checkHostWindowScaleFactor()
1312          {
1313              auto hostWindowScale = (float) getScaleFactorForWindow ((HostWindowType) hostWindow);
1314 
1315              if (hostWindowScale > 0.0f && ! approximatelyEqual (hostWindowScale, editorScaleFactor))
1316                  wrapper.handleSetContentScaleFactor (hostWindowScale);
1317          }
1318 
timerCallbackJuceVSTWrapper::EditorCompWrapper1319          void timerCallback() override
1320          {
1321              checkHostWindowScaleFactor();
1322          }
1323         #endif
1324        #endif
1325 
1326        #if JUCE_MAC
keyPressedJuceVSTWrapper::EditorCompWrapper1327         bool keyPressed (const KeyPress&) override
1328         {
1329             // If we have an unused keypress, move the key-focus to a host window
1330             // and re-inject the event..
1331             return forwardCurrentKeyEventToHostVST (this, wrapper.useNSView);
1332         }
1333        #endif
1334 
1335     private:
1336         //==============================================================================
convertToHostBoundsJuceVSTWrapper::EditorCompWrapper1337         static Vst2::ERect convertToHostBounds (const Vst2::ERect& rect)
1338         {
1339             auto desktopScale = Desktop::getInstance().getGlobalScaleFactor();
1340 
1341             if (approximatelyEqual (desktopScale, 1.0f))
1342                 return rect;
1343 
1344             return { (int16) roundToInt (rect.top    * desktopScale),
1345                      (int16) roundToInt (rect.left   * desktopScale),
1346                      (int16) roundToInt (rect.bottom * desktopScale),
1347                      (int16) roundToInt (rect.right  * desktopScale) };
1348         }
1349 
1350         //==============================================================================
1351         JuceVSTWrapper& wrapper;
1352         FakeMouseMoveGenerator fakeMouseGenerator;
1353         bool resizingChild = false, resizingParent = false;
1354 
1355         float editorScaleFactor = 1.0f;
1356         juce::Rectangle<int> lastBounds;
1357 
1358        #if JUCE_LINUX || JUCE_BSD
1359         using HostWindowType = ::Window;
1360         ::Display* display = XWindowSystem::getInstance()->getDisplay();
1361        #elif JUCE_WINDOWS
1362         using HostWindowType = HWND;
1363         WindowsHooks hooks;
1364        #else
1365         using HostWindowType = void*;
1366        #endif
1367 
1368         HostWindowType hostWindow = {};
1369 
1370         JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorCompWrapper)
1371     };
1372 
1373     //==============================================================================
1374 private:
1375     struct HostChangeUpdater  : private AsyncUpdater
1376     {
HostChangeUpdaterJuceVSTWrapper::HostChangeUpdater1377         explicit HostChangeUpdater (JuceVSTWrapper& o)  : owner (o) {}
~HostChangeUpdaterJuceVSTWrapper::HostChangeUpdater1378         ~HostChangeUpdater() override  { cancelPendingUpdate(); }
1379 
updateJuceVSTWrapper::HostChangeUpdater1380         void update (const ChangeDetails& details)
1381         {
1382             if (details.latencyChanged)
1383                 owner.vstEffect.initialDelay = owner.processor->getLatencySamples();
1384 
1385             if (details.parameterInfoChanged || details.programChanged)
1386                 triggerAsyncUpdate();
1387         }
1388 
1389     private:
handleAsyncUpdateJuceVSTWrapper::HostChangeUpdater1390         void handleAsyncUpdate() override
1391         {
1392             if (auto* callback = owner.hostCallback)
1393             {
1394                 callback (&owner.vstEffect, Vst2::audioMasterUpdateDisplay, 0, 0, nullptr, 0);
1395                 callback (&owner.vstEffect, Vst2::audioMasterIOChanged,     0, 0, nullptr, 0);
1396             }
1397         }
1398 
1399         JuceVSTWrapper& owner;
1400     };
1401 
getWrapper(Vst2::AEffect * v)1402     static JuceVSTWrapper* getWrapper (Vst2::AEffect* v) noexcept  { return static_cast<JuceVSTWrapper*> (v->object); }
1403 
isProcessLevelOffline()1404     bool isProcessLevelOffline()
1405     {
1406         return hostCallback != nullptr
1407                 && (int32) hostCallback (&vstEffect, Vst2::audioMasterGetCurrentProcessLevel, 0, 0, nullptr, 0) == 4;
1408     }
1409 
convertHexVersionToDecimal(const unsigned int hexVersion)1410     static int32 convertHexVersionToDecimal (const unsigned int hexVersion)
1411     {
1412        #if JUCE_VST_RETURN_HEX_VERSION_NUMBER_DIRECTLY
1413         return (int32) hexVersion;
1414        #else
1415         // Currently, only Cubase displays the version number to the user
1416         // We are hoping here that when other DAWs start to display the version
1417         // number, that they do so according to yfede's encoding table in the link
1418         // below. If not, then this code will need an if (isSteinberg()) in the
1419         // future.
1420         int major = (hexVersion >> 16) & 0xff;
1421         int minor = (hexVersion >> 8) & 0xff;
1422         int bugfix = hexVersion & 0xff;
1423 
1424         // for details, see: https://forum.juce.com/t/issues-with-version-integer-reported-by-vst2/23867
1425 
1426         // Encoding B
1427         if (major < 1)
1428             return major * 1000 + minor * 100 + bugfix * 10;
1429 
1430         // Encoding E
1431         if (major > 100)
1432             return major * 10000000 + minor * 100000 + bugfix * 1000;
1433 
1434         // Encoding D
1435         return static_cast<int32> (hexVersion);
1436        #endif
1437     }
1438 
1439     //==============================================================================
1440    #if JUCE_WINDOWS
1441     // Workarounds for hosts which attempt to open editor windows on a non-GUI thread.. (Grrrr...)
checkWhetherMessageThreadIsCorrect()1442     static void checkWhetherMessageThreadIsCorrect()
1443     {
1444         auto host = getHostType();
1445 
1446         if (host.isWavelab() || host.isCubaseBridged() || host.isPremiere())
1447         {
1448             if (! messageThreadIsDefinitelyCorrect)
1449             {
1450                 MessageManager::getInstance()->setCurrentThreadAsMessageThread();
1451 
1452                 struct MessageThreadCallback  : public CallbackMessage
1453                 {
1454                     MessageThreadCallback (bool& tr) : triggered (tr) {}
1455                     void messageCallback() override     { triggered = true; }
1456 
1457                     bool& triggered;
1458                 };
1459 
1460                 (new MessageThreadCallback (messageThreadIsDefinitelyCorrect))->post();
1461             }
1462         }
1463     }
1464    #else
checkWhetherMessageThreadIsCorrect()1465     static void checkWhetherMessageThreadIsCorrect() {}
1466    #endif
1467 
1468     //==============================================================================
1469     template <typename FloatType>
deleteTempChannels(VstTempBuffers<FloatType> & tmpBuffers)1470     void deleteTempChannels (VstTempBuffers<FloatType>& tmpBuffers)
1471     {
1472         tmpBuffers.release();
1473 
1474         if (processor != nullptr)
1475             tmpBuffers.tempChannels.insertMultiple (0, nullptr, vstEffect.numInputs
1476                                                                  + vstEffect.numOutputs);
1477     }
1478 
deleteTempChannels()1479     void deleteTempChannels()
1480     {
1481         deleteTempChannels (floatTempBuffers);
1482         deleteTempChannels (doubleTempBuffers);
1483     }
1484 
1485     //==============================================================================
findMaxTotalChannels(int & maxTotalIns,int & maxTotalOuts)1486     void findMaxTotalChannels (int& maxTotalIns, int& maxTotalOuts)
1487     {
1488        #ifdef JucePlugin_PreferredChannelConfigurations
1489         int configs[][2] = { JucePlugin_PreferredChannelConfigurations };
1490         maxTotalIns = maxTotalOuts = 0;
1491 
1492         for (auto& config : configs)
1493         {
1494             maxTotalIns =  jmax (maxTotalIns,  config[0]);
1495             maxTotalOuts = jmax (maxTotalOuts, config[1]);
1496         }
1497        #else
1498         auto numInputBuses  = processor->getBusCount (true);
1499         auto numOutputBuses = processor->getBusCount (false);
1500 
1501         if (numInputBuses > 1 || numOutputBuses > 1)
1502         {
1503             maxTotalIns = maxTotalOuts = 0;
1504 
1505             for (int i = 0; i < numInputBuses; ++i)
1506                 maxTotalIns  += processor->getChannelCountOfBus (true, i);
1507 
1508             for (int i = 0; i < numOutputBuses; ++i)
1509                 maxTotalOuts += processor->getChannelCountOfBus (false, i);
1510         }
1511         else
1512         {
1513             maxTotalIns  = numInputBuses  > 0 ? processor->getBus (true,  0)->getMaxSupportedChannels (64) : 0;
1514             maxTotalOuts = numOutputBuses > 0 ? processor->getBus (false, 0)->getMaxSupportedChannels (64) : 0;
1515         }
1516        #endif
1517     }
1518 
pluginHasSidechainsOrAuxs() const1519     bool pluginHasSidechainsOrAuxs() const  { return (processor->getBusCount (true) > 1 || processor->getBusCount (false) > 1); }
1520 
1521     //==============================================================================
1522     /** Host to plug-in calls. */
1523 
handleOpen(VstOpCodeArguments)1524     pointer_sized_int handleOpen (VstOpCodeArguments)
1525     {
1526         // Note: most hosts call this on the UI thread, but wavelab doesn't, so be careful in here.
1527         setHasEditorFlag (processor->hasEditor());
1528 
1529         return 0;
1530     }
1531 
handleClose(VstOpCodeArguments)1532     pointer_sized_int handleClose (VstOpCodeArguments)
1533     {
1534         // Note: most hosts call this on the UI thread, but wavelab doesn't, so be careful in here.
1535         stopTimer();
1536 
1537         if (MessageManager::getInstance()->isThisTheMessageThread())
1538             deleteEditor (false);
1539 
1540         return 0;
1541     }
1542 
handleSetCurrentProgram(VstOpCodeArguments args)1543     pointer_sized_int handleSetCurrentProgram (VstOpCodeArguments args)
1544     {
1545         if (processor != nullptr && isPositiveAndBelow ((int) args.value, processor->getNumPrograms()))
1546             processor->setCurrentProgram ((int) args.value);
1547 
1548         return 0;
1549     }
1550 
handleGetCurrentProgram(VstOpCodeArguments)1551     pointer_sized_int handleGetCurrentProgram (VstOpCodeArguments)
1552     {
1553         return (processor != nullptr && processor->getNumPrograms() > 0 ? processor->getCurrentProgram() : 0);
1554     }
1555 
handleSetCurrentProgramName(VstOpCodeArguments args)1556     pointer_sized_int handleSetCurrentProgramName (VstOpCodeArguments args)
1557     {
1558         if (processor != nullptr && processor->getNumPrograms() > 0)
1559             processor->changeProgramName (processor->getCurrentProgram(), (char*) args.ptr);
1560 
1561         return 0;
1562     }
1563 
handleGetCurrentProgramName(VstOpCodeArguments args)1564     pointer_sized_int handleGetCurrentProgramName (VstOpCodeArguments args)
1565     {
1566         if (processor != nullptr && processor->getNumPrograms() > 0)
1567             processor->getProgramName (processor->getCurrentProgram()).copyToUTF8 ((char*) args.ptr, 24 + 1);
1568 
1569         return 0;
1570     }
1571 
handleGetParameterLabel(VstOpCodeArguments args)1572     pointer_sized_int handleGetParameterLabel (VstOpCodeArguments args)
1573     {
1574         if (auto* param = juceParameters.getParamForIndex (args.index))
1575         {
1576             // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more.
1577             param->getLabel().copyToUTF8 ((char*) args.ptr, 24 + 1);
1578         }
1579 
1580         return 0;
1581     }
1582 
handleGetParameterText(VstOpCodeArguments args)1583     pointer_sized_int handleGetParameterText (VstOpCodeArguments args)
1584     {
1585         if (auto* param = juceParameters.getParamForIndex (args.index))
1586         {
1587             // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more.
1588             param->getCurrentValueAsText().copyToUTF8 ((char*) args.ptr, 24 + 1);
1589         }
1590 
1591         return 0;
1592     }
1593 
handleGetParameterName(VstOpCodeArguments args)1594     pointer_sized_int handleGetParameterName (VstOpCodeArguments args)
1595     {
1596         if (auto* param = juceParameters.getParamForIndex (args.index))
1597         {
1598             // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more.
1599             param->getName (32).copyToUTF8 ((char*) args.ptr, 32 + 1);
1600         }
1601 
1602         return 0;
1603     }
1604 
handleSetSampleRate(VstOpCodeArguments args)1605     pointer_sized_int handleSetSampleRate (VstOpCodeArguments args)
1606     {
1607         sampleRate = args.opt;
1608         return 0;
1609     }
1610 
handleSetBlockSize(VstOpCodeArguments args)1611     pointer_sized_int handleSetBlockSize (VstOpCodeArguments args)
1612     {
1613         blockSize = (int32) args.value;
1614         return 0;
1615     }
1616 
handleResumeSuspend(VstOpCodeArguments args)1617     pointer_sized_int handleResumeSuspend (VstOpCodeArguments args)
1618     {
1619         if (args.value)
1620             resume();
1621         else
1622             suspend();
1623 
1624         return 0;
1625     }
1626 
handleGetEditorBounds(VstOpCodeArguments args)1627     pointer_sized_int handleGetEditorBounds (VstOpCodeArguments args)
1628     {
1629         checkWhetherMessageThreadIsCorrect();
1630         const MessageManagerLock mmLock;
1631         createEditorComp();
1632 
1633         if (editorComp != nullptr)
1634         {
1635             editorComp->getEditorBounds (editorRect);
1636             *((Vst2::ERect**) args.ptr) = &editorRect;
1637             return (pointer_sized_int) &editorRect;
1638         }
1639 
1640         return 0;
1641     }
1642 
handleOpenEditor(VstOpCodeArguments args)1643     pointer_sized_int handleOpenEditor (VstOpCodeArguments args)
1644     {
1645         checkWhetherMessageThreadIsCorrect();
1646         const MessageManagerLock mmLock;
1647         jassert (! recursionCheck);
1648 
1649         startTimerHz (4); // performs misc housekeeping chores
1650 
1651         deleteEditor (true);
1652         createEditorComp();
1653 
1654         if (editorComp != nullptr)
1655         {
1656             editorComp->attachToHost (args);
1657             return 1;
1658         }
1659 
1660         return 0;
1661     }
1662 
handleCloseEditor(VstOpCodeArguments)1663     pointer_sized_int handleCloseEditor (VstOpCodeArguments)
1664     {
1665         checkWhetherMessageThreadIsCorrect();
1666         const MessageManagerLock mmLock;
1667         deleteEditor (true);
1668         return 0;
1669     }
1670 
handleGetData(VstOpCodeArguments args)1671     pointer_sized_int handleGetData (VstOpCodeArguments args)
1672     {
1673         if (processor == nullptr)
1674             return 0;
1675 
1676         auto data = (void**) args.ptr;
1677         bool onlyStoreCurrentProgramData = (args.index != 0);
1678 
1679         ScopedLock lock (stateInformationLock);
1680         chunkMemory.reset();
1681 
1682         if (onlyStoreCurrentProgramData)
1683             processor->getCurrentProgramStateInformation (chunkMemory);
1684         else
1685             processor->getStateInformation (chunkMemory);
1686 
1687         *data = (void*) chunkMemory.getData();
1688 
1689         // because the chunk is only needed temporarily by the host (or at least you'd
1690         // hope so) we'll give it a while and then free it in the timer callback.
1691         chunkMemoryTime = juce::Time::getApproximateMillisecondCounter();
1692 
1693         return (int32) chunkMemory.getSize();
1694     }
1695 
handleSetData(VstOpCodeArguments args)1696     pointer_sized_int handleSetData (VstOpCodeArguments args)
1697     {
1698         if (processor != nullptr)
1699         {
1700             void* data = args.ptr;
1701             int32 byteSize = (int32) args.value;
1702             bool onlyRestoreCurrentProgramData = (args.index != 0);
1703 
1704             {
1705                 ScopedLock lock (stateInformationLock);
1706 
1707                 chunkMemory.reset();
1708                 chunkMemoryTime = 0;
1709 
1710                 if (byteSize > 0 && data != nullptr)
1711                 {
1712                     if (onlyRestoreCurrentProgramData)
1713                         processor->setCurrentProgramStateInformation (data, byteSize);
1714                     else
1715                         processor->setStateInformation (data, byteSize);
1716                 }
1717             }
1718         }
1719 
1720         return 0;
1721     }
1722 
handlePreAudioProcessingEvents(VstOpCodeArguments args)1723     pointer_sized_int handlePreAudioProcessingEvents (VstOpCodeArguments args)
1724     {
1725        #if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
1726         VSTMidiEventList::addEventsToMidiBuffer ((Vst2::VstEvents*) args.ptr, midiEvents);
1727         return 1;
1728        #else
1729         ignoreUnused (args);
1730         return 0;
1731        #endif
1732     }
1733 
handleIsParameterAutomatable(VstOpCodeArguments args)1734     pointer_sized_int handleIsParameterAutomatable (VstOpCodeArguments args)
1735     {
1736         if (auto* param = juceParameters.getParamForIndex (args.index))
1737         {
1738             const bool isMeter = ((((unsigned int) param->getCategory() & 0xffff0000) >> 16) == 2);
1739             return (param->isAutomatable() && (! isMeter) ? 1 : 0);
1740         }
1741 
1742         return 0;
1743     }
1744 
handleParameterValueForText(VstOpCodeArguments args)1745     pointer_sized_int handleParameterValueForText (VstOpCodeArguments args)
1746     {
1747         if (auto* param = juceParameters.getParamForIndex (args.index))
1748         {
1749             if (! LegacyAudioParameter::isLegacy (param))
1750             {
1751                 auto value = param->getValueForText (String::fromUTF8 ((char*) args.ptr));
1752                 param->setValue (value);
1753 
1754                 inParameterChangedCallback = true;
1755                 param->sendValueChangedMessageToListeners (value);
1756 
1757                 return 1;
1758             }
1759         }
1760 
1761         return 0;
1762     }
1763 
handleGetProgramName(VstOpCodeArguments args)1764     pointer_sized_int handleGetProgramName (VstOpCodeArguments args)
1765     {
1766         if (processor != nullptr && isPositiveAndBelow (args.index, processor->getNumPrograms()))
1767         {
1768             processor->getProgramName (args.index).copyToUTF8 ((char*) args.ptr, 24 + 1);
1769             return 1;
1770         }
1771 
1772         return 0;
1773     }
1774 
handleGetInputPinProperties(VstOpCodeArguments args)1775     pointer_sized_int handleGetInputPinProperties (VstOpCodeArguments args)
1776     {
1777         return (processor != nullptr && getPinProperties (*(Vst2::VstPinProperties*) args.ptr, true, args.index)) ? 1 : 0;
1778     }
1779 
handleGetOutputPinProperties(VstOpCodeArguments args)1780     pointer_sized_int handleGetOutputPinProperties (VstOpCodeArguments args)
1781     {
1782         return (processor != nullptr && getPinProperties (*(Vst2::VstPinProperties*) args.ptr, false, args.index)) ? 1 : 0;
1783     }
1784 
handleGetPlugInCategory(VstOpCodeArguments)1785     pointer_sized_int handleGetPlugInCategory (VstOpCodeArguments)
1786     {
1787         return Vst2::JucePlugin_VSTCategory;
1788     }
1789 
handleSetSpeakerConfiguration(VstOpCodeArguments args)1790     pointer_sized_int handleSetSpeakerConfiguration (VstOpCodeArguments args)
1791     {
1792         auto* pluginInput  = reinterpret_cast<Vst2::VstSpeakerArrangement*> (args.value);
1793         auto* pluginOutput = reinterpret_cast<Vst2::VstSpeakerArrangement*> (args.ptr);
1794 
1795         if (processor->isMidiEffect())
1796             return 0;
1797 
1798         auto numIns  = processor->getBusCount (true);
1799         auto numOuts = processor->getBusCount (false);
1800 
1801         if (pluginInput != nullptr && pluginInput->type >= 0)
1802         {
1803             // inconsistent request?
1804             if (SpeakerMappings::vstArrangementTypeToChannelSet (*pluginInput).size() != pluginInput->numChannels)
1805                 return 0;
1806         }
1807 
1808         if (pluginOutput != nullptr && pluginOutput->type >= 0)
1809         {
1810             // inconsistent request?
1811             if (SpeakerMappings::vstArrangementTypeToChannelSet (*pluginOutput).size() != pluginOutput->numChannels)
1812                 return 0;
1813         }
1814 
1815         if (pluginInput != nullptr  && pluginInput->numChannels  > 0 && numIns  == 0)
1816             return 0;
1817 
1818         if (pluginOutput != nullptr && pluginOutput->numChannels > 0 && numOuts == 0)
1819             return 0;
1820 
1821         auto layouts = processor->getBusesLayout();
1822 
1823         if (pluginInput != nullptr && pluginInput-> numChannels >= 0 && numIns  > 0)
1824             layouts.getChannelSet (true,  0) = SpeakerMappings::vstArrangementTypeToChannelSet (*pluginInput);
1825 
1826         if (pluginOutput != nullptr && pluginOutput->numChannels >= 0 && numOuts > 0)
1827             layouts.getChannelSet (false, 0) = SpeakerMappings::vstArrangementTypeToChannelSet (*pluginOutput);
1828 
1829        #ifdef JucePlugin_PreferredChannelConfigurations
1830         short configs[][2] = { JucePlugin_PreferredChannelConfigurations };
1831         if (! AudioProcessor::containsLayout (layouts, configs))
1832             return 0;
1833        #endif
1834 
1835         return processor->setBusesLayout (layouts) ? 1 : 0;
1836     }
1837 
handleSetBypass(VstOpCodeArguments args)1838     pointer_sized_int handleSetBypass (VstOpCodeArguments args)
1839     {
1840         isBypassed = (args.value != 0);
1841 
1842         if (auto* bypass = processor->getBypassParameter())
1843             bypass->setValueNotifyingHost (isBypassed ? 1.0f : 0.0f);
1844 
1845         return 1;
1846     }
1847 
handleGetPlugInName(VstOpCodeArguments args)1848     pointer_sized_int handleGetPlugInName (VstOpCodeArguments args)
1849     {
1850         String (JucePlugin_Name).copyToUTF8 ((char*) args.ptr, 64 + 1);
1851         return 1;
1852     }
1853 
handleGetManufacturerName(VstOpCodeArguments args)1854     pointer_sized_int handleGetManufacturerName (VstOpCodeArguments args)
1855     {
1856         String (JucePlugin_Manufacturer).copyToUTF8 ((char*) args.ptr, 64 + 1);
1857         return 1;
1858     }
1859 
handleGetManufacturerVersion(VstOpCodeArguments)1860     pointer_sized_int handleGetManufacturerVersion (VstOpCodeArguments)
1861     {
1862         return convertHexVersionToDecimal (JucePlugin_VersionCode);
1863     }
1864 
handleManufacturerSpecific(VstOpCodeArguments args)1865     pointer_sized_int handleManufacturerSpecific (VstOpCodeArguments args)
1866     {
1867         if (handleManufacturerSpecificVST2Opcode (args.index, args.value, args.ptr, args.opt))
1868             return 1;
1869 
1870         if (args.index == (int32) ByteOrder::bigEndianInt ("PreS")
1871              && args.value == (int32) ByteOrder::bigEndianInt ("AeCs"))
1872             return handleSetContentScaleFactor (args.opt);
1873 
1874         if (args.index == Vst2::effGetParamDisplay)
1875             return handleCockosGetParameterText (args.value, args.ptr, args.opt);
1876 
1877         if (auto callbackHandler = dynamic_cast<VSTCallbackHandler*> (processor))
1878             return callbackHandler->handleVstManufacturerSpecific (args.index, args.value, args.ptr, args.opt);
1879 
1880         return 0;
1881     }
1882 
handleCanPlugInDo(VstOpCodeArguments args)1883     pointer_sized_int handleCanPlugInDo (VstOpCodeArguments args)
1884     {
1885         auto text = (const char*) args.ptr;
1886         auto matches = [=] (const char* s) { return strcmp (text, s) == 0; };
1887 
1888         if (matches ("receiveVstEvents")
1889          || matches ("receiveVstMidiEvent")
1890          || matches ("receiveVstMidiEvents"))
1891         {
1892            #if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
1893             return 1;
1894            #else
1895             return -1;
1896            #endif
1897         }
1898 
1899         if (matches ("sendVstEvents")
1900          || matches ("sendVstMidiEvent")
1901          || matches ("sendVstMidiEvents"))
1902         {
1903            #if JucePlugin_ProducesMidiOutput || JucePlugin_IsMidiEffect
1904             return 1;
1905            #else
1906             return -1;
1907            #endif
1908         }
1909 
1910         if (matches ("receiveVstTimeInfo")
1911          || matches ("conformsToWindowRules")
1912          || matches ("supportsViewDpiScaling")
1913          || matches ("bypass"))
1914         {
1915             return 1;
1916         }
1917 
1918         // This tells Wavelab to use the UI thread to invoke open/close,
1919         // like all other hosts do.
1920         if (matches ("openCloseAnyThread"))
1921             return -1;
1922 
1923         if (matches ("MPE"))
1924             return processor->supportsMPE() ? 1 : 0;
1925 
1926        #if JUCE_MAC
1927         if (matches ("hasCockosViewAsConfig"))
1928         {
1929             useNSView = true;
1930             return (int32) 0xbeef0000;
1931         }
1932        #endif
1933 
1934         if (matches ("hasCockosExtensions"))
1935             return (int32) 0xbeef0000;
1936 
1937         if (auto callbackHandler = dynamic_cast<VSTCallbackHandler*> (processor))
1938             return callbackHandler->handleVstPluginCanDo (args.index, args.value, args.ptr, args.opt);
1939 
1940         return 0;
1941     }
1942 
handleGetTailSize(VstOpCodeArguments)1943     pointer_sized_int handleGetTailSize (VstOpCodeArguments)
1944     {
1945         if (processor != nullptr)
1946         {
1947             int32 result;
1948 
1949             auto tailSeconds = processor->getTailLengthSeconds();
1950 
1951             if (tailSeconds == std::numeric_limits<double>::infinity())
1952                 result = std::numeric_limits<int32>::max();
1953             else
1954                 result = static_cast<int32> (tailSeconds * sampleRate);
1955 
1956             return result; // Vst2 expects an int32 upcasted to a intptr_t here
1957         }
1958 
1959         return 0;
1960     }
1961 
handleKeyboardFocusRequired(VstOpCodeArguments)1962     pointer_sized_int handleKeyboardFocusRequired (VstOpCodeArguments)
1963     {
1964         return (JucePlugin_EditorRequiresKeyboardFocus != 0) ? 1 : 0;
1965     }
1966 
handleGetVstInterfaceVersion(VstOpCodeArguments)1967     pointer_sized_int handleGetVstInterfaceVersion (VstOpCodeArguments)
1968     {
1969         return kVstVersion;
1970     }
1971 
handleGetCurrentMidiProgram(VstOpCodeArguments)1972     pointer_sized_int handleGetCurrentMidiProgram (VstOpCodeArguments)
1973     {
1974         return -1;
1975     }
1976 
handleGetSpeakerConfiguration(VstOpCodeArguments args)1977     pointer_sized_int handleGetSpeakerConfiguration (VstOpCodeArguments args)
1978     {
1979         auto** pluginInput  = reinterpret_cast<Vst2::VstSpeakerArrangement**> (args.value);
1980         auto** pluginOutput = reinterpret_cast<Vst2::VstSpeakerArrangement**> (args.ptr);
1981 
1982         if (pluginHasSidechainsOrAuxs() || processor->isMidiEffect())
1983             return false;
1984 
1985         auto inputLayout  = processor->getChannelLayoutOfBus (true,  0);
1986         auto outputLayout = processor->getChannelLayoutOfBus (false,  0);
1987 
1988         auto speakerBaseSize = sizeof (Vst2::VstSpeakerArrangement) - (sizeof (Vst2::VstSpeakerProperties) * 8);
1989 
1990         cachedInArrangement .malloc (speakerBaseSize + (static_cast<std::size_t> (inputLayout. size()) * sizeof (Vst2::VstSpeakerArrangement)), 1);
1991         cachedOutArrangement.malloc (speakerBaseSize + (static_cast<std::size_t> (outputLayout.size()) * sizeof (Vst2::VstSpeakerArrangement)), 1);
1992 
1993         *pluginInput  = cachedInArrangement. getData();
1994         *pluginOutput = cachedOutArrangement.getData();
1995 
1996         SpeakerMappings::channelSetToVstArrangement (processor->getChannelLayoutOfBus (true,  0), **pluginInput);
1997         SpeakerMappings::channelSetToVstArrangement (processor->getChannelLayoutOfBus (false, 0), **pluginOutput);
1998 
1999         return 1;
2000     }
2001 
handleSetNumberOfSamplesToProcess(VstOpCodeArguments args)2002     pointer_sized_int handleSetNumberOfSamplesToProcess (VstOpCodeArguments args)
2003     {
2004         return args.value;
2005     }
2006 
handleSetSampleFloatType(VstOpCodeArguments args)2007     pointer_sized_int handleSetSampleFloatType (VstOpCodeArguments args)
2008     {
2009         if (! isProcessing)
2010         {
2011             if (processor != nullptr)
2012             {
2013                 processor->setProcessingPrecision ((args.value == Vst2::kVstProcessPrecision64
2014                                                      && processor->supportsDoublePrecisionProcessing())
2015                                                          ? AudioProcessor::doublePrecision
2016                                                          : AudioProcessor::singlePrecision);
2017 
2018                 return 1;
2019             }
2020         }
2021 
2022         return 0;
2023     }
2024 
handleSetContentScaleFactor(float scale)2025     pointer_sized_int handleSetContentScaleFactor (float scale)
2026     {
2027        #if ! JUCE_MAC
2028         if (editorComp != nullptr)
2029             editorComp->setContentScaleFactor (scale);
2030 
2031         lastScaleFactorReceived = scale;
2032        #else
2033         ignoreUnused (scale);
2034        #endif
2035 
2036         return 1;
2037     }
2038 
handleCockosGetParameterText(pointer_sized_int paramIndex,void * dest,float value)2039     pointer_sized_int handleCockosGetParameterText (pointer_sized_int paramIndex,
2040                                                     void* dest,
2041                                                     float value)
2042     {
2043         if (processor != nullptr && dest != nullptr)
2044         {
2045             if (auto* param = juceParameters.getParamForIndex ((int) paramIndex))
2046             {
2047                 if (! LegacyAudioParameter::isLegacy (param))
2048                 {
2049                     String text (param->getText (value, 1024));
2050                     memcpy (dest, text.toRawUTF8(), ((size_t) text.length()) + 1);
2051                     return 0xbeef;
2052                 }
2053             }
2054         }
2055 
2056         return 0;
2057     }
2058 
2059     //==============================================================================
handleGetNumMidiInputChannels()2060     pointer_sized_int handleGetNumMidiInputChannels()
2061     {
2062        #if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
2063         #ifdef JucePlugin_VSTNumMidiInputs
2064          return JucePlugin_VSTNumMidiInputs;
2065         #else
2066          return 16;
2067         #endif
2068        #else
2069         return 0;
2070        #endif
2071     }
2072 
handleGetNumMidiOutputChannels()2073     pointer_sized_int handleGetNumMidiOutputChannels()
2074     {
2075        #if JucePlugin_ProducesMidiOutput || JucePlugin_IsMidiEffect
2076         #ifdef JucePlugin_VSTNumMidiOutputs
2077          return JucePlugin_VSTNumMidiOutputs;
2078         #else
2079          return 16;
2080         #endif
2081        #else
2082         return 0;
2083        #endif
2084     }
2085 
2086     //==============================================================================
2087     Vst2::audioMasterCallback hostCallback;
2088     AudioProcessor* processor = {};
2089     double sampleRate = 44100.0;
2090     int32 blockSize = 1024;
2091     Vst2::AEffect vstEffect;
2092     CriticalSection stateInformationLock;
2093     juce::MemoryBlock chunkMemory;
2094     uint32 chunkMemoryTime = 0;
2095     std::unique_ptr<EditorCompWrapper> editorComp;
2096     Vst2::ERect editorRect;
2097     MidiBuffer midiEvents;
2098     VSTMidiEventList outgoingEvents;
2099 
2100    #if ! JUCE_MAC
2101     float lastScaleFactorReceived = 1.0f;
2102    #endif
2103 
2104     LegacyAudioParametersWrapper juceParameters;
2105 
2106     bool isProcessing = false, isBypassed = false, hasShutdown = false;
2107     bool firstProcessCallback = true, shouldDeleteEditor = false;
2108 
2109   #if JUCE_MAC
2110    #if JUCE_64BIT
2111     bool useNSView = true;
2112    #else
2113     bool useNSView = false;
2114    #endif
2115   #endif
2116 
2117     VstTempBuffers<float> floatTempBuffers;
2118     VstTempBuffers<double> doubleTempBuffers;
2119     int maxNumInChannels = 0, maxNumOutChannels = 0;
2120 
2121     HeapBlock<Vst2::VstSpeakerArrangement> cachedInArrangement, cachedOutArrangement;
2122 
2123     ThreadLocalValue<bool> inParameterChangedCallback;
2124 
2125     HostChangeUpdater hostChangeUpdater { *this };
2126 
2127     //==============================================================================
2128     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVSTWrapper)
2129 };
2130 
2131 
2132 //==============================================================================
2133 namespace
2134 {
pluginEntryPoint(Vst2::audioMasterCallback audioMaster)2135     Vst2::AEffect* pluginEntryPoint (Vst2::audioMasterCallback audioMaster)
2136     {
2137         JUCE_AUTORELEASEPOOL
2138         {
2139             initialiseJuce_GUI();
2140 
2141             try
2142             {
2143                 if (audioMaster (nullptr, Vst2::audioMasterVersion, 0, 0, nullptr, 0) != 0)
2144                 {
2145                    #if JUCE_LINUX || JUCE_BSD
2146                     MessageManagerLock mmLock;
2147                    #endif
2148 
2149                     auto* processor = createPluginFilterOfType (AudioProcessor::wrapperType_VST);
2150                     auto* wrapper = new JuceVSTWrapper (audioMaster, processor);
2151                     auto* aEffect = wrapper->getAEffect();
2152 
2153                     if (auto* callbackHandler = dynamic_cast<VSTCallbackHandler*> (processor))
2154                     {
2155                         callbackHandler->handleVstHostCallbackAvailable ([audioMaster, aEffect] (int32 opcode, int32 index, pointer_sized_int value, void* ptr, float opt)
2156                         {
2157                             return audioMaster (aEffect, opcode, index, value, ptr, opt);
2158                         });
2159                     }
2160 
2161                     return aEffect;
2162                 }
2163             }
2164             catch (...)
2165             {}
2166         }
2167 
2168         return nullptr;
2169     }
2170 }
2171 
2172 #if ! JUCE_WINDOWS
2173  #define JUCE_EXPORTED_FUNCTION extern "C" __attribute__ ((visibility("default")))
2174 #endif
2175 
2176 //==============================================================================
2177 // Mac startup code..
2178 #if JUCE_MAC
2179 
2180     JUCE_EXPORTED_FUNCTION Vst2::AEffect* VSTPluginMain (Vst2::audioMasterCallback audioMaster);
VSTPluginMain(Vst2::audioMasterCallback audioMaster)2181     JUCE_EXPORTED_FUNCTION Vst2::AEffect* VSTPluginMain (Vst2::audioMasterCallback audioMaster)
2182     {
2183         PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_VST;
2184 
2185         initialiseMacVST();
2186         return pluginEntryPoint (audioMaster);
2187     }
2188 
2189     JUCE_EXPORTED_FUNCTION Vst2::AEffect* main_macho (Vst2::audioMasterCallback audioMaster);
main_macho(Vst2::audioMasterCallback audioMaster)2190     JUCE_EXPORTED_FUNCTION Vst2::AEffect* main_macho (Vst2::audioMasterCallback audioMaster)
2191     {
2192         PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_VST;
2193 
2194         initialiseMacVST();
2195         return pluginEntryPoint (audioMaster);
2196     }
2197 
2198 //==============================================================================
2199 // Linux startup code..
2200 #elif JUCE_LINUX || JUCE_BSD
2201 
2202     JUCE_EXPORTED_FUNCTION Vst2::AEffect* VSTPluginMain (Vst2::audioMasterCallback audioMaster);
VSTPluginMain(Vst2::audioMasterCallback audioMaster)2203     JUCE_EXPORTED_FUNCTION Vst2::AEffect* VSTPluginMain (Vst2::audioMasterCallback audioMaster)
2204     {
2205         PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_VST;
2206 
2207         SharedMessageThread::getInstance();
2208         return pluginEntryPoint (audioMaster);
2209     }
2210 
2211     JUCE_EXPORTED_FUNCTION Vst2::AEffect* main_plugin (Vst2::audioMasterCallback audioMaster) asm ("main");
main_plugin(Vst2::audioMasterCallback audioMaster)2212     JUCE_EXPORTED_FUNCTION Vst2::AEffect* main_plugin (Vst2::audioMasterCallback audioMaster)
2213     {
2214         PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_VST;
2215 
2216         return VSTPluginMain (audioMaster);
2217     }
2218 
2219     // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash!
myPluginInit()2220     __attribute__((constructor)) void myPluginInit() {}
myPluginFini()2221     __attribute__((destructor))  void myPluginFini() {}
2222 
2223 //==============================================================================
2224 // Win32 startup code..
2225 #else
2226 
VSTPluginMain(Vst2::audioMasterCallback audioMaster)2227     extern "C" __declspec (dllexport) Vst2::AEffect* VSTPluginMain (Vst2::audioMasterCallback audioMaster)
2228     {
2229         PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_VST;
2230 
2231         return pluginEntryPoint (audioMaster);
2232     }
2233 
2234    #if ! defined (JUCE_64BIT) && JUCE_MSVC // (can't compile this on win64, but it's not needed anyway with VST2.4)
main(Vst2::audioMasterCallback audioMaster)2235     extern "C" __declspec (dllexport) int main (Vst2::audioMasterCallback audioMaster)
2236     {
2237         PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_VST;
2238 
2239         return (int) pluginEntryPoint (audioMaster);
2240     }
2241    #endif
2242 
DllMain(HINSTANCE instance,DWORD reason,LPVOID)2243     extern "C" BOOL WINAPI DllMain (HINSTANCE instance, DWORD reason, LPVOID)
2244     {
2245         if (reason == DLL_PROCESS_ATTACH)
2246             Process::setCurrentModuleInstanceHandle (instance);
2247 
2248         return true;
2249     }
2250 #endif
2251 
2252 JUCE_END_IGNORE_WARNINGS_MSVC
2253 
2254 #endif
2255