The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2948 lines
88KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../../../core/juce_TargetPlatform.h"
  19. #include "../../../../juce_Config.h"
  20. #if JUCE_PLUGINHOST_VST && (JUCE_MAC_VST_INCLUDED || ! JUCE_MAC)
  21. #if JUCE_WINDOWS
  22. #undef _WIN32_WINNT
  23. #define _WIN32_WINNT 0x500
  24. #undef STRICT
  25. #define STRICT
  26. #include <windows.h>
  27. #include <float.h>
  28. #pragma warning (disable : 4312 4355)
  29. #ifdef __INTEL_COMPILER
  30. #pragma warning (disable : 1899)
  31. #endif
  32. #elif JUCE_LINUX
  33. #include <float.h>
  34. #include <sys/time.h>
  35. #include <X11/Xlib.h>
  36. #include <X11/Xutil.h>
  37. #include <X11/Xatom.h>
  38. #undef Font
  39. #undef KeyPress
  40. #undef Drawable
  41. #undef Time
  42. #else
  43. #include <Cocoa/Cocoa.h>
  44. #include <Carbon/Carbon.h>
  45. #endif
  46. //==============================================================================
  47. #include "../../../core/juce_StandardHeader.h"
  48. #if ! (JUCE_MAC && JUCE_64BIT)
  49. BEGIN_JUCE_NAMESPACE
  50. #include "juce_VSTPluginFormat.h"
  51. #include "../../../threads/juce_Process.h"
  52. #include "../../../threads/juce_CriticalSection.h"
  53. #include "../../../threads/juce_DynamicLibrary.h"
  54. #include "../../../maths/juce_Random.h"
  55. #include "../../../io/files/juce_DirectoryIterator.h"
  56. #include "../../../events/juce_Timer.h"
  57. #include "../../../events/juce_AsyncUpdater.h"
  58. #include "../../../events/juce_MessageManager.h"
  59. #include "../../../gui/components/layout/juce_ComponentMovementWatcher.h"
  60. #include "../../../gui/components/windows/juce_ComponentPeer.h"
  61. #include "../../../application/juce_Application.h"
  62. #if JUCE_MAC && JUCE_SUPPORT_CARBON
  63. #include "../../../native/mac/juce_mac_CarbonViewWrapperComponent.h"
  64. #endif
  65. #if JUCE_MAC
  66. static bool makeFSRefFromPath (FSRef* destFSRef, const String& path)
  67. {
  68. return FSPathMakeRef (reinterpret_cast <const UInt8*> (path.toUTF8().getAddress()), destFSRef, 0) == noErr;
  69. }
  70. #endif
  71. //==============================================================================
  72. #undef PRAGMA_ALIGN_SUPPORTED
  73. #define VST_FORCE_DEPRECATED 0
  74. #if JUCE_MSVC
  75. #pragma warning (push)
  76. #pragma warning (disable: 4996)
  77. #endif
  78. /* Obviously you're going to need the Steinberg vstsdk2.4 folder in
  79. your include path if you want to add VST support.
  80. If you're not interested in VSTs, you can disable them by changing the
  81. JUCE_PLUGINHOST_VST flag in juce_Config.h
  82. */
  83. #include <pluginterfaces/vst2.x/aeffectx.h>
  84. #if JUCE_MSVC
  85. #pragma warning (pop)
  86. #endif
  87. //==============================================================================
  88. #if JUCE_LINUX
  89. #define Font JUCE_NAMESPACE::Font
  90. #define KeyPress JUCE_NAMESPACE::KeyPress
  91. #define Drawable JUCE_NAMESPACE::Drawable
  92. #define Time JUCE_NAMESPACE::Time
  93. #endif
  94. #include "../juce_PluginDescription.h"
  95. #include "juce_VSTMidiEventList.h"
  96. #if ! JUCE_WINDOWS
  97. static void _fpreset() {}
  98. static void _clearfp() {}
  99. #endif
  100. extern void JUCE_API juce_callAnyTimersSynchronously();
  101. //==============================================================================
  102. const int fxbVersionNum = 1;
  103. struct fxProgram
  104. {
  105. long chunkMagic; // 'CcnK'
  106. long byteSize; // of this chunk, excl. magic + byteSize
  107. long fxMagic; // 'FxCk'
  108. long version;
  109. long fxID; // fx unique id
  110. long fxVersion;
  111. long numParams;
  112. char prgName[28];
  113. float params[1]; // variable no. of parameters
  114. };
  115. struct fxSet
  116. {
  117. long chunkMagic; // 'CcnK'
  118. long byteSize; // of this chunk, excl. magic + byteSize
  119. long fxMagic; // 'FxBk'
  120. long version;
  121. long fxID; // fx unique id
  122. long fxVersion;
  123. long numPrograms;
  124. char future[128];
  125. fxProgram programs[1]; // variable no. of programs
  126. };
  127. struct fxChunkSet
  128. {
  129. long chunkMagic; // 'CcnK'
  130. long byteSize; // of this chunk, excl. magic + byteSize
  131. long fxMagic; // 'FxCh', 'FPCh', or 'FBCh'
  132. long version;
  133. long fxID; // fx unique id
  134. long fxVersion;
  135. long numPrograms;
  136. char future[128];
  137. long chunkSize;
  138. char chunk[8]; // variable
  139. };
  140. struct fxProgramSet
  141. {
  142. long chunkMagic; // 'CcnK'
  143. long byteSize; // of this chunk, excl. magic + byteSize
  144. long fxMagic; // 'FxCh', 'FPCh', or 'FBCh'
  145. long version;
  146. long fxID; // fx unique id
  147. long fxVersion;
  148. long numPrograms;
  149. char name[28];
  150. long chunkSize;
  151. char chunk[8]; // variable
  152. };
  153. namespace
  154. {
  155. long vst_swap (const long x) noexcept
  156. {
  157. #ifdef JUCE_LITTLE_ENDIAN
  158. return (long) ByteOrder::swap ((uint32) x);
  159. #else
  160. return x;
  161. #endif
  162. }
  163. float vst_swapFloat (const float x) noexcept
  164. {
  165. #ifdef JUCE_LITTLE_ENDIAN
  166. union { uint32 asInt; float asFloat; } n;
  167. n.asFloat = x;
  168. n.asInt = ByteOrder::swap (n.asInt);
  169. return n.asFloat;
  170. #else
  171. return x;
  172. #endif
  173. }
  174. double getVSTHostTimeNanoseconds()
  175. {
  176. #if JUCE_WINDOWS
  177. return timeGetTime() * 1000000.0;
  178. #elif JUCE_LINUX
  179. timeval micro;
  180. gettimeofday (&micro, 0);
  181. return micro.tv_usec * 1000.0;
  182. #elif JUCE_MAC
  183. UnsignedWide micro;
  184. Microseconds (&micro);
  185. return micro.lo * 1000.0;
  186. #endif
  187. }
  188. }
  189. //==============================================================================
  190. typedef AEffect* (VSTCALLBACK *MainCall) (audioMasterCallback);
  191. static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt);
  192. static int shellUIDToCreate = 0;
  193. static int insideVSTCallback = 0;
  194. class IdleCallRecursionPreventer
  195. {
  196. public:
  197. IdleCallRecursionPreventer()
  198. : isMessageThread (MessageManager::getInstance()->isThisTheMessageThread())
  199. {
  200. if (isMessageThread)
  201. ++insideVSTCallback;
  202. }
  203. ~IdleCallRecursionPreventer()
  204. {
  205. if (isMessageThread)
  206. --insideVSTCallback;
  207. }
  208. private:
  209. const bool isMessageThread;
  210. JUCE_DECLARE_NON_COPYABLE (IdleCallRecursionPreventer);
  211. };
  212. class VSTPluginWindow;
  213. //==============================================================================
  214. // Change this to disable logging of various VST activities
  215. #ifndef VST_LOGGING
  216. #define VST_LOGGING 1
  217. #endif
  218. #if VST_LOGGING
  219. #define log(a) Logger::writeToLog(a);
  220. #else
  221. #define log(a)
  222. #endif
  223. //==============================================================================
  224. #if JUCE_MAC && JUCE_PPC
  225. static void* NewCFMFromMachO (void* const machofp) noexcept
  226. {
  227. void* result = (void*) new char[8];
  228. ((void**) result)[0] = machofp;
  229. ((void**) result)[1] = result;
  230. return result;
  231. }
  232. #endif
  233. //==============================================================================
  234. #if JUCE_LINUX
  235. extern Display* display;
  236. extern XContext windowHandleXContext;
  237. typedef void (*EventProcPtr) (XEvent* ev);
  238. static bool xErrorTriggered;
  239. namespace
  240. {
  241. int temporaryErrorHandler (Display*, XErrorEvent*)
  242. {
  243. xErrorTriggered = true;
  244. return 0;
  245. }
  246. int getPropertyFromXWindow (Window handle, Atom atom)
  247. {
  248. XErrorHandler oldErrorHandler = XSetErrorHandler (temporaryErrorHandler);
  249. xErrorTriggered = false;
  250. int userSize;
  251. unsigned long bytes, userCount;
  252. unsigned char* data;
  253. Atom userType;
  254. XGetWindowProperty (display, handle, atom, 0, 1, false, AnyPropertyType,
  255. &userType, &userSize, &userCount, &bytes, &data);
  256. XSetErrorHandler (oldErrorHandler);
  257. return (userCount == 1 && ! xErrorTriggered) ? *reinterpret_cast<int*> (data)
  258. : 0;
  259. }
  260. Window getChildWindow (Window windowToCheck)
  261. {
  262. Window rootWindow, parentWindow;
  263. Window* childWindows;
  264. unsigned int numChildren;
  265. XQueryTree (display,
  266. windowToCheck,
  267. &rootWindow,
  268. &parentWindow,
  269. &childWindows,
  270. &numChildren);
  271. if (numChildren > 0)
  272. return childWindows [0];
  273. return 0;
  274. }
  275. void translateJuceToXButtonModifiers (const MouseEvent& e, XEvent& ev) noexcept
  276. {
  277. if (e.mods.isLeftButtonDown())
  278. {
  279. ev.xbutton.button = Button1;
  280. ev.xbutton.state |= Button1Mask;
  281. }
  282. else if (e.mods.isRightButtonDown())
  283. {
  284. ev.xbutton.button = Button3;
  285. ev.xbutton.state |= Button3Mask;
  286. }
  287. else if (e.mods.isMiddleButtonDown())
  288. {
  289. ev.xbutton.button = Button2;
  290. ev.xbutton.state |= Button2Mask;
  291. }
  292. }
  293. void translateJuceToXMotionModifiers (const MouseEvent& e, XEvent& ev) noexcept
  294. {
  295. if (e.mods.isLeftButtonDown()) ev.xmotion.state |= Button1Mask;
  296. else if (e.mods.isRightButtonDown()) ev.xmotion.state |= Button3Mask;
  297. else if (e.mods.isMiddleButtonDown()) ev.xmotion.state |= Button2Mask;
  298. }
  299. void translateJuceToXCrossingModifiers (const MouseEvent& e, XEvent& ev) noexcept
  300. {
  301. if (e.mods.isLeftButtonDown()) ev.xcrossing.state |= Button1Mask;
  302. else if (e.mods.isRightButtonDown()) ev.xcrossing.state |= Button3Mask;
  303. else if (e.mods.isMiddleButtonDown()) ev.xcrossing.state |= Button2Mask;
  304. }
  305. void translateJuceToXMouseWheelModifiers (const MouseEvent& e, const float increment, XEvent& ev) noexcept
  306. {
  307. if (increment < 0)
  308. {
  309. ev.xbutton.button = Button5;
  310. ev.xbutton.state |= Button5Mask;
  311. }
  312. else if (increment > 0)
  313. {
  314. ev.xbutton.button = Button4;
  315. ev.xbutton.state |= Button4Mask;
  316. }
  317. }
  318. }
  319. #endif
  320. //==============================================================================
  321. class ModuleHandle : public ReferenceCountedObject
  322. {
  323. public:
  324. //==============================================================================
  325. File file;
  326. MainCall moduleMain;
  327. String pluginName;
  328. static Array <ModuleHandle*>& getActiveModules()
  329. {
  330. static Array <ModuleHandle*> activeModules;
  331. return activeModules;
  332. }
  333. //==============================================================================
  334. static ModuleHandle* findOrCreateModule (const File& file)
  335. {
  336. for (int i = getActiveModules().size(); --i >= 0;)
  337. {
  338. ModuleHandle* const module = getActiveModules().getUnchecked(i);
  339. if (module->file == file)
  340. return module;
  341. }
  342. _fpreset(); // (doesn't do any harm)
  343. const IdleCallRecursionPreventer icrp;
  344. shellUIDToCreate = 0;
  345. log ("Attempting to load VST: " + file.getFullPathName());
  346. ScopedPointer <ModuleHandle> m (new ModuleHandle (file));
  347. if (! m->open())
  348. m = nullptr;
  349. _fpreset(); // (doesn't do any harm)
  350. return m.release();
  351. }
  352. //==============================================================================
  353. ModuleHandle (const File& file_)
  354. : file (file_),
  355. moduleMain (0)
  356. #if JUCE_MAC
  357. , fragId (0), resHandle (0), bundleRef (0), resFileId (0)
  358. #endif
  359. {
  360. getActiveModules().add (this);
  361. #if JUCE_WINDOWS || JUCE_LINUX
  362. fullParentDirectoryPathName = file_.getParentDirectory().getFullPathName();
  363. #elif JUCE_MAC
  364. FSRef ref;
  365. makeFSRefFromPath (&ref, file_.getParentDirectory().getFullPathName());
  366. FSGetCatalogInfo (&ref, kFSCatInfoNone, 0, 0, &parentDirFSSpec, 0);
  367. #endif
  368. }
  369. ~ModuleHandle()
  370. {
  371. getActiveModules().removeValue (this);
  372. close();
  373. }
  374. //==============================================================================
  375. #if JUCE_WINDOWS || JUCE_LINUX
  376. DynamicLibrary module;
  377. String fullParentDirectoryPathName;
  378. bool open()
  379. {
  380. #if JUCE_WINDOWS
  381. static bool timePeriodSet = false;
  382. if (! timePeriodSet)
  383. {
  384. timePeriodSet = true;
  385. timeBeginPeriod (2);
  386. }
  387. #endif
  388. pluginName = file.getFileNameWithoutExtension();
  389. module.open (file.getFullPathName());
  390. moduleMain = (MainCall) module.getFunction ("VSTPluginMain");
  391. if (moduleMain == nullptr)
  392. moduleMain = (MainCall) module.getFunction ("main");
  393. return moduleMain != nullptr;
  394. }
  395. void close()
  396. {
  397. _fpreset(); // (doesn't do any harm)
  398. module.close();
  399. }
  400. void closeEffect (AEffect* eff)
  401. {
  402. eff->dispatcher (eff, effClose, 0, 0, 0, 0);
  403. }
  404. #else
  405. CFragConnectionID fragId;
  406. Handle resHandle;
  407. CFBundleRef bundleRef;
  408. FSSpec parentDirFSSpec;
  409. short resFileId;
  410. bool open()
  411. {
  412. bool ok = false;
  413. const String filename (file.getFullPathName());
  414. if (file.hasFileExtension (".vst"))
  415. {
  416. const char* const utf8 = filename.toUTF8().getAddress();
  417. CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8,
  418. strlen (utf8), file.isDirectory());
  419. if (url != 0)
  420. {
  421. bundleRef = CFBundleCreate (kCFAllocatorDefault, url);
  422. CFRelease (url);
  423. if (bundleRef != 0)
  424. {
  425. if (CFBundleLoadExecutable (bundleRef))
  426. {
  427. moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("main_macho"));
  428. if (moduleMain == 0)
  429. moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("VSTPluginMain"));
  430. if (moduleMain != 0)
  431. {
  432. CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName"));
  433. if (name != 0)
  434. {
  435. if (CFGetTypeID (name) == CFStringGetTypeID())
  436. {
  437. char buffer[1024];
  438. if (CFStringGetCString ((CFStringRef) name, buffer, sizeof (buffer), CFStringGetSystemEncoding()))
  439. pluginName = buffer;
  440. }
  441. }
  442. if (pluginName.isEmpty())
  443. pluginName = file.getFileNameWithoutExtension();
  444. resFileId = CFBundleOpenBundleResourceMap (bundleRef);
  445. ok = true;
  446. }
  447. }
  448. if (! ok)
  449. {
  450. CFBundleUnloadExecutable (bundleRef);
  451. CFRelease (bundleRef);
  452. bundleRef = 0;
  453. }
  454. }
  455. }
  456. }
  457. #if JUCE_PPC
  458. else
  459. {
  460. FSRef fn;
  461. if (FSPathMakeRef ((UInt8*) filename.toUTF8().getAddress(), &fn, 0) == noErr)
  462. {
  463. resFileId = FSOpenResFile (&fn, fsRdPerm);
  464. if (resFileId != -1)
  465. {
  466. const int numEffs = Count1Resources ('aEff');
  467. for (int i = 0; i < numEffs; ++i)
  468. {
  469. resHandle = Get1IndResource ('aEff', i + 1);
  470. if (resHandle != 0)
  471. {
  472. OSType type;
  473. Str255 name;
  474. SInt16 id;
  475. GetResInfo (resHandle, &id, &type, name);
  476. pluginName = String ((const char*) name + 1, name[0]);
  477. DetachResource (resHandle);
  478. HLock (resHandle);
  479. Ptr ptr;
  480. Str255 errorText;
  481. OSErr err = GetMemFragment (*resHandle, GetHandleSize (resHandle),
  482. name, kPrivateCFragCopy,
  483. &fragId, &ptr, errorText);
  484. if (err == noErr)
  485. {
  486. moduleMain = (MainCall) newMachOFromCFM (ptr);
  487. ok = true;
  488. }
  489. else
  490. {
  491. HUnlock (resHandle);
  492. }
  493. break;
  494. }
  495. }
  496. if (! ok)
  497. CloseResFile (resFileId);
  498. }
  499. }
  500. }
  501. #endif
  502. return ok;
  503. }
  504. void close()
  505. {
  506. #if JUCE_PPC
  507. if (fragId != 0)
  508. {
  509. if (moduleMain != 0)
  510. disposeMachOFromCFM ((void*) moduleMain);
  511. CloseConnection (&fragId);
  512. HUnlock (resHandle);
  513. if (resFileId != 0)
  514. CloseResFile (resFileId);
  515. }
  516. else
  517. #endif
  518. if (bundleRef != 0)
  519. {
  520. CFBundleCloseBundleResourceMap (bundleRef, resFileId);
  521. if (CFGetRetainCount (bundleRef) == 1)
  522. CFBundleUnloadExecutable (bundleRef);
  523. if (CFGetRetainCount (bundleRef) > 0)
  524. CFRelease (bundleRef);
  525. }
  526. }
  527. void closeEffect (AEffect* eff)
  528. {
  529. #if JUCE_PPC
  530. if (fragId != 0)
  531. {
  532. Array<void*> thingsToDelete;
  533. thingsToDelete.add ((void*) eff->dispatcher);
  534. thingsToDelete.add ((void*) eff->process);
  535. thingsToDelete.add ((void*) eff->setParameter);
  536. thingsToDelete.add ((void*) eff->getParameter);
  537. thingsToDelete.add ((void*) eff->processReplacing);
  538. eff->dispatcher (eff, effClose, 0, 0, 0, 0);
  539. for (int i = thingsToDelete.size(); --i >= 0;)
  540. disposeMachOFromCFM (thingsToDelete[i]);
  541. }
  542. else
  543. #endif
  544. {
  545. eff->dispatcher (eff, effClose, 0, 0, 0, 0);
  546. }
  547. }
  548. #if JUCE_PPC
  549. static void* newMachOFromCFM (void* cfmfp)
  550. {
  551. if (cfmfp == 0)
  552. return nullptr;
  553. UInt32* const mfp = new UInt32[6];
  554. mfp[0] = 0x3d800000 | ((UInt32) cfmfp >> 16);
  555. mfp[1] = 0x618c0000 | ((UInt32) cfmfp & 0xffff);
  556. mfp[2] = 0x800c0000;
  557. mfp[3] = 0x804c0004;
  558. mfp[4] = 0x7c0903a6;
  559. mfp[5] = 0x4e800420;
  560. MakeDataExecutable (mfp, sizeof (UInt32) * 6);
  561. return mfp;
  562. }
  563. static void disposeMachOFromCFM (void* ptr)
  564. {
  565. delete[] static_cast <UInt32*> (ptr);
  566. }
  567. void coerceAEffectFunctionCalls (AEffect* eff)
  568. {
  569. if (fragId != 0)
  570. {
  571. eff->dispatcher = (AEffectDispatcherProc) newMachOFromCFM ((void*) eff->dispatcher);
  572. eff->process = (AEffectProcessProc) newMachOFromCFM ((void*) eff->process);
  573. eff->setParameter = (AEffectSetParameterProc) newMachOFromCFM ((void*) eff->setParameter);
  574. eff->getParameter = (AEffectGetParameterProc) newMachOFromCFM ((void*) eff->getParameter);
  575. eff->processReplacing = (AEffectProcessProc) newMachOFromCFM ((void*) eff->processReplacing);
  576. }
  577. }
  578. #endif
  579. #endif
  580. private:
  581. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModuleHandle);
  582. };
  583. //==============================================================================
  584. /**
  585. An instance of a plugin, created by a VSTPluginFormat.
  586. */
  587. class VSTPluginInstance : public AudioPluginInstance,
  588. private Timer,
  589. private AsyncUpdater
  590. {
  591. public:
  592. //==============================================================================
  593. ~VSTPluginInstance();
  594. //==============================================================================
  595. // AudioPluginInstance methods:
  596. void fillInPluginDescription (PluginDescription& desc) const
  597. {
  598. desc.name = name;
  599. {
  600. char buffer [512] = { 0 };
  601. dispatch (effGetEffectName, 0, 0, buffer, 0);
  602. desc.descriptiveName = String (buffer).trim();
  603. if (desc.descriptiveName.isEmpty())
  604. desc.descriptiveName = name;
  605. }
  606. desc.fileOrIdentifier = module->file.getFullPathName();
  607. desc.uid = getUID();
  608. desc.lastFileModTime = module->file.getLastModificationTime();
  609. desc.pluginFormatName = "VST";
  610. desc.category = getCategory();
  611. {
  612. char buffer [kVstMaxVendorStrLen + 8] = { 0 };
  613. dispatch (effGetVendorString, 0, 0, buffer, 0);
  614. desc.manufacturerName = buffer;
  615. }
  616. desc.version = getVersion();
  617. desc.numInputChannels = getNumInputChannels();
  618. desc.numOutputChannels = getNumOutputChannels();
  619. desc.isInstrument = (effect != nullptr && (effect->flags & effFlagsIsSynth) != 0);
  620. }
  621. void* getPlatformSpecificData() { return effect; }
  622. const String getName() const { return name; }
  623. int getUID() const;
  624. bool acceptsMidi() const { return wantsMidiMessages; }
  625. bool producesMidi() const { return dispatch (effCanDo, 0, 0, (void*) "sendVstMidiEvent", 0) > 0; }
  626. //==============================================================================
  627. // AudioProcessor methods:
  628. void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock);
  629. void releaseResources();
  630. void processBlock (AudioSampleBuffer& buffer,
  631. MidiBuffer& midiMessages);
  632. bool hasEditor() const { return effect != nullptr && (effect->flags & effFlagsHasEditor) != 0; }
  633. AudioProcessorEditor* createEditor();
  634. const String getInputChannelName (int index) const;
  635. bool isInputChannelStereoPair (int index) const;
  636. const String getOutputChannelName (int index) const;
  637. bool isOutputChannelStereoPair (int index) const;
  638. //==============================================================================
  639. int getNumParameters() { return effect != nullptr ? effect->numParams : 0; }
  640. float getParameter (int index);
  641. void setParameter (int index, float newValue);
  642. const String getParameterName (int index);
  643. const String getParameterText (int index);
  644. bool isParameterAutomatable (int index) const;
  645. //==============================================================================
  646. int getNumPrograms() { return effect != nullptr ? effect->numPrograms : 0; }
  647. int getCurrentProgram() { return dispatch (effGetProgram, 0, 0, 0, 0); }
  648. void setCurrentProgram (int index);
  649. const String getProgramName (int index);
  650. void changeProgramName (int index, const String& newName);
  651. //==============================================================================
  652. void getStateInformation (MemoryBlock& destData);
  653. void getCurrentProgramStateInformation (MemoryBlock& destData);
  654. void setStateInformation (const void* data, int sizeInBytes);
  655. void setCurrentProgramStateInformation (const void* data, int sizeInBytes);
  656. //==============================================================================
  657. void timerCallback();
  658. void handleAsyncUpdate();
  659. VstIntPtr handleCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt);
  660. private:
  661. //==============================================================================
  662. friend class VSTPluginWindow;
  663. friend class VSTPluginFormat;
  664. AEffect* effect;
  665. String name;
  666. CriticalSection lock;
  667. bool wantsMidiMessages, initialised, isPowerOn;
  668. mutable StringArray programNames;
  669. AudioSampleBuffer tempBuffer;
  670. CriticalSection midiInLock;
  671. MidiBuffer incomingMidi;
  672. VSTMidiEventList midiEventsToSend;
  673. VstTimeInfo vstHostTime;
  674. ReferenceCountedObjectPtr <ModuleHandle> module;
  675. //==============================================================================
  676. int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) const;
  677. bool restoreProgramSettings (const fxProgram* const prog);
  678. const String getCurrentProgramName();
  679. void setParamsInProgramBlock (fxProgram* const prog);
  680. void updateStoredProgramNames();
  681. void initialise();
  682. void handleMidiFromPlugin (const VstEvents* const events);
  683. void createTempParameterStore (MemoryBlock& dest);
  684. void restoreFromTempParameterStore (const MemoryBlock& mb);
  685. const String getParameterLabel (int index) const;
  686. bool usesChunks() const noexcept { return effect != nullptr && (effect->flags & effFlagsProgramChunks) != 0; }
  687. void getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const;
  688. void setChunkData (const char* data, int size, bool isPreset);
  689. bool loadFromFXBFile (const void* data, int numBytes);
  690. bool saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB);
  691. int getVersionNumber() const noexcept { return effect != nullptr ? effect->version : 0; }
  692. const String getVersion() const;
  693. const String getCategory() const;
  694. void setPower (const bool on);
  695. VSTPluginInstance (const ReferenceCountedObjectPtr <ModuleHandle>& module);
  696. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginInstance);
  697. };
  698. //==============================================================================
  699. VSTPluginInstance::VSTPluginInstance (const ReferenceCountedObjectPtr <ModuleHandle>& module_)
  700. : effect (nullptr),
  701. name (module_->pluginName),
  702. wantsMidiMessages (false),
  703. initialised (false),
  704. isPowerOn (false),
  705. tempBuffer (1, 1),
  706. module (module_)
  707. {
  708. try
  709. {
  710. const IdleCallRecursionPreventer icrp;
  711. _fpreset();
  712. log ("Creating VST instance: " + name);
  713. #if JUCE_MAC
  714. if (module->resFileId != 0)
  715. UseResFile (module->resFileId);
  716. #if JUCE_PPC
  717. if (module->fragId != 0)
  718. {
  719. static void* audioMasterCoerced = nullptr;
  720. if (audioMasterCoerced == nullptr)
  721. audioMasterCoerced = NewCFMFromMachO ((void*) &audioMaster);
  722. effect = module->moduleMain ((audioMasterCallback) audioMasterCoerced);
  723. }
  724. else
  725. #endif
  726. #endif
  727. {
  728. effect = module->moduleMain (&audioMaster);
  729. }
  730. if (effect != nullptr && effect->magic == kEffectMagic)
  731. {
  732. #if JUCE_PPC
  733. module->coerceAEffectFunctionCalls (effect);
  734. #endif
  735. jassert (effect->resvd2 == 0);
  736. jassert (effect->object != 0);
  737. _fpreset(); // some dodgy plugs fuck around with this
  738. }
  739. else
  740. {
  741. effect = nullptr;
  742. }
  743. }
  744. catch (...)
  745. {}
  746. }
  747. VSTPluginInstance::~VSTPluginInstance()
  748. {
  749. const ScopedLock sl (lock);
  750. if (effect != nullptr && effect->magic == kEffectMagic)
  751. {
  752. try
  753. {
  754. #if JUCE_MAC
  755. if (module->resFileId != 0)
  756. UseResFile (module->resFileId);
  757. #endif
  758. // Must delete any editors before deleting the plugin instance!
  759. jassert (getActiveEditor() == 0);
  760. _fpreset(); // some dodgy plugs fuck around with this
  761. module->closeEffect (effect);
  762. }
  763. catch (...)
  764. {}
  765. }
  766. module = nullptr;
  767. effect = nullptr;
  768. }
  769. //==============================================================================
  770. void VSTPluginInstance::initialise()
  771. {
  772. if (initialised || effect == 0)
  773. return;
  774. log ("Initialising VST: " + module->pluginName);
  775. initialised = true;
  776. dispatch (effIdentify, 0, 0, 0, 0);
  777. if (getSampleRate() > 0)
  778. dispatch (effSetSampleRate, 0, 0, 0, (float) getSampleRate());
  779. if (getBlockSize() > 0)
  780. dispatch (effSetBlockSize, 0, jmax (32, getBlockSize()), 0, 0);
  781. dispatch (effOpen, 0, 0, 0, 0);
  782. setPlayConfigDetails (effect->numInputs, effect->numOutputs,
  783. getSampleRate(), getBlockSize());
  784. if (getNumPrograms() > 1)
  785. setCurrentProgram (0);
  786. else
  787. dispatch (effSetProgram, 0, 0, 0, 0);
  788. int i;
  789. for (i = effect->numInputs; --i >= 0;)
  790. dispatch (effConnectInput, i, 1, 0, 0);
  791. for (i = effect->numOutputs; --i >= 0;)
  792. dispatch (effConnectOutput, i, 1, 0, 0);
  793. updateStoredProgramNames();
  794. wantsMidiMessages = dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0;
  795. setLatencySamples (effect->initialDelay);
  796. }
  797. //==============================================================================
  798. void VSTPluginInstance::prepareToPlay (double sampleRate_,
  799. int samplesPerBlockExpected)
  800. {
  801. setPlayConfigDetails (effect->numInputs, effect->numOutputs,
  802. sampleRate_, samplesPerBlockExpected);
  803. setLatencySamples (effect->initialDelay);
  804. vstHostTime.tempo = 120.0;
  805. vstHostTime.timeSigNumerator = 4;
  806. vstHostTime.timeSigDenominator = 4;
  807. vstHostTime.sampleRate = sampleRate_;
  808. vstHostTime.samplePos = 0;
  809. vstHostTime.flags = kVstNanosValid; /*| kVstTransportPlaying | kVstTempoValid | kVstTimeSigValid*/;
  810. initialise();
  811. if (initialised)
  812. {
  813. wantsMidiMessages = wantsMidiMessages
  814. || (dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0);
  815. if (wantsMidiMessages)
  816. midiEventsToSend.ensureSize (256);
  817. else
  818. midiEventsToSend.freeEvents();
  819. incomingMidi.clear();
  820. dispatch (effSetSampleRate, 0, 0, 0, (float) sampleRate_);
  821. dispatch (effSetBlockSize, 0, jmax (16, samplesPerBlockExpected), 0, 0);
  822. tempBuffer.setSize (jmax (1, effect->numOutputs), samplesPerBlockExpected);
  823. if (! isPowerOn)
  824. setPower (true);
  825. // dodgy hack to force some plugins to initialise the sample rate..
  826. if ((! hasEditor()) && getNumParameters() > 0)
  827. {
  828. const float old = getParameter (0);
  829. setParameter (0, (old < 0.5f) ? 1.0f : 0.0f);
  830. setParameter (0, old);
  831. }
  832. dispatch (effStartProcess, 0, 0, 0, 0);
  833. }
  834. }
  835. void VSTPluginInstance::releaseResources()
  836. {
  837. if (initialised)
  838. {
  839. dispatch (effStopProcess, 0, 0, 0, 0);
  840. setPower (false);
  841. }
  842. tempBuffer.setSize (1, 1);
  843. incomingMidi.clear();
  844. midiEventsToSend.freeEvents();
  845. }
  846. void VSTPluginInstance::processBlock (AudioSampleBuffer& buffer,
  847. MidiBuffer& midiMessages)
  848. {
  849. const int numSamples = buffer.getNumSamples();
  850. if (initialised)
  851. {
  852. AudioPlayHead* playHead = getPlayHead();
  853. if (playHead != nullptr)
  854. {
  855. AudioPlayHead::CurrentPositionInfo position;
  856. playHead->getCurrentPosition (position);
  857. vstHostTime.tempo = position.bpm;
  858. vstHostTime.timeSigNumerator = position.timeSigNumerator;
  859. vstHostTime.timeSigDenominator = position.timeSigDenominator;
  860. vstHostTime.ppqPos = position.ppqPosition;
  861. vstHostTime.barStartPos = position.ppqPositionOfLastBarStart;
  862. vstHostTime.flags |= kVstTempoValid | kVstTimeSigValid | kVstPpqPosValid | kVstBarsValid;
  863. if (position.isPlaying)
  864. vstHostTime.flags |= kVstTransportPlaying;
  865. else
  866. vstHostTime.flags &= ~kVstTransportPlaying;
  867. }
  868. vstHostTime.nanoSeconds = getVSTHostTimeNanoseconds();
  869. if (wantsMidiMessages)
  870. {
  871. midiEventsToSend.clear();
  872. midiEventsToSend.ensureSize (1);
  873. MidiBuffer::Iterator iter (midiMessages);
  874. const uint8* midiData;
  875. int numBytesOfMidiData, samplePosition;
  876. while (iter.getNextEvent (midiData, numBytesOfMidiData, samplePosition))
  877. {
  878. midiEventsToSend.addEvent (midiData, numBytesOfMidiData,
  879. jlimit (0, numSamples - 1, samplePosition));
  880. }
  881. try
  882. {
  883. effect->dispatcher (effect, effProcessEvents, 0, 0, midiEventsToSend.events, 0);
  884. }
  885. catch (...)
  886. {}
  887. }
  888. _clearfp();
  889. if ((effect->flags & effFlagsCanReplacing) != 0)
  890. {
  891. try
  892. {
  893. effect->processReplacing (effect, buffer.getArrayOfChannels(), buffer.getArrayOfChannels(), numSamples);
  894. }
  895. catch (...)
  896. {}
  897. }
  898. else
  899. {
  900. tempBuffer.setSize (effect->numOutputs, numSamples);
  901. tempBuffer.clear();
  902. try
  903. {
  904. effect->process (effect, buffer.getArrayOfChannels(), tempBuffer.getArrayOfChannels(), numSamples);
  905. }
  906. catch (...)
  907. {}
  908. for (int i = effect->numOutputs; --i >= 0;)
  909. buffer.copyFrom (i, 0, tempBuffer.getSampleData (i), numSamples);
  910. }
  911. }
  912. else
  913. {
  914. // Not initialised, so just bypass..
  915. for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
  916. buffer.clear (i, 0, buffer.getNumSamples());
  917. }
  918. {
  919. // copy any incoming midi..
  920. const ScopedLock sl (midiInLock);
  921. midiMessages.swapWith (incomingMidi);
  922. incomingMidi.clear();
  923. }
  924. }
  925. //==============================================================================
  926. void VSTPluginInstance::handleMidiFromPlugin (const VstEvents* const events)
  927. {
  928. if (events != nullptr)
  929. {
  930. const ScopedLock sl (midiInLock);
  931. VSTMidiEventList::addEventsToMidiBuffer (events, incomingMidi);
  932. }
  933. }
  934. //==============================================================================
  935. static Array <VSTPluginWindow*> activeVSTWindows;
  936. //==============================================================================
  937. class VSTPluginWindow : public AudioProcessorEditor,
  938. #if ! JUCE_MAC
  939. public ComponentMovementWatcher,
  940. #endif
  941. public Timer
  942. {
  943. public:
  944. //==============================================================================
  945. VSTPluginWindow (VSTPluginInstance& plugin_)
  946. : AudioProcessorEditor (&plugin_),
  947. #if ! JUCE_MAC
  948. ComponentMovementWatcher (this),
  949. #endif
  950. plugin (plugin_),
  951. isOpen (false),
  952. recursiveResize (false),
  953. pluginWantsKeys (false),
  954. pluginRefusesToResize (false),
  955. alreadyInside (false)
  956. {
  957. #if JUCE_WINDOWS
  958. sizeCheckCount = 0;
  959. pluginHWND = 0;
  960. #elif JUCE_LINUX
  961. pluginWindow = None;
  962. pluginProc = None;
  963. #else
  964. addAndMakeVisible (innerWrapper = new InnerWrapperComponent (this));
  965. #endif
  966. activeVSTWindows.add (this);
  967. setSize (1, 1);
  968. setOpaque (true);
  969. setVisible (true);
  970. }
  971. ~VSTPluginWindow()
  972. {
  973. #if JUCE_MAC
  974. innerWrapper = nullptr;
  975. #else
  976. closePluginWindow();
  977. #endif
  978. activeVSTWindows.removeValue (this);
  979. plugin.editorBeingDeleted (this);
  980. }
  981. //==============================================================================
  982. #if ! JUCE_MAC
  983. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
  984. {
  985. if (recursiveResize)
  986. return;
  987. Component* const topComp = getTopLevelComponent();
  988. if (topComp->getPeer() != nullptr)
  989. {
  990. const Point<int> pos (topComp->getLocalPoint (this, Point<int>()));
  991. recursiveResize = true;
  992. #if JUCE_WINDOWS
  993. if (pluginHWND != 0)
  994. MoveWindow (pluginHWND, pos.getX(), pos.getY(), getWidth(), getHeight(), TRUE);
  995. #elif JUCE_LINUX
  996. if (pluginWindow != 0)
  997. {
  998. XResizeWindow (display, pluginWindow, getWidth(), getHeight());
  999. XMoveWindow (display, pluginWindow, pos.getX(), pos.getY());
  1000. XMapRaised (display, pluginWindow);
  1001. }
  1002. #endif
  1003. recursiveResize = false;
  1004. }
  1005. }
  1006. void componentVisibilityChanged()
  1007. {
  1008. if (isShowing())
  1009. openPluginWindow();
  1010. else
  1011. closePluginWindow();
  1012. componentMovedOrResized (true, true);
  1013. }
  1014. void componentPeerChanged()
  1015. {
  1016. closePluginWindow();
  1017. openPluginWindow();
  1018. }
  1019. #endif
  1020. //==============================================================================
  1021. bool keyStateChanged (bool)
  1022. {
  1023. return pluginWantsKeys;
  1024. }
  1025. bool keyPressed (const KeyPress&)
  1026. {
  1027. return pluginWantsKeys;
  1028. }
  1029. //==============================================================================
  1030. #if JUCE_MAC
  1031. void paint (Graphics& g)
  1032. {
  1033. g.fillAll (Colours::black);
  1034. }
  1035. #else
  1036. void paint (Graphics& g)
  1037. {
  1038. if (isOpen)
  1039. {
  1040. ComponentPeer* const peer = getPeer();
  1041. if (peer != nullptr)
  1042. {
  1043. const Point<int> pos (getScreenPosition() - peer->getScreenPosition());
  1044. peer->addMaskedRegion (pos.getX(), pos.getY(), getWidth(), getHeight());
  1045. #if JUCE_LINUX
  1046. if (pluginWindow != 0)
  1047. {
  1048. const Rectangle<int> clip (g.getClipBounds());
  1049. XEvent ev = { 0 };
  1050. ev.xexpose.type = Expose;
  1051. ev.xexpose.display = display;
  1052. ev.xexpose.window = pluginWindow;
  1053. ev.xexpose.x = clip.getX();
  1054. ev.xexpose.y = clip.getY();
  1055. ev.xexpose.width = clip.getWidth();
  1056. ev.xexpose.height = clip.getHeight();
  1057. sendEventToChild (&ev);
  1058. }
  1059. #endif
  1060. }
  1061. }
  1062. else
  1063. {
  1064. g.fillAll (Colours::black);
  1065. }
  1066. }
  1067. #endif
  1068. //==============================================================================
  1069. void timerCallback()
  1070. {
  1071. #if JUCE_WINDOWS
  1072. if (--sizeCheckCount <= 0)
  1073. {
  1074. sizeCheckCount = 10;
  1075. checkPluginWindowSize();
  1076. }
  1077. #endif
  1078. try
  1079. {
  1080. static bool reentrant = false;
  1081. if (! reentrant)
  1082. {
  1083. reentrant = true;
  1084. plugin.dispatch (effEditIdle, 0, 0, 0, 0);
  1085. reentrant = false;
  1086. }
  1087. }
  1088. catch (...)
  1089. {}
  1090. }
  1091. //==============================================================================
  1092. void mouseDown (const MouseEvent& e)
  1093. {
  1094. #if JUCE_LINUX
  1095. if (pluginWindow == 0)
  1096. return;
  1097. toFront (true);
  1098. XEvent ev = { 0 };
  1099. ev.xbutton.display = display;
  1100. ev.xbutton.type = ButtonPress;
  1101. ev.xbutton.window = pluginWindow;
  1102. ev.xbutton.root = RootWindow (display, DefaultScreen (display));
  1103. ev.xbutton.time = CurrentTime;
  1104. ev.xbutton.x = e.x;
  1105. ev.xbutton.y = e.y;
  1106. ev.xbutton.x_root = e.getScreenX();
  1107. ev.xbutton.y_root = e.getScreenY();
  1108. translateJuceToXButtonModifiers (e, ev);
  1109. sendEventToChild (&ev);
  1110. #elif JUCE_WINDOWS
  1111. (void) e;
  1112. toFront (true);
  1113. #endif
  1114. }
  1115. void broughtToFront()
  1116. {
  1117. activeVSTWindows.removeValue (this);
  1118. activeVSTWindows.add (this);
  1119. #if JUCE_MAC
  1120. dispatch (effEditTop, 0, 0, 0, 0);
  1121. #endif
  1122. }
  1123. //==============================================================================
  1124. private:
  1125. VSTPluginInstance& plugin;
  1126. bool isOpen, recursiveResize;
  1127. bool pluginWantsKeys, pluginRefusesToResize, alreadyInside;
  1128. #if JUCE_WINDOWS
  1129. HWND pluginHWND;
  1130. void* originalWndProc;
  1131. int sizeCheckCount;
  1132. #elif JUCE_LINUX
  1133. Window pluginWindow;
  1134. EventProcPtr pluginProc;
  1135. #endif
  1136. //==============================================================================
  1137. #if JUCE_MAC
  1138. void openPluginWindow (WindowRef parentWindow)
  1139. {
  1140. if (isOpen || parentWindow == 0)
  1141. return;
  1142. isOpen = true;
  1143. ERect* rect = nullptr;
  1144. dispatch (effEditGetRect, 0, 0, &rect, 0);
  1145. dispatch (effEditOpen, 0, 0, parentWindow, 0);
  1146. // do this before and after like in the steinberg example
  1147. dispatch (effEditGetRect, 0, 0, &rect, 0);
  1148. dispatch (effGetProgram, 0, 0, 0, 0); // also in steinberg code
  1149. // Install keyboard hooks
  1150. pluginWantsKeys = (dispatch (effKeysRequired, 0, 0, 0, 0) == 0);
  1151. // double-check it's not too tiny
  1152. int w = 250, h = 150;
  1153. if (rect != nullptr)
  1154. {
  1155. w = rect->right - rect->left;
  1156. h = rect->bottom - rect->top;
  1157. if (w == 0 || h == 0)
  1158. {
  1159. w = 250;
  1160. h = 150;
  1161. }
  1162. }
  1163. w = jmax (w, 32);
  1164. h = jmax (h, 32);
  1165. setSize (w, h);
  1166. startTimer (18 + JUCE_NAMESPACE::Random::getSystemRandom().nextInt (5));
  1167. repaint();
  1168. }
  1169. #else
  1170. void openPluginWindow()
  1171. {
  1172. if (isOpen || getWindowHandle() == 0)
  1173. return;
  1174. log ("Opening VST UI: " + plugin.name);
  1175. isOpen = true;
  1176. ERect* rect = nullptr;
  1177. dispatch (effEditGetRect, 0, 0, &rect, 0);
  1178. dispatch (effEditOpen, 0, 0, getWindowHandle(), 0);
  1179. // do this before and after like in the steinberg example
  1180. dispatch (effEditGetRect, 0, 0, &rect, 0);
  1181. dispatch (effGetProgram, 0, 0, 0, 0); // also in steinberg code
  1182. // Install keyboard hooks
  1183. pluginWantsKeys = (dispatch (effKeysRequired, 0, 0, 0, 0) == 0);
  1184. #if JUCE_WINDOWS
  1185. originalWndProc = 0;
  1186. pluginHWND = GetWindow ((HWND) getWindowHandle(), GW_CHILD);
  1187. if (pluginHWND == 0)
  1188. {
  1189. isOpen = false;
  1190. setSize (300, 150);
  1191. return;
  1192. }
  1193. #pragma warning (push)
  1194. #pragma warning (disable: 4244)
  1195. originalWndProc = (void*) GetWindowLongPtr (pluginHWND, GWLP_WNDPROC);
  1196. if (! pluginWantsKeys)
  1197. SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) vstHookWndProc);
  1198. #pragma warning (pop)
  1199. int w, h;
  1200. RECT r;
  1201. GetWindowRect (pluginHWND, &r);
  1202. w = r.right - r.left;
  1203. h = r.bottom - r.top;
  1204. if (rect != nullptr)
  1205. {
  1206. const int rw = rect->right - rect->left;
  1207. const int rh = rect->bottom - rect->top;
  1208. if ((rw > 50 && rh > 50 && rw < 2000 && rh < 2000 && rw != w && rh != h)
  1209. || ((w == 0 && rw > 0) || (h == 0 && rh > 0)))
  1210. {
  1211. // very dodgy logic to decide which size is right.
  1212. if (abs (rw - w) > 350 || abs (rh - h) > 350)
  1213. {
  1214. SetWindowPos (pluginHWND, 0,
  1215. 0, 0, rw, rh,
  1216. SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
  1217. GetWindowRect (pluginHWND, &r);
  1218. w = r.right - r.left;
  1219. h = r.bottom - r.top;
  1220. pluginRefusesToResize = (w != rw) || (h != rh);
  1221. w = rw;
  1222. h = rh;
  1223. }
  1224. }
  1225. }
  1226. #elif JUCE_LINUX
  1227. pluginWindow = getChildWindow ((Window) getWindowHandle());
  1228. if (pluginWindow != 0)
  1229. pluginProc = (EventProcPtr) getPropertyFromXWindow (pluginWindow,
  1230. XInternAtom (display, "_XEventProc", False));
  1231. int w = 250, h = 150;
  1232. if (rect != nullptr)
  1233. {
  1234. w = rect->right - rect->left;
  1235. h = rect->bottom - rect->top;
  1236. if (w == 0 || h == 0)
  1237. {
  1238. w = 250;
  1239. h = 150;
  1240. }
  1241. }
  1242. if (pluginWindow != 0)
  1243. XMapRaised (display, pluginWindow);
  1244. #endif
  1245. // double-check it's not too tiny
  1246. w = jmax (w, 32);
  1247. h = jmax (h, 32);
  1248. setSize (w, h);
  1249. #if JUCE_WINDOWS
  1250. checkPluginWindowSize();
  1251. #endif
  1252. startTimer (18 + JUCE_NAMESPACE::Random::getSystemRandom().nextInt (5));
  1253. repaint();
  1254. }
  1255. #endif
  1256. //==============================================================================
  1257. #if ! JUCE_MAC
  1258. void closePluginWindow()
  1259. {
  1260. if (isOpen)
  1261. {
  1262. log ("Closing VST UI: " + plugin.getName());
  1263. isOpen = false;
  1264. dispatch (effEditClose, 0, 0, 0, 0);
  1265. #if JUCE_WINDOWS
  1266. #pragma warning (push)
  1267. #pragma warning (disable: 4244)
  1268. if (pluginHWND != 0 && IsWindow (pluginHWND))
  1269. SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) originalWndProc);
  1270. #pragma warning (pop)
  1271. stopTimer();
  1272. if (pluginHWND != 0 && IsWindow (pluginHWND))
  1273. DestroyWindow (pluginHWND);
  1274. pluginHWND = 0;
  1275. #elif JUCE_LINUX
  1276. stopTimer();
  1277. pluginWindow = 0;
  1278. pluginProc = 0;
  1279. #endif
  1280. }
  1281. }
  1282. #endif
  1283. //==============================================================================
  1284. int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt)
  1285. {
  1286. return plugin.dispatch (opcode, index, value, ptr, opt);
  1287. }
  1288. //==============================================================================
  1289. #if JUCE_WINDOWS
  1290. void checkPluginWindowSize()
  1291. {
  1292. RECT r;
  1293. GetWindowRect (pluginHWND, &r);
  1294. const int w = r.right - r.left;
  1295. const int h = r.bottom - r.top;
  1296. if (isShowing() && w > 0 && h > 0
  1297. && (w != getWidth() || h != getHeight())
  1298. && ! pluginRefusesToResize)
  1299. {
  1300. setSize (w, h);
  1301. sizeCheckCount = 0;
  1302. }
  1303. }
  1304. // hooks to get keyboard events from VST windows..
  1305. static LRESULT CALLBACK vstHookWndProc (HWND hW, UINT message, WPARAM wParam, LPARAM lParam)
  1306. {
  1307. for (int i = activeVSTWindows.size(); --i >= 0;)
  1308. {
  1309. const VSTPluginWindow* const w = activeVSTWindows.getUnchecked (i);
  1310. if (w->pluginHWND == hW)
  1311. {
  1312. if (message == WM_CHAR
  1313. || message == WM_KEYDOWN
  1314. || message == WM_SYSKEYDOWN
  1315. || message == WM_KEYUP
  1316. || message == WM_SYSKEYUP
  1317. || message == WM_APPCOMMAND)
  1318. {
  1319. SendMessage ((HWND) w->getTopLevelComponent()->getWindowHandle(),
  1320. message, wParam, lParam);
  1321. }
  1322. return CallWindowProc ((WNDPROC) (w->originalWndProc),
  1323. (HWND) w->pluginHWND,
  1324. message,
  1325. wParam,
  1326. lParam);
  1327. }
  1328. }
  1329. return DefWindowProc (hW, message, wParam, lParam);
  1330. }
  1331. #endif
  1332. #if JUCE_LINUX
  1333. //==============================================================================
  1334. // overload mouse/keyboard events to forward them to the plugin's inner window..
  1335. void sendEventToChild (XEvent* event)
  1336. {
  1337. if (pluginProc != 0)
  1338. {
  1339. // if the plugin publishes an event procedure, pass the event directly..
  1340. pluginProc (event);
  1341. }
  1342. else if (pluginWindow != 0)
  1343. {
  1344. // if the plugin has a window, then send the event to the window so that
  1345. // its message thread will pick it up..
  1346. XSendEvent (display, pluginWindow, False, 0L, event);
  1347. XFlush (display);
  1348. }
  1349. }
  1350. void mouseEnter (const MouseEvent& e)
  1351. {
  1352. if (pluginWindow != 0)
  1353. {
  1354. XEvent ev = { 0 };
  1355. ev.xcrossing.display = display;
  1356. ev.xcrossing.type = EnterNotify;
  1357. ev.xcrossing.window = pluginWindow;
  1358. ev.xcrossing.root = RootWindow (display, DefaultScreen (display));
  1359. ev.xcrossing.time = CurrentTime;
  1360. ev.xcrossing.x = e.x;
  1361. ev.xcrossing.y = e.y;
  1362. ev.xcrossing.x_root = e.getScreenX();
  1363. ev.xcrossing.y_root = e.getScreenY();
  1364. ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab
  1365. ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual
  1366. translateJuceToXCrossingModifiers (e, ev);
  1367. sendEventToChild (&ev);
  1368. }
  1369. }
  1370. void mouseExit (const MouseEvent& e)
  1371. {
  1372. if (pluginWindow != 0)
  1373. {
  1374. XEvent ev = { 0 };
  1375. ev.xcrossing.display = display;
  1376. ev.xcrossing.type = LeaveNotify;
  1377. ev.xcrossing.window = pluginWindow;
  1378. ev.xcrossing.root = RootWindow (display, DefaultScreen (display));
  1379. ev.xcrossing.time = CurrentTime;
  1380. ev.xcrossing.x = e.x;
  1381. ev.xcrossing.y = e.y;
  1382. ev.xcrossing.x_root = e.getScreenX();
  1383. ev.xcrossing.y_root = e.getScreenY();
  1384. ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab
  1385. ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual
  1386. ev.xcrossing.focus = hasKeyboardFocus (true); // TODO - yes ?
  1387. translateJuceToXCrossingModifiers (e, ev);
  1388. sendEventToChild (&ev);
  1389. }
  1390. }
  1391. void mouseMove (const MouseEvent& e)
  1392. {
  1393. if (pluginWindow != 0)
  1394. {
  1395. XEvent ev = { 0 };
  1396. ev.xmotion.display = display;
  1397. ev.xmotion.type = MotionNotify;
  1398. ev.xmotion.window = pluginWindow;
  1399. ev.xmotion.root = RootWindow (display, DefaultScreen (display));
  1400. ev.xmotion.time = CurrentTime;
  1401. ev.xmotion.is_hint = NotifyNormal;
  1402. ev.xmotion.x = e.x;
  1403. ev.xmotion.y = e.y;
  1404. ev.xmotion.x_root = e.getScreenX();
  1405. ev.xmotion.y_root = e.getScreenY();
  1406. sendEventToChild (&ev);
  1407. }
  1408. }
  1409. void mouseDrag (const MouseEvent& e)
  1410. {
  1411. if (pluginWindow != 0)
  1412. {
  1413. XEvent ev = { 0 };
  1414. ev.xmotion.display = display;
  1415. ev.xmotion.type = MotionNotify;
  1416. ev.xmotion.window = pluginWindow;
  1417. ev.xmotion.root = RootWindow (display, DefaultScreen (display));
  1418. ev.xmotion.time = CurrentTime;
  1419. ev.xmotion.x = e.x ;
  1420. ev.xmotion.y = e.y;
  1421. ev.xmotion.x_root = e.getScreenX();
  1422. ev.xmotion.y_root = e.getScreenY();
  1423. ev.xmotion.is_hint = NotifyNormal;
  1424. translateJuceToXMotionModifiers (e, ev);
  1425. sendEventToChild (&ev);
  1426. }
  1427. }
  1428. void mouseUp (const MouseEvent& e)
  1429. {
  1430. if (pluginWindow != 0)
  1431. {
  1432. XEvent ev = { 0 };
  1433. ev.xbutton.display = display;
  1434. ev.xbutton.type = ButtonRelease;
  1435. ev.xbutton.window = pluginWindow;
  1436. ev.xbutton.root = RootWindow (display, DefaultScreen (display));
  1437. ev.xbutton.time = CurrentTime;
  1438. ev.xbutton.x = e.x;
  1439. ev.xbutton.y = e.y;
  1440. ev.xbutton.x_root = e.getScreenX();
  1441. ev.xbutton.y_root = e.getScreenY();
  1442. translateJuceToXButtonModifiers (e, ev);
  1443. sendEventToChild (&ev);
  1444. }
  1445. }
  1446. void mouseWheelMove (const MouseEvent& e,
  1447. float incrementX,
  1448. float incrementY)
  1449. {
  1450. if (pluginWindow != 0)
  1451. {
  1452. XEvent ev = { 0 };
  1453. ev.xbutton.display = display;
  1454. ev.xbutton.type = ButtonPress;
  1455. ev.xbutton.window = pluginWindow;
  1456. ev.xbutton.root = RootWindow (display, DefaultScreen (display));
  1457. ev.xbutton.time = CurrentTime;
  1458. ev.xbutton.x = e.x;
  1459. ev.xbutton.y = e.y;
  1460. ev.xbutton.x_root = e.getScreenX();
  1461. ev.xbutton.y_root = e.getScreenY();
  1462. translateJuceToXMouseWheelModifiers (e, incrementY, ev);
  1463. sendEventToChild (&ev);
  1464. // TODO - put a usleep here ?
  1465. ev.xbutton.type = ButtonRelease;
  1466. sendEventToChild (&ev);
  1467. }
  1468. }
  1469. #endif
  1470. #if JUCE_MAC
  1471. #if ! JUCE_SUPPORT_CARBON
  1472. #error "To build VSTs, you need to enable the JUCE_SUPPORT_CARBON flag in your config!"
  1473. #endif
  1474. class InnerWrapperComponent : public CarbonViewWrapperComponent
  1475. {
  1476. public:
  1477. InnerWrapperComponent (VSTPluginWindow* const owner_)
  1478. : owner (owner_),
  1479. alreadyInside (false)
  1480. {
  1481. }
  1482. ~InnerWrapperComponent()
  1483. {
  1484. deleteWindow();
  1485. }
  1486. HIViewRef attachView (WindowRef windowRef, HIViewRef rootView)
  1487. {
  1488. owner->openPluginWindow (windowRef);
  1489. return 0;
  1490. }
  1491. void removeView (HIViewRef)
  1492. {
  1493. owner->dispatch (effEditClose, 0, 0, 0, 0);
  1494. owner->dispatch (effEditSleep, 0, 0, 0, 0);
  1495. }
  1496. bool getEmbeddedViewSize (int& w, int& h)
  1497. {
  1498. ERect* rect = nullptr;
  1499. owner->dispatch (effEditGetRect, 0, 0, &rect, 0);
  1500. w = rect->right - rect->left;
  1501. h = rect->bottom - rect->top;
  1502. return true;
  1503. }
  1504. void mouseDown (int x, int y)
  1505. {
  1506. if (! alreadyInside)
  1507. {
  1508. alreadyInside = true;
  1509. getTopLevelComponent()->toFront (true);
  1510. owner->dispatch (effEditMouse, x, y, 0, 0);
  1511. alreadyInside = false;
  1512. }
  1513. else
  1514. {
  1515. PostEvent (::mouseDown, 0);
  1516. }
  1517. }
  1518. void paint()
  1519. {
  1520. ComponentPeer* const peer = getPeer();
  1521. if (peer != nullptr)
  1522. {
  1523. const Point<int> pos (getScreenPosition() - peer->getScreenPosition());
  1524. ERect r;
  1525. r.left = pos.getX();
  1526. r.right = r.left + getWidth();
  1527. r.top = pos.getY();
  1528. r.bottom = r.top + getHeight();
  1529. owner->dispatch (effEditDraw, 0, 0, &r, 0);
  1530. }
  1531. }
  1532. private:
  1533. VSTPluginWindow* const owner;
  1534. bool alreadyInside;
  1535. };
  1536. friend class InnerWrapperComponent;
  1537. ScopedPointer <InnerWrapperComponent> innerWrapper;
  1538. void resized()
  1539. {
  1540. innerWrapper->setSize (getWidth(), getHeight());
  1541. }
  1542. #endif
  1543. private:
  1544. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginWindow);
  1545. };
  1546. //==============================================================================
  1547. AudioProcessorEditor* VSTPluginInstance::createEditor()
  1548. {
  1549. if (hasEditor())
  1550. return new VSTPluginWindow (*this);
  1551. return nullptr;
  1552. }
  1553. //==============================================================================
  1554. void VSTPluginInstance::handleAsyncUpdate()
  1555. {
  1556. // indicates that something about the plugin has changed..
  1557. updateHostDisplay();
  1558. }
  1559. //==============================================================================
  1560. bool VSTPluginInstance::restoreProgramSettings (const fxProgram* const prog)
  1561. {
  1562. if (vst_swap (prog->chunkMagic) == 'CcnK' && vst_swap (prog->fxMagic) == 'FxCk')
  1563. {
  1564. changeProgramName (getCurrentProgram(), prog->prgName);
  1565. for (int i = 0; i < vst_swap (prog->numParams); ++i)
  1566. setParameter (i, vst_swapFloat (prog->params[i]));
  1567. return true;
  1568. }
  1569. return false;
  1570. }
  1571. bool VSTPluginInstance::loadFromFXBFile (const void* const data,
  1572. const int dataSize)
  1573. {
  1574. if (dataSize < 28)
  1575. return false;
  1576. const fxSet* const set = (const fxSet*) data;
  1577. if ((vst_swap (set->chunkMagic) != 'CcnK' && vst_swap (set->chunkMagic) != 'KncC')
  1578. || vst_swap (set->version) > fxbVersionNum)
  1579. return false;
  1580. if (vst_swap (set->fxMagic) == 'FxBk')
  1581. {
  1582. // bank of programs
  1583. if (vst_swap (set->numPrograms) >= 0)
  1584. {
  1585. const int oldProg = getCurrentProgram();
  1586. const int numParams = vst_swap (((const fxProgram*) (set->programs))->numParams);
  1587. const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float);
  1588. for (int i = 0; i < vst_swap (set->numPrograms); ++i)
  1589. {
  1590. if (i != oldProg)
  1591. {
  1592. const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + i * progLen);
  1593. if (((const char*) prog) - ((const char*) set) >= dataSize)
  1594. return false;
  1595. if (vst_swap (set->numPrograms) > 0)
  1596. setCurrentProgram (i);
  1597. if (! restoreProgramSettings (prog))
  1598. return false;
  1599. }
  1600. }
  1601. if (vst_swap (set->numPrograms) > 0)
  1602. setCurrentProgram (oldProg);
  1603. const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen);
  1604. if (((const char*) prog) - ((const char*) set) >= dataSize)
  1605. return false;
  1606. if (! restoreProgramSettings (prog))
  1607. return false;
  1608. }
  1609. }
  1610. else if (vst_swap (set->fxMagic) == 'FxCk')
  1611. {
  1612. // single program
  1613. const fxProgram* const prog = (const fxProgram*) data;
  1614. if (vst_swap (prog->chunkMagic) != 'CcnK')
  1615. return false;
  1616. changeProgramName (getCurrentProgram(), prog->prgName);
  1617. for (int i = 0; i < vst_swap (prog->numParams); ++i)
  1618. setParameter (i, vst_swapFloat (prog->params[i]));
  1619. }
  1620. else if (vst_swap (set->fxMagic) == 'FBCh' || vst_swap (set->fxMagic) == 'hCBF')
  1621. {
  1622. // non-preset chunk
  1623. const fxChunkSet* const cset = (const fxChunkSet*) data;
  1624. if (vst_swap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize)
  1625. return false;
  1626. setChunkData (cset->chunk, vst_swap (cset->chunkSize), false);
  1627. }
  1628. else if (vst_swap (set->fxMagic) == 'FPCh' || vst_swap (set->fxMagic) == 'hCPF')
  1629. {
  1630. // preset chunk
  1631. const fxProgramSet* const cset = (const fxProgramSet*) data;
  1632. if (vst_swap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize)
  1633. return false;
  1634. setChunkData (cset->chunk, vst_swap (cset->chunkSize), true);
  1635. changeProgramName (getCurrentProgram(), cset->name);
  1636. }
  1637. else
  1638. {
  1639. return false;
  1640. }
  1641. return true;
  1642. }
  1643. //==============================================================================
  1644. void VSTPluginInstance::setParamsInProgramBlock (fxProgram* const prog)
  1645. {
  1646. const int numParams = getNumParameters();
  1647. prog->chunkMagic = vst_swap ('CcnK');
  1648. prog->byteSize = 0;
  1649. prog->fxMagic = vst_swap ('FxCk');
  1650. prog->version = vst_swap (fxbVersionNum);
  1651. prog->fxID = vst_swap (getUID());
  1652. prog->fxVersion = vst_swap (getVersionNumber());
  1653. prog->numParams = vst_swap (numParams);
  1654. getCurrentProgramName().copyToUTF8 (prog->prgName, sizeof (prog->prgName) - 1);
  1655. for (int i = 0; i < numParams; ++i)
  1656. prog->params[i] = vst_swapFloat (getParameter (i));
  1657. }
  1658. bool VSTPluginInstance::saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB)
  1659. {
  1660. const int numPrograms = getNumPrograms();
  1661. const int numParams = getNumParameters();
  1662. if (usesChunks())
  1663. {
  1664. if (isFXB)
  1665. {
  1666. MemoryBlock chunk;
  1667. getChunkData (chunk, false, maxSizeMB);
  1668. const size_t totalLen = sizeof (fxChunkSet) + chunk.getSize() - 8;
  1669. dest.setSize (totalLen, true);
  1670. fxChunkSet* const set = (fxChunkSet*) dest.getData();
  1671. set->chunkMagic = vst_swap ('CcnK');
  1672. set->byteSize = 0;
  1673. set->fxMagic = vst_swap ('FBCh');
  1674. set->version = vst_swap (fxbVersionNum);
  1675. set->fxID = vst_swap (getUID());
  1676. set->fxVersion = vst_swap (getVersionNumber());
  1677. set->numPrograms = vst_swap (numPrograms);
  1678. set->chunkSize = vst_swap ((long) chunk.getSize());
  1679. chunk.copyTo (set->chunk, 0, chunk.getSize());
  1680. }
  1681. else
  1682. {
  1683. MemoryBlock chunk;
  1684. getChunkData (chunk, true, maxSizeMB);
  1685. const size_t totalLen = sizeof (fxProgramSet) + chunk.getSize() - 8;
  1686. dest.setSize (totalLen, true);
  1687. fxProgramSet* const set = (fxProgramSet*) dest.getData();
  1688. set->chunkMagic = vst_swap ('CcnK');
  1689. set->byteSize = 0;
  1690. set->fxMagic = vst_swap ('FPCh');
  1691. set->version = vst_swap (fxbVersionNum);
  1692. set->fxID = vst_swap (getUID());
  1693. set->fxVersion = vst_swap (getVersionNumber());
  1694. set->numPrograms = vst_swap (numPrograms);
  1695. set->chunkSize = vst_swap ((long) chunk.getSize());
  1696. getCurrentProgramName().copyToUTF8 (set->name, sizeof (set->name) - 1);
  1697. chunk.copyTo (set->chunk, 0, chunk.getSize());
  1698. }
  1699. }
  1700. else
  1701. {
  1702. if (isFXB)
  1703. {
  1704. const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float);
  1705. const int len = (sizeof (fxSet) - sizeof (fxProgram)) + progLen * jmax (1, numPrograms);
  1706. dest.setSize (len, true);
  1707. fxSet* const set = (fxSet*) dest.getData();
  1708. set->chunkMagic = vst_swap ('CcnK');
  1709. set->byteSize = 0;
  1710. set->fxMagic = vst_swap ('FxBk');
  1711. set->version = vst_swap (fxbVersionNum);
  1712. set->fxID = vst_swap (getUID());
  1713. set->fxVersion = vst_swap (getVersionNumber());
  1714. set->numPrograms = vst_swap (numPrograms);
  1715. const int oldProgram = getCurrentProgram();
  1716. MemoryBlock oldSettings;
  1717. createTempParameterStore (oldSettings);
  1718. setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + oldProgram * progLen));
  1719. for (int i = 0; i < numPrograms; ++i)
  1720. {
  1721. if (i != oldProgram)
  1722. {
  1723. setCurrentProgram (i);
  1724. setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + i * progLen));
  1725. }
  1726. }
  1727. setCurrentProgram (oldProgram);
  1728. restoreFromTempParameterStore (oldSettings);
  1729. }
  1730. else
  1731. {
  1732. const int totalLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float);
  1733. dest.setSize (totalLen, true);
  1734. setParamsInProgramBlock ((fxProgram*) dest.getData());
  1735. }
  1736. }
  1737. return true;
  1738. }
  1739. void VSTPluginInstance::getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const
  1740. {
  1741. if (usesChunks())
  1742. {
  1743. void* data = nullptr;
  1744. const int bytes = dispatch (effGetChunk, isPreset ? 1 : 0, 0, &data, 0.0f);
  1745. if (data != nullptr && bytes <= maxSizeMB * 1024 * 1024)
  1746. {
  1747. mb.setSize (bytes);
  1748. mb.copyFrom (data, 0, bytes);
  1749. }
  1750. }
  1751. }
  1752. void VSTPluginInstance::setChunkData (const char* data, int size, bool isPreset)
  1753. {
  1754. if (size > 0 && usesChunks())
  1755. {
  1756. dispatch (effSetChunk, isPreset ? 1 : 0, size, (void*) data, 0.0f);
  1757. if (! isPreset)
  1758. updateStoredProgramNames();
  1759. }
  1760. }
  1761. //==============================================================================
  1762. void VSTPluginInstance::timerCallback()
  1763. {
  1764. if (dispatch (effIdle, 0, 0, 0, 0) == 0)
  1765. stopTimer();
  1766. }
  1767. int VSTPluginInstance::dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) const
  1768. {
  1769. int result = 0;
  1770. if (effect != nullptr)
  1771. {
  1772. const ScopedLock sl (lock);
  1773. const IdleCallRecursionPreventer icrp;
  1774. try
  1775. {
  1776. #if JUCE_MAC
  1777. if (module->resFileId != 0)
  1778. UseResFile (module->resFileId);
  1779. #endif
  1780. result = effect->dispatcher (effect, opcode, index, value, ptr, opt);
  1781. #if JUCE_MAC
  1782. module->resFileId = CurResFile();
  1783. #endif
  1784. }
  1785. catch (...)
  1786. {}
  1787. }
  1788. return result;
  1789. }
  1790. //==============================================================================
  1791. namespace
  1792. {
  1793. static const int defaultVSTSampleRateValue = 16384;
  1794. static const int defaultVSTBlockSizeValue = 512;
  1795. // handles non plugin-specific callbacks..
  1796. VstIntPtr handleGeneralCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt)
  1797. {
  1798. (void) index;
  1799. (void) value;
  1800. (void) opt;
  1801. switch (opcode)
  1802. {
  1803. case audioMasterCanDo:
  1804. {
  1805. static const char* canDos[] = { "supplyIdle",
  1806. "sendVstEvents",
  1807. "sendVstMidiEvent",
  1808. "sendVstTimeInfo",
  1809. "receiveVstEvents",
  1810. "receiveVstMidiEvent",
  1811. "supportShell",
  1812. "shellCategory" };
  1813. for (int i = 0; i < numElementsInArray (canDos); ++i)
  1814. if (strcmp (canDos[i], (const char*) ptr) == 0)
  1815. return 1;
  1816. return 0;
  1817. }
  1818. case audioMasterVersion: return 0x2400;
  1819. case audioMasterCurrentId: return shellUIDToCreate;
  1820. case audioMasterGetNumAutomatableParameters: return 0;
  1821. case audioMasterGetAutomationState: return 1;
  1822. case audioMasterGetVendorVersion: return 0x0101;
  1823. case audioMasterGetVendorString:
  1824. case audioMasterGetProductString:
  1825. {
  1826. String hostName ("Juce VST Host");
  1827. if (JUCEApplication::getInstance() != nullptr)
  1828. hostName = JUCEApplication::getInstance()->getApplicationName();
  1829. hostName.copyToUTF8 ((char*) ptr, jmin (kVstMaxVendorStrLen, kVstMaxProductStrLen) - 1);
  1830. break;
  1831. }
  1832. case audioMasterGetSampleRate: return (VstIntPtr) defaultVSTSampleRateValue;
  1833. case audioMasterGetBlockSize: return (VstIntPtr) defaultVSTBlockSizeValue;
  1834. case audioMasterSetOutputSampleRate: return 0;
  1835. default:
  1836. DBG ("*** Unhandled VST Callback: " + String ((int) opcode));
  1837. break;
  1838. }
  1839. return 0;
  1840. }
  1841. }
  1842. // handles callbacks for a specific plugin
  1843. VstIntPtr VSTPluginInstance::handleCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt)
  1844. {
  1845. switch (opcode)
  1846. {
  1847. case audioMasterAutomate:
  1848. sendParamChangeMessageToListeners (index, opt);
  1849. break;
  1850. case audioMasterProcessEvents:
  1851. handleMidiFromPlugin ((const VstEvents*) ptr);
  1852. break;
  1853. case audioMasterGetTime:
  1854. #if JUCE_MSVC
  1855. #pragma warning (push)
  1856. #pragma warning (disable: 4311)
  1857. #endif
  1858. return (VstIntPtr) &vstHostTime;
  1859. #if JUCE_MSVC
  1860. #pragma warning (pop)
  1861. #endif
  1862. break;
  1863. case audioMasterIdle:
  1864. if (insideVSTCallback == 0 && MessageManager::getInstance()->isThisTheMessageThread())
  1865. {
  1866. const IdleCallRecursionPreventer icrp;
  1867. #if JUCE_MAC
  1868. if (getActiveEditor() != nullptr)
  1869. dispatch (effEditIdle, 0, 0, 0, 0);
  1870. #endif
  1871. juce_callAnyTimersSynchronously();
  1872. handleUpdateNowIfNeeded();
  1873. for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
  1874. ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow();
  1875. }
  1876. break;
  1877. case audioMasterUpdateDisplay:
  1878. triggerAsyncUpdate();
  1879. break;
  1880. case audioMasterTempoAt:
  1881. // returns (10000 * bpm)
  1882. break;
  1883. case audioMasterNeedIdle:
  1884. startTimer (50);
  1885. break;
  1886. case audioMasterSizeWindow:
  1887. if (getActiveEditor() != nullptr)
  1888. getActiveEditor()->setSize (index, value);
  1889. return 1;
  1890. case audioMasterGetSampleRate:
  1891. return (VstIntPtr) (getSampleRate() > 0 ? getSampleRate() : defaultVSTSampleRateValue);
  1892. case audioMasterGetBlockSize:
  1893. return (VstIntPtr) (getBlockSize() > 0 ? getBlockSize() : defaultVSTBlockSizeValue);
  1894. case audioMasterWantMidi:
  1895. wantsMidiMessages = true;
  1896. break;
  1897. case audioMasterGetDirectory:
  1898. #if JUCE_MAC
  1899. return (VstIntPtr) (void*) &module->parentDirFSSpec;
  1900. #else
  1901. return (VstIntPtr) (pointer_sized_uint) module->fullParentDirectoryPathName.toUTF8().getAddress();
  1902. #endif
  1903. case audioMasterGetAutomationState:
  1904. // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write
  1905. break;
  1906. // none of these are handled (yet)..
  1907. case audioMasterBeginEdit:
  1908. case audioMasterEndEdit:
  1909. case audioMasterSetTime:
  1910. case audioMasterPinConnected:
  1911. case audioMasterGetParameterQuantization:
  1912. case audioMasterIOChanged:
  1913. case audioMasterGetInputLatency:
  1914. case audioMasterGetOutputLatency:
  1915. case audioMasterGetPreviousPlug:
  1916. case audioMasterGetNextPlug:
  1917. case audioMasterWillReplaceOrAccumulate:
  1918. case audioMasterGetCurrentProcessLevel:
  1919. case audioMasterOfflineStart:
  1920. case audioMasterOfflineRead:
  1921. case audioMasterOfflineWrite:
  1922. case audioMasterOfflineGetCurrentPass:
  1923. case audioMasterOfflineGetCurrentMetaPass:
  1924. case audioMasterVendorSpecific:
  1925. case audioMasterSetIcon:
  1926. case audioMasterGetLanguage:
  1927. case audioMasterOpenWindow:
  1928. case audioMasterCloseWindow:
  1929. break;
  1930. default:
  1931. return handleGeneralCallback (opcode, index, value, ptr, opt);
  1932. }
  1933. return 0;
  1934. }
  1935. // entry point for all callbacks from the plugin
  1936. static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
  1937. {
  1938. try
  1939. {
  1940. if (effect != nullptr && effect->resvd2 != 0)
  1941. {
  1942. return ((VSTPluginInstance*)(effect->resvd2))
  1943. ->handleCallback (opcode, index, value, ptr, opt);
  1944. }
  1945. return handleGeneralCallback (opcode, index, value, ptr, opt);
  1946. }
  1947. catch (...)
  1948. {
  1949. return 0;
  1950. }
  1951. }
  1952. //==============================================================================
  1953. const String VSTPluginInstance::getVersion() const
  1954. {
  1955. unsigned int v = dispatch (effGetVendorVersion, 0, 0, 0, 0);
  1956. String s;
  1957. if (v == 0 || v == -1)
  1958. v = getVersionNumber();
  1959. if (v != 0)
  1960. {
  1961. int versionBits[4];
  1962. int n = 0;
  1963. while (v != 0)
  1964. {
  1965. versionBits [n++] = (v & 0xff);
  1966. v >>= 8;
  1967. }
  1968. s << 'V';
  1969. while (n > 0)
  1970. {
  1971. s << versionBits [--n];
  1972. if (n > 0)
  1973. s << '.';
  1974. }
  1975. }
  1976. return s;
  1977. }
  1978. int VSTPluginInstance::getUID() const
  1979. {
  1980. int uid = effect != nullptr ? effect->uniqueID : 0;
  1981. if (uid == 0)
  1982. uid = module->file.hashCode();
  1983. return uid;
  1984. }
  1985. const String VSTPluginInstance::getCategory() const
  1986. {
  1987. const char* result = nullptr;
  1988. switch (dispatch (effGetPlugCategory, 0, 0, 0, 0))
  1989. {
  1990. case kPlugCategEffect: result = "Effect"; break;
  1991. case kPlugCategSynth: result = "Synth"; break;
  1992. case kPlugCategAnalysis: result = "Anaylsis"; break;
  1993. case kPlugCategMastering: result = "Mastering"; break;
  1994. case kPlugCategSpacializer: result = "Spacial"; break;
  1995. case kPlugCategRoomFx: result = "Reverb"; break;
  1996. case kPlugSurroundFx: result = "Surround"; break;
  1997. case kPlugCategRestoration: result = "Restoration"; break;
  1998. case kPlugCategGenerator: result = "Tone generation"; break;
  1999. default: break;
  2000. }
  2001. return result;
  2002. }
  2003. //==============================================================================
  2004. float VSTPluginInstance::getParameter (int index)
  2005. {
  2006. if (effect != nullptr && isPositiveAndBelow (index, (int) effect->numParams))
  2007. {
  2008. try
  2009. {
  2010. const ScopedLock sl (lock);
  2011. return effect->getParameter (effect, index);
  2012. }
  2013. catch (...)
  2014. {
  2015. }
  2016. }
  2017. return 0.0f;
  2018. }
  2019. void VSTPluginInstance::setParameter (int index, float newValue)
  2020. {
  2021. if (effect != nullptr && isPositiveAndBelow (index, (int) effect->numParams))
  2022. {
  2023. try
  2024. {
  2025. const ScopedLock sl (lock);
  2026. if (effect->getParameter (effect, index) != newValue)
  2027. effect->setParameter (effect, index, newValue);
  2028. }
  2029. catch (...)
  2030. {
  2031. }
  2032. }
  2033. }
  2034. const String VSTPluginInstance::getParameterName (int index)
  2035. {
  2036. if (effect != nullptr)
  2037. {
  2038. jassert (index >= 0 && index < effect->numParams);
  2039. char nm [256] = { 0 };
  2040. dispatch (effGetParamName, index, 0, nm, 0);
  2041. return String (nm).trim();
  2042. }
  2043. return String::empty;
  2044. }
  2045. const String VSTPluginInstance::getParameterLabel (int index) const
  2046. {
  2047. if (effect != nullptr)
  2048. {
  2049. jassert (index >= 0 && index < effect->numParams);
  2050. char nm [256] = { 0 };
  2051. dispatch (effGetParamLabel, index, 0, nm, 0);
  2052. return String (nm).trim();
  2053. }
  2054. return String::empty;
  2055. }
  2056. const String VSTPluginInstance::getParameterText (int index)
  2057. {
  2058. if (effect != nullptr)
  2059. {
  2060. jassert (index >= 0 && index < effect->numParams);
  2061. char nm [256] = { 0 };
  2062. dispatch (effGetParamDisplay, index, 0, nm, 0);
  2063. return String (nm).trim();
  2064. }
  2065. return String::empty;
  2066. }
  2067. bool VSTPluginInstance::isParameterAutomatable (int index) const
  2068. {
  2069. if (effect != nullptr)
  2070. {
  2071. jassert (index >= 0 && index < effect->numParams);
  2072. return dispatch (effCanBeAutomated, index, 0, 0, 0) != 0;
  2073. }
  2074. return false;
  2075. }
  2076. void VSTPluginInstance::createTempParameterStore (MemoryBlock& dest)
  2077. {
  2078. dest.setSize (64 + 4 * getNumParameters());
  2079. dest.fillWith (0);
  2080. getCurrentProgramName().copyToUTF8 ((char*) dest.getData(), 63);
  2081. float* const p = (float*) (((char*) dest.getData()) + 64);
  2082. for (int i = 0; i < getNumParameters(); ++i)
  2083. p[i] = getParameter(i);
  2084. }
  2085. void VSTPluginInstance::restoreFromTempParameterStore (const MemoryBlock& m)
  2086. {
  2087. changeProgramName (getCurrentProgram(), (const char*) m.getData());
  2088. float* p = (float*) (((char*) m.getData()) + 64);
  2089. for (int i = 0; i < getNumParameters(); ++i)
  2090. setParameter (i, p[i]);
  2091. }
  2092. //==============================================================================
  2093. void VSTPluginInstance::setCurrentProgram (int newIndex)
  2094. {
  2095. if (getNumPrograms() > 0 && newIndex != getCurrentProgram())
  2096. dispatch (effSetProgram, 0, jlimit (0, getNumPrograms() - 1, newIndex), 0, 0);
  2097. }
  2098. const String VSTPluginInstance::getProgramName (int index)
  2099. {
  2100. if (index == getCurrentProgram())
  2101. {
  2102. return getCurrentProgramName();
  2103. }
  2104. else if (effect != nullptr)
  2105. {
  2106. char nm [256] = { 0 };
  2107. if (dispatch (effGetProgramNameIndexed,
  2108. jlimit (0, getNumPrograms(), index),
  2109. -1, nm, 0) != 0)
  2110. {
  2111. return String (nm).trim();
  2112. }
  2113. }
  2114. return programNames [index];
  2115. }
  2116. void VSTPluginInstance::changeProgramName (int index, const String& newName)
  2117. {
  2118. if (index == getCurrentProgram())
  2119. {
  2120. if (getNumPrograms() > 0 && newName != getCurrentProgramName())
  2121. dispatch (effSetProgramName, 0, 0, (void*) newName.substring (0, 24).toUTF8().getAddress(), 0.0f);
  2122. }
  2123. else
  2124. {
  2125. jassertfalse; // xxx not implemented!
  2126. }
  2127. }
  2128. void VSTPluginInstance::updateStoredProgramNames()
  2129. {
  2130. if (effect != nullptr && getNumPrograms() > 0)
  2131. {
  2132. char nm [256] = { 0 };
  2133. // only do this if the plugin can't use indexed names..
  2134. if (dispatch (effGetProgramNameIndexed, 0, -1, nm, 0) == 0)
  2135. {
  2136. const int oldProgram = getCurrentProgram();
  2137. MemoryBlock oldSettings;
  2138. createTempParameterStore (oldSettings);
  2139. for (int i = 0; i < getNumPrograms(); ++i)
  2140. {
  2141. setCurrentProgram (i);
  2142. getCurrentProgramName(); // (this updates the list)
  2143. }
  2144. setCurrentProgram (oldProgram);
  2145. restoreFromTempParameterStore (oldSettings);
  2146. }
  2147. }
  2148. }
  2149. const String VSTPluginInstance::getCurrentProgramName()
  2150. {
  2151. if (effect != nullptr)
  2152. {
  2153. char nm [256] = { 0 };
  2154. dispatch (effGetProgramName, 0, 0, nm, 0);
  2155. const int index = getCurrentProgram();
  2156. if (programNames[index].isEmpty())
  2157. {
  2158. while (programNames.size() < index)
  2159. programNames.add (String::empty);
  2160. programNames.set (index, String (nm).trim());
  2161. }
  2162. return String (nm).trim();
  2163. }
  2164. return String::empty;
  2165. }
  2166. //==============================================================================
  2167. const String VSTPluginInstance::getInputChannelName (int index) const
  2168. {
  2169. if (index >= 0 && index < getNumInputChannels())
  2170. {
  2171. VstPinProperties pinProps;
  2172. if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0)
  2173. return String (pinProps.label, sizeof (pinProps.label));
  2174. }
  2175. return String::empty;
  2176. }
  2177. bool VSTPluginInstance::isInputChannelStereoPair (int index) const
  2178. {
  2179. if (index < 0 || index >= getNumInputChannels())
  2180. return false;
  2181. VstPinProperties pinProps;
  2182. if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0)
  2183. return (pinProps.flags & kVstPinIsStereo) != 0;
  2184. return true;
  2185. }
  2186. const String VSTPluginInstance::getOutputChannelName (int index) const
  2187. {
  2188. if (index >= 0 && index < getNumOutputChannels())
  2189. {
  2190. VstPinProperties pinProps;
  2191. if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0)
  2192. return String (pinProps.label, sizeof (pinProps.label));
  2193. }
  2194. return String::empty;
  2195. }
  2196. bool VSTPluginInstance::isOutputChannelStereoPair (int index) const
  2197. {
  2198. if (index < 0 || index >= getNumOutputChannels())
  2199. return false;
  2200. VstPinProperties pinProps;
  2201. if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0)
  2202. return (pinProps.flags & kVstPinIsStereo) != 0;
  2203. return true;
  2204. }
  2205. //==============================================================================
  2206. void VSTPluginInstance::setPower (const bool on)
  2207. {
  2208. dispatch (effMainsChanged, 0, on ? 1 : 0, 0, 0);
  2209. isPowerOn = on;
  2210. }
  2211. //==============================================================================
  2212. const int defaultMaxSizeMB = 64;
  2213. void VSTPluginInstance::getStateInformation (MemoryBlock& destData)
  2214. {
  2215. saveToFXBFile (destData, true, defaultMaxSizeMB);
  2216. }
  2217. void VSTPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData)
  2218. {
  2219. saveToFXBFile (destData, false, defaultMaxSizeMB);
  2220. }
  2221. void VSTPluginInstance::setStateInformation (const void* data, int sizeInBytes)
  2222. {
  2223. loadFromFXBFile (data, sizeInBytes);
  2224. }
  2225. void VSTPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes)
  2226. {
  2227. loadFromFXBFile (data, sizeInBytes);
  2228. }
  2229. //==============================================================================
  2230. //==============================================================================
  2231. VSTPluginFormat::VSTPluginFormat()
  2232. {
  2233. }
  2234. VSTPluginFormat::~VSTPluginFormat()
  2235. {
  2236. }
  2237. void VSTPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& results,
  2238. const String& fileOrIdentifier)
  2239. {
  2240. if (! fileMightContainThisPluginType (fileOrIdentifier))
  2241. return;
  2242. PluginDescription desc;
  2243. desc.fileOrIdentifier = fileOrIdentifier;
  2244. desc.uid = 0;
  2245. ScopedPointer <VSTPluginInstance> instance (dynamic_cast <VSTPluginInstance*> (createInstanceFromDescription (desc)));
  2246. if (instance == 0)
  2247. return;
  2248. try
  2249. {
  2250. #if JUCE_MAC
  2251. if (instance->module->resFileId != 0)
  2252. UseResFile (instance->module->resFileId);
  2253. #endif
  2254. instance->fillInPluginDescription (desc);
  2255. VstPlugCategory category = (VstPlugCategory) instance->dispatch (effGetPlugCategory, 0, 0, 0, 0);
  2256. if (category != kPlugCategShell)
  2257. {
  2258. // Normal plugin...
  2259. results.add (new PluginDescription (desc));
  2260. instance->dispatch (effOpen, 0, 0, 0, 0);
  2261. }
  2262. else
  2263. {
  2264. // It's a shell plugin, so iterate all the subtypes...
  2265. for (;;)
  2266. {
  2267. char shellEffectName [64] = { 0 };
  2268. const int uid = instance->dispatch (effShellGetNextPlugin, 0, 0, shellEffectName, 0);
  2269. if (uid == 0)
  2270. {
  2271. break;
  2272. }
  2273. else
  2274. {
  2275. desc.uid = uid;
  2276. desc.name = shellEffectName;
  2277. desc.descriptiveName = shellEffectName;
  2278. bool alreadyThere = false;
  2279. for (int i = results.size(); --i >= 0;)
  2280. {
  2281. PluginDescription* const d = results.getUnchecked(i);
  2282. if (d->isDuplicateOf (desc))
  2283. {
  2284. alreadyThere = true;
  2285. break;
  2286. }
  2287. }
  2288. if (! alreadyThere)
  2289. results.add (new PluginDescription (desc));
  2290. }
  2291. }
  2292. }
  2293. }
  2294. catch (...)
  2295. {
  2296. // crashed while loading...
  2297. }
  2298. }
  2299. AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const PluginDescription& desc)
  2300. {
  2301. ScopedPointer <VSTPluginInstance> result;
  2302. if (fileMightContainThisPluginType (desc.fileOrIdentifier))
  2303. {
  2304. File file (desc.fileOrIdentifier);
  2305. const File previousWorkingDirectory (File::getCurrentWorkingDirectory());
  2306. file.getParentDirectory().setAsCurrentWorkingDirectory();
  2307. const ReferenceCountedObjectPtr <ModuleHandle> module (ModuleHandle::findOrCreateModule (file));
  2308. if (module != nullptr)
  2309. {
  2310. shellUIDToCreate = desc.uid;
  2311. result = new VSTPluginInstance (module);
  2312. if (result->effect != nullptr)
  2313. {
  2314. result->effect->resvd2 = (VstIntPtr) (pointer_sized_int) (VSTPluginInstance*) result;
  2315. result->initialise();
  2316. }
  2317. else
  2318. {
  2319. result = nullptr;
  2320. }
  2321. }
  2322. previousWorkingDirectory.setAsCurrentWorkingDirectory();
  2323. }
  2324. return result.release();
  2325. }
  2326. bool VSTPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier)
  2327. {
  2328. const File f (fileOrIdentifier);
  2329. #if JUCE_MAC
  2330. if (f.isDirectory() && f.hasFileExtension (".vst"))
  2331. return true;
  2332. #if JUCE_PPC
  2333. FSRef fileRef;
  2334. if (makeFSRefFromPath (&fileRef, f.getFullPathName()))
  2335. {
  2336. const short resFileId = FSOpenResFile (&fileRef, fsRdPerm);
  2337. if (resFileId != -1)
  2338. {
  2339. const int numEffects = Count1Resources ('aEff');
  2340. CloseResFile (resFileId);
  2341. if (numEffects > 0)
  2342. return true;
  2343. }
  2344. }
  2345. #endif
  2346. return false;
  2347. #elif JUCE_WINDOWS
  2348. return f.existsAsFile() && f.hasFileExtension (".dll");
  2349. #elif JUCE_LINUX
  2350. return f.existsAsFile() && f.hasFileExtension (".so");
  2351. #endif
  2352. }
  2353. String VSTPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier)
  2354. {
  2355. return fileOrIdentifier;
  2356. }
  2357. bool VSTPluginFormat::doesPluginStillExist (const PluginDescription& desc)
  2358. {
  2359. return File (desc.fileOrIdentifier).exists();
  2360. }
  2361. StringArray VSTPluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive)
  2362. {
  2363. StringArray results;
  2364. for (int j = 0; j < directoriesToSearch.getNumPaths(); ++j)
  2365. recursiveFileSearch (results, directoriesToSearch [j], recursive);
  2366. return results;
  2367. }
  2368. void VSTPluginFormat::recursiveFileSearch (StringArray& results, const File& dir, const bool recursive)
  2369. {
  2370. // avoid allowing the dir iterator to be recursive, because we want to avoid letting it delve inside
  2371. // .component or .vst directories.
  2372. DirectoryIterator iter (dir, false, "*", File::findFilesAndDirectories);
  2373. while (iter.next())
  2374. {
  2375. const File f (iter.getFile());
  2376. bool isPlugin = false;
  2377. if (fileMightContainThisPluginType (f.getFullPathName()))
  2378. {
  2379. isPlugin = true;
  2380. results.add (f.getFullPathName());
  2381. }
  2382. if (recursive && (! isPlugin) && f.isDirectory())
  2383. recursiveFileSearch (results, f, true);
  2384. }
  2385. }
  2386. FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch()
  2387. {
  2388. #if JUCE_MAC
  2389. return FileSearchPath ("~/Library/Audio/Plug-Ins/VST;/Library/Audio/Plug-Ins/VST");
  2390. #elif JUCE_WINDOWS
  2391. const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName());
  2392. return FileSearchPath (programFiles + "\\Steinberg\\VstPlugins");
  2393. #elif JUCE_LINUX
  2394. return FileSearchPath ("/usr/lib/vst");
  2395. #endif
  2396. }
  2397. END_JUCE_NAMESPACE
  2398. #endif
  2399. #undef log
  2400. #endif