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