Add TAL-Reverb-II plugin to test
[juce-lv2.git] / juce / source / src / audio / plugin_host / formats / juce_VSTPluginFormat.cpp
blob9a3d026aa0ae65d5f74a18982db3c4e040151d0b
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../../core/juce_TargetPlatform.h"
27 #include "../../../../juce_Config.h"
29 #if JUCE_PLUGINHOST_VST && (JUCE_MAC_VST_INCLUDED || ! JUCE_MAC)
31 #if JUCE_WINDOWS
32 #undef _WIN32_WINNT
33 #define _WIN32_WINNT 0x500
34 #undef STRICT
35 #define STRICT
36 #include <windows.h>
37 #include <float.h>
38 #pragma warning (disable : 4312 4355)
39 #ifdef __INTEL_COMPILER
40 #pragma warning (disable : 1899)
41 #endif
42 #elif JUCE_LINUX
43 #include <float.h>
44 #include <sys/time.h>
45 #include <X11/Xlib.h>
46 #include <X11/Xutil.h>
47 #include <X11/Xatom.h>
48 #undef Font
49 #undef KeyPress
50 #undef Drawable
51 #undef Time
52 #else
53 #include <Cocoa/Cocoa.h>
54 #include <Carbon/Carbon.h>
55 #endif
57 //==============================================================================
58 #include "../../../core/juce_StandardHeader.h"
60 #if ! (JUCE_MAC && JUCE_64BIT)
62 BEGIN_JUCE_NAMESPACE
64 #include "juce_VSTPluginFormat.h"
65 #include "../../../threads/juce_Process.h"
66 #include "../../../threads/juce_CriticalSection.h"
67 #include "../../../maths/juce_Random.h"
68 #include "../../../io/files/juce_DirectoryIterator.h"
69 #include "../../../events/juce_Timer.h"
70 #include "../../../events/juce_AsyncUpdater.h"
71 #include "../../../events/juce_MessageManager.h"
72 #include "../../../gui/components/layout/juce_ComponentMovementWatcher.h"
73 #include "../../../gui/components/windows/juce_ComponentPeer.h"
74 #include "../../../application/juce_Application.h"
75 #include "../../../core/juce_PlatformUtilities.h"
77 #if JUCE_MAC && JUCE_SUPPORT_CARBON
78 #include "../../../native/mac/juce_mac_CarbonViewWrapperComponent.h"
79 #endif
81 //==============================================================================
82 #undef PRAGMA_ALIGN_SUPPORTED
83 #define VST_FORCE_DEPRECATED 0
85 #if JUCE_MSVC
86 #pragma warning (push)
87 #pragma warning (disable: 4996)
88 #endif
90 /* Obviously you're going to need the Steinberg vstsdk2.4 folder in
91 your include path if you want to add VST support.
93 If you're not interested in VSTs, you can disable them by changing the
94 JUCE_PLUGINHOST_VST flag in juce_Config.h
96 #include <pluginterfaces/vst2.x/aeffectx.h>
98 #if JUCE_MSVC
99 #pragma warning (pop)
100 #endif
102 //==============================================================================
103 #if JUCE_LINUX
104 #define Font JUCE_NAMESPACE::Font
105 #define KeyPress JUCE_NAMESPACE::KeyPress
106 #define Drawable JUCE_NAMESPACE::Drawable
107 #define Time JUCE_NAMESPACE::Time
108 #endif
110 #include "../juce_PluginDescription.h"
111 #include "juce_VSTMidiEventList.h"
113 #if ! JUCE_WINDOWS
114 static void _fpreset() {}
115 static void _clearfp() {}
116 #endif
118 extern void juce_callAnyTimersSynchronously();
121 //==============================================================================
122 const int fxbVersionNum = 1;
124 struct fxProgram
126 long chunkMagic; // 'CcnK'
127 long byteSize; // of this chunk, excl. magic + byteSize
128 long fxMagic; // 'FxCk'
129 long version;
130 long fxID; // fx unique id
131 long fxVersion;
132 long numParams;
133 char prgName[28];
134 float params[1]; // variable no. of parameters
137 struct fxSet
139 long chunkMagic; // 'CcnK'
140 long byteSize; // of this chunk, excl. magic + byteSize
141 long fxMagic; // 'FxBk'
142 long version;
143 long fxID; // fx unique id
144 long fxVersion;
145 long numPrograms;
146 char future[128];
147 fxProgram programs[1]; // variable no. of programs
150 struct fxChunkSet
152 long chunkMagic; // 'CcnK'
153 long byteSize; // of this chunk, excl. magic + byteSize
154 long fxMagic; // 'FxCh', 'FPCh', or 'FBCh'
155 long version;
156 long fxID; // fx unique id
157 long fxVersion;
158 long numPrograms;
159 char future[128];
160 long chunkSize;
161 char chunk[8]; // variable
164 struct fxProgramSet
166 long chunkMagic; // 'CcnK'
167 long byteSize; // of this chunk, excl. magic + byteSize
168 long fxMagic; // 'FxCh', 'FPCh', or 'FBCh'
169 long version;
170 long fxID; // fx unique id
171 long fxVersion;
172 long numPrograms;
173 char name[28];
174 long chunkSize;
175 char chunk[8]; // variable
178 namespace
180 long vst_swap (const long x) noexcept
182 #ifdef JUCE_LITTLE_ENDIAN
183 return (long) ByteOrder::swap ((uint32) x);
184 #else
185 return x;
186 #endif
189 float vst_swapFloat (const float x) noexcept
191 #ifdef JUCE_LITTLE_ENDIAN
192 union { uint32 asInt; float asFloat; } n;
193 n.asFloat = x;
194 n.asInt = ByteOrder::swap (n.asInt);
195 return n.asFloat;
196 #else
197 return x;
198 #endif
201 double getVSTHostTimeNanoseconds()
203 #if JUCE_WINDOWS
204 return timeGetTime() * 1000000.0;
205 #elif JUCE_LINUX
206 timeval micro;
207 gettimeofday (&micro, 0);
208 return micro.tv_usec * 1000.0;
209 #elif JUCE_MAC
210 UnsignedWide micro;
211 Microseconds (&micro);
212 return micro.lo * 1000.0;
213 #endif
217 //==============================================================================
218 typedef AEffect* (VSTCALLBACK *MainCall) (audioMasterCallback);
220 static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt);
222 static int shellUIDToCreate = 0;
223 static int insideVSTCallback = 0;
225 class IdleCallRecursionPreventer
227 public:
228 IdleCallRecursionPreventer()
229 : isMessageThread (MessageManager::getInstance()->isThisTheMessageThread())
231 if (isMessageThread)
232 ++insideVSTCallback;
235 ~IdleCallRecursionPreventer()
237 if (isMessageThread)
238 --insideVSTCallback;
241 private:
242 const bool isMessageThread;
244 JUCE_DECLARE_NON_COPYABLE (IdleCallRecursionPreventer);
247 class VSTPluginWindow;
249 //==============================================================================
250 // Change this to disable logging of various VST activities
251 #ifndef VST_LOGGING
252 #define VST_LOGGING 1
253 #endif
255 #if VST_LOGGING
256 #define log(a) Logger::writeToLog(a);
257 #else
258 #define log(a)
259 #endif
261 //==============================================================================
262 #if JUCE_MAC && JUCE_PPC
263 static void* NewCFMFromMachO (void* const machofp) noexcept
265 void* result = (void*) new char[8];
267 ((void**) result)[0] = machofp;
268 ((void**) result)[1] = result;
270 return result;
272 #endif
274 //==============================================================================
275 #if JUCE_LINUX
277 extern Display* display;
278 extern XContext windowHandleXContext;
280 typedef void (*EventProcPtr) (XEvent* ev);
282 static bool xErrorTriggered;
284 namespace
286 int temporaryErrorHandler (Display*, XErrorEvent*)
288 xErrorTriggered = true;
289 return 0;
292 int getPropertyFromXWindow (Window handle, Atom atom)
294 XErrorHandler oldErrorHandler = XSetErrorHandler (temporaryErrorHandler);
295 xErrorTriggered = false;
297 int userSize;
298 unsigned long bytes, userCount;
299 unsigned char* data;
300 Atom userType;
302 XGetWindowProperty (display, handle, atom, 0, 1, false, AnyPropertyType,
303 &userType, &userSize, &userCount, &bytes, &data);
305 XSetErrorHandler (oldErrorHandler);
307 return (userCount == 1 && ! xErrorTriggered) ? *reinterpret_cast<int*> (data)
308 : 0;
311 Window getChildWindow (Window windowToCheck)
313 Window rootWindow, parentWindow;
314 Window* childWindows;
315 unsigned int numChildren;
317 XQueryTree (display,
318 windowToCheck,
319 &rootWindow,
320 &parentWindow,
321 &childWindows,
322 &numChildren);
324 if (numChildren > 0)
325 return childWindows [0];
327 return 0;
330 void translateJuceToXButtonModifiers (const MouseEvent& e, XEvent& ev) noexcept
332 if (e.mods.isLeftButtonDown())
334 ev.xbutton.button = Button1;
335 ev.xbutton.state |= Button1Mask;
337 else if (e.mods.isRightButtonDown())
339 ev.xbutton.button = Button3;
340 ev.xbutton.state |= Button3Mask;
342 else if (e.mods.isMiddleButtonDown())
344 ev.xbutton.button = Button2;
345 ev.xbutton.state |= Button2Mask;
349 void translateJuceToXMotionModifiers (const MouseEvent& e, XEvent& ev) noexcept
351 if (e.mods.isLeftButtonDown()) ev.xmotion.state |= Button1Mask;
352 else if (e.mods.isRightButtonDown()) ev.xmotion.state |= Button3Mask;
353 else if (e.mods.isMiddleButtonDown()) ev.xmotion.state |= Button2Mask;
356 void translateJuceToXCrossingModifiers (const MouseEvent& e, XEvent& ev) noexcept
358 if (e.mods.isLeftButtonDown()) ev.xcrossing.state |= Button1Mask;
359 else if (e.mods.isRightButtonDown()) ev.xcrossing.state |= Button3Mask;
360 else if (e.mods.isMiddleButtonDown()) ev.xcrossing.state |= Button2Mask;
363 void translateJuceToXMouseWheelModifiers (const MouseEvent& e, const float increment, XEvent& ev) noexcept
365 if (increment < 0)
367 ev.xbutton.button = Button5;
368 ev.xbutton.state |= Button5Mask;
370 else if (increment > 0)
372 ev.xbutton.button = Button4;
373 ev.xbutton.state |= Button4Mask;
378 #endif
380 //==============================================================================
381 class ModuleHandle : public ReferenceCountedObject
383 public:
384 //==============================================================================
385 File file;
386 MainCall moduleMain;
387 String pluginName;
389 static Array <ModuleHandle*>& getActiveModules()
391 static Array <ModuleHandle*> activeModules;
392 return activeModules;
395 //==============================================================================
396 static ModuleHandle* findOrCreateModule (const File& file)
398 for (int i = getActiveModules().size(); --i >= 0;)
400 ModuleHandle* const module = getActiveModules().getUnchecked(i);
402 if (module->file == file)
403 return module;
406 _fpreset(); // (doesn't do any harm)
408 const IdleCallRecursionPreventer icrp;
409 shellUIDToCreate = 0;
411 log ("Attempting to load VST: " + file.getFullPathName());
413 ScopedPointer <ModuleHandle> m (new ModuleHandle (file));
415 if (! m->open())
416 m = nullptr;
418 _fpreset(); // (doesn't do any harm)
420 return m.release();
423 //==============================================================================
424 ModuleHandle (const File& file_)
425 : file (file_),
426 moduleMain (0),
427 #if JUCE_WINDOWS || JUCE_LINUX
428 hModule (0)
429 #elif JUCE_MAC
430 fragId (0), resHandle (0), bundleRef (0), resFileId (0)
431 #endif
433 getActiveModules().add (this);
435 #if JUCE_WINDOWS || JUCE_LINUX
436 fullParentDirectoryPathName = file_.getParentDirectory().getFullPathName();
437 #elif JUCE_MAC
438 FSRef ref;
439 PlatformUtilities::makeFSRefFromPath (&ref, file_.getParentDirectory().getFullPathName());
440 FSGetCatalogInfo (&ref, kFSCatInfoNone, 0, 0, &parentDirFSSpec, 0);
441 #endif
444 ~ModuleHandle()
446 getActiveModules().removeValue (this);
447 close();
450 //==============================================================================
451 #if JUCE_WINDOWS || JUCE_LINUX
452 void* hModule;
453 String fullParentDirectoryPathName;
455 bool open()
457 #if JUCE_WINDOWS
458 static bool timePeriodSet = false;
460 if (! timePeriodSet)
462 timePeriodSet = true;
463 timeBeginPeriod (2);
465 #endif
467 pluginName = file.getFileNameWithoutExtension();
469 hModule = PlatformUtilities::loadDynamicLibrary (file.getFullPathName());
471 moduleMain = (MainCall) PlatformUtilities::getProcedureEntryPoint (hModule, "VSTPluginMain");
473 if (moduleMain == 0)
474 moduleMain = (MainCall) PlatformUtilities::getProcedureEntryPoint (hModule, "main");
476 return moduleMain != 0;
479 void close()
481 _fpreset(); // (doesn't do any harm)
483 PlatformUtilities::freeDynamicLibrary (hModule);
486 void closeEffect (AEffect* eff)
488 eff->dispatcher (eff, effClose, 0, 0, 0, 0);
491 #else
492 CFragConnectionID fragId;
493 Handle resHandle;
494 CFBundleRef bundleRef;
495 FSSpec parentDirFSSpec;
496 short resFileId;
498 bool open()
500 bool ok = false;
501 const String filename (file.getFullPathName());
503 if (file.hasFileExtension (".vst"))
505 const char* const utf8 = filename.toUTF8().getAddress();
506 CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8,
507 strlen (utf8), file.isDirectory());
509 if (url != 0)
511 bundleRef = CFBundleCreate (kCFAllocatorDefault, url);
512 CFRelease (url);
514 if (bundleRef != 0)
516 if (CFBundleLoadExecutable (bundleRef))
518 moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("main_macho"));
520 if (moduleMain == 0)
521 moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("VSTPluginMain"));
523 if (moduleMain != 0)
525 CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName"));
527 if (name != 0)
529 if (CFGetTypeID (name) == CFStringGetTypeID())
531 char buffer[1024];
533 if (CFStringGetCString ((CFStringRef) name, buffer, sizeof (buffer), CFStringGetSystemEncoding()))
534 pluginName = buffer;
538 if (pluginName.isEmpty())
539 pluginName = file.getFileNameWithoutExtension();
541 resFileId = CFBundleOpenBundleResourceMap (bundleRef);
543 ok = true;
547 if (! ok)
549 CFBundleUnloadExecutable (bundleRef);
550 CFRelease (bundleRef);
551 bundleRef = 0;
556 #if JUCE_PPC
557 else
559 FSRef fn;
561 if (FSPathMakeRef ((UInt8*) filename.toUTF8().getAddress(), &fn, 0) == noErr)
563 resFileId = FSOpenResFile (&fn, fsRdPerm);
565 if (resFileId != -1)
567 const int numEffs = Count1Resources ('aEff');
569 for (int i = 0; i < numEffs; ++i)
571 resHandle = Get1IndResource ('aEff', i + 1);
573 if (resHandle != 0)
575 OSType type;
576 Str255 name;
577 SInt16 id;
578 GetResInfo (resHandle, &id, &type, name);
579 pluginName = String ((const char*) name + 1, name[0]);
580 DetachResource (resHandle);
581 HLock (resHandle);
583 Ptr ptr;
584 Str255 errorText;
586 OSErr err = GetMemFragment (*resHandle, GetHandleSize (resHandle),
587 name, kPrivateCFragCopy,
588 &fragId, &ptr, errorText);
590 if (err == noErr)
592 moduleMain = (MainCall) newMachOFromCFM (ptr);
593 ok = true;
595 else
597 HUnlock (resHandle);
600 break;
604 if (! ok)
605 CloseResFile (resFileId);
609 #endif
611 return ok;
614 void close()
616 #if JUCE_PPC
617 if (fragId != 0)
619 if (moduleMain != 0)
620 disposeMachOFromCFM ((void*) moduleMain);
622 CloseConnection (&fragId);
623 HUnlock (resHandle);
625 if (resFileId != 0)
626 CloseResFile (resFileId);
628 else
629 #endif
630 if (bundleRef != 0)
632 CFBundleCloseBundleResourceMap (bundleRef, resFileId);
634 if (CFGetRetainCount (bundleRef) == 1)
635 CFBundleUnloadExecutable (bundleRef);
637 if (CFGetRetainCount (bundleRef) > 0)
638 CFRelease (bundleRef);
642 void closeEffect (AEffect* eff)
644 #if JUCE_PPC
645 if (fragId != 0)
647 Array<void*> thingsToDelete;
648 thingsToDelete.add ((void*) eff->dispatcher);
649 thingsToDelete.add ((void*) eff->process);
650 thingsToDelete.add ((void*) eff->setParameter);
651 thingsToDelete.add ((void*) eff->getParameter);
652 thingsToDelete.add ((void*) eff->processReplacing);
654 eff->dispatcher (eff, effClose, 0, 0, 0, 0);
656 for (int i = thingsToDelete.size(); --i >= 0;)
657 disposeMachOFromCFM (thingsToDelete[i]);
659 else
660 #endif
662 eff->dispatcher (eff, effClose, 0, 0, 0, 0);
666 #if JUCE_PPC
667 static void* newMachOFromCFM (void* cfmfp)
669 if (cfmfp == 0)
670 return nullptr;
672 UInt32* const mfp = new UInt32[6];
674 mfp[0] = 0x3d800000 | ((UInt32) cfmfp >> 16);
675 mfp[1] = 0x618c0000 | ((UInt32) cfmfp & 0xffff);
676 mfp[2] = 0x800c0000;
677 mfp[3] = 0x804c0004;
678 mfp[4] = 0x7c0903a6;
679 mfp[5] = 0x4e800420;
681 MakeDataExecutable (mfp, sizeof (UInt32) * 6);
682 return mfp;
685 static void disposeMachOFromCFM (void* ptr)
687 delete[] static_cast <UInt32*> (ptr);
690 void coerceAEffectFunctionCalls (AEffect* eff)
692 if (fragId != 0)
694 eff->dispatcher = (AEffectDispatcherProc) newMachOFromCFM ((void*) eff->dispatcher);
695 eff->process = (AEffectProcessProc) newMachOFromCFM ((void*) eff->process);
696 eff->setParameter = (AEffectSetParameterProc) newMachOFromCFM ((void*) eff->setParameter);
697 eff->getParameter = (AEffectGetParameterProc) newMachOFromCFM ((void*) eff->getParameter);
698 eff->processReplacing = (AEffectProcessProc) newMachOFromCFM ((void*) eff->processReplacing);
701 #endif
703 #endif
705 private:
706 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModuleHandle);
709 //==============================================================================
711 An instance of a plugin, created by a VSTPluginFormat.
714 class VSTPluginInstance : public AudioPluginInstance,
715 private Timer,
716 private AsyncUpdater
718 public:
719 //==============================================================================
720 ~VSTPluginInstance();
722 //==============================================================================
723 // AudioPluginInstance methods:
725 void fillInPluginDescription (PluginDescription& desc) const
727 desc.name = name;
730 char buffer [512] = { 0 };
731 dispatch (effGetEffectName, 0, 0, buffer, 0);
733 desc.descriptiveName = String (buffer).trim();
735 if (desc.descriptiveName.isEmpty())
736 desc.descriptiveName = name;
739 desc.fileOrIdentifier = module->file.getFullPathName();
740 desc.uid = getUID();
741 desc.lastFileModTime = module->file.getLastModificationTime();
742 desc.pluginFormatName = "VST";
743 desc.category = getCategory();
746 char buffer [kVstMaxVendorStrLen + 8] = { 0 };
747 dispatch (effGetVendorString, 0, 0, buffer, 0);
748 desc.manufacturerName = buffer;
751 desc.version = getVersion();
752 desc.numInputChannels = getNumInputChannels();
753 desc.numOutputChannels = getNumOutputChannels();
754 desc.isInstrument = (effect != nullptr && (effect->flags & effFlagsIsSynth) != 0);
757 void* getPlatformSpecificData() { return effect; }
758 const String getName() const { return name; }
759 int getUID() const;
760 bool acceptsMidi() const { return wantsMidiMessages; }
761 bool producesMidi() const { return dispatch (effCanDo, 0, 0, (void*) "sendVstMidiEvent", 0) > 0; }
763 //==============================================================================
764 // AudioProcessor methods:
766 void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock);
767 void releaseResources();
768 void processBlock (AudioSampleBuffer& buffer,
769 MidiBuffer& midiMessages);
771 bool hasEditor() const { return effect != nullptr && (effect->flags & effFlagsHasEditor) != 0; }
772 AudioProcessorEditor* createEditor();
774 const String getInputChannelName (int index) const;
775 bool isInputChannelStereoPair (int index) const;
777 const String getOutputChannelName (int index) const;
778 bool isOutputChannelStereoPair (int index) const;
780 //==============================================================================
781 int getNumParameters() { return effect != nullptr ? effect->numParams : 0; }
782 float getParameter (int index);
783 void setParameter (int index, float newValue);
784 const String getParameterName (int index);
785 const String getParameterText (int index);
786 bool isParameterAutomatable (int index) const;
788 //==============================================================================
789 int getNumPrograms() { return effect != nullptr ? effect->numPrograms : 0; }
790 int getCurrentProgram() { return dispatch (effGetProgram, 0, 0, 0, 0); }
791 void setCurrentProgram (int index);
792 const String getProgramName (int index);
793 void changeProgramName (int index, const String& newName);
795 //==============================================================================
796 void getStateInformation (MemoryBlock& destData);
797 void getCurrentProgramStateInformation (MemoryBlock& destData);
798 void setStateInformation (const void* data, int sizeInBytes);
799 void setCurrentProgramStateInformation (const void* data, int sizeInBytes);
801 //==============================================================================
802 void timerCallback();
803 void handleAsyncUpdate();
804 VstIntPtr handleCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt);
806 private:
807 //==============================================================================
808 friend class VSTPluginWindow;
809 friend class VSTPluginFormat;
811 AEffect* effect;
812 String name;
813 CriticalSection lock;
814 bool wantsMidiMessages, initialised, isPowerOn;
815 mutable StringArray programNames;
816 AudioSampleBuffer tempBuffer;
817 CriticalSection midiInLock;
818 MidiBuffer incomingMidi;
819 VSTMidiEventList midiEventsToSend;
820 VstTimeInfo vstHostTime;
822 ReferenceCountedObjectPtr <ModuleHandle> module;
824 //==============================================================================
825 int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) const;
826 bool restoreProgramSettings (const fxProgram* const prog);
827 const String getCurrentProgramName();
828 void setParamsInProgramBlock (fxProgram* const prog);
829 void updateStoredProgramNames();
830 void initialise();
831 void handleMidiFromPlugin (const VstEvents* const events);
832 void createTempParameterStore (MemoryBlock& dest);
833 void restoreFromTempParameterStore (const MemoryBlock& mb);
834 const String getParameterLabel (int index) const;
836 bool usesChunks() const noexcept { return effect != nullptr && (effect->flags & effFlagsProgramChunks) != 0; }
837 void getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const;
838 void setChunkData (const char* data, int size, bool isPreset);
839 bool loadFromFXBFile (const void* data, int numBytes);
840 bool saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB);
842 int getVersionNumber() const noexcept { return effect != nullptr ? effect->version : 0; }
843 const String getVersion() const;
844 const String getCategory() const;
846 void setPower (const bool on);
848 VSTPluginInstance (const ReferenceCountedObjectPtr <ModuleHandle>& module);
849 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginInstance);
852 //==============================================================================
853 VSTPluginInstance::VSTPluginInstance (const ReferenceCountedObjectPtr <ModuleHandle>& module_)
854 : effect (nullptr),
855 name (module_->pluginName),
856 wantsMidiMessages (false),
857 initialised (false),
858 isPowerOn (false),
859 tempBuffer (1, 1),
860 module (module_)
864 const IdleCallRecursionPreventer icrp;
865 _fpreset();
867 log ("Creating VST instance: " + name);
869 #if JUCE_MAC
870 if (module->resFileId != 0)
871 UseResFile (module->resFileId);
873 #if JUCE_PPC
874 if (module->fragId != 0)
876 static void* audioMasterCoerced = nullptr;
877 if (audioMasterCoerced == nullptr)
878 audioMasterCoerced = NewCFMFromMachO ((void*) &audioMaster);
880 effect = module->moduleMain ((audioMasterCallback) audioMasterCoerced);
882 else
883 #endif
884 #endif
886 effect = module->moduleMain (&audioMaster);
889 if (effect != nullptr && effect->magic == kEffectMagic)
891 #if JUCE_PPC
892 module->coerceAEffectFunctionCalls (effect);
893 #endif
895 jassert (effect->resvd2 == 0);
896 jassert (effect->object != 0);
898 _fpreset(); // some dodgy plugs fuck around with this
900 else
902 effect = nullptr;
905 catch (...)
909 VSTPluginInstance::~VSTPluginInstance()
911 const ScopedLock sl (lock);
913 if (effect != nullptr && effect->magic == kEffectMagic)
917 #if JUCE_MAC
918 if (module->resFileId != 0)
919 UseResFile (module->resFileId);
920 #endif
922 // Must delete any editors before deleting the plugin instance!
923 jassert (getActiveEditor() == 0);
925 _fpreset(); // some dodgy plugs fuck around with this
927 module->closeEffect (effect);
929 catch (...)
933 module = nullptr;
934 effect = nullptr;
937 //==============================================================================
938 void VSTPluginInstance::initialise()
940 if (initialised || effect == 0)
941 return;
943 log ("Initialising VST: " + module->pluginName);
944 initialised = true;
946 dispatch (effIdentify, 0, 0, 0, 0);
948 if (getSampleRate() > 0)
949 dispatch (effSetSampleRate, 0, 0, 0, (float) getSampleRate());
951 if (getBlockSize() > 0)
952 dispatch (effSetBlockSize, 0, jmax (32, getBlockSize()), 0, 0);
954 dispatch (effOpen, 0, 0, 0, 0);
956 setPlayConfigDetails (effect->numInputs, effect->numOutputs,
957 getSampleRate(), getBlockSize());
959 if (getNumPrograms() > 1)
960 setCurrentProgram (0);
961 else
962 dispatch (effSetProgram, 0, 0, 0, 0);
964 int i;
965 for (i = effect->numInputs; --i >= 0;)
966 dispatch (effConnectInput, i, 1, 0, 0);
968 for (i = effect->numOutputs; --i >= 0;)
969 dispatch (effConnectOutput, i, 1, 0, 0);
971 updateStoredProgramNames();
973 wantsMidiMessages = dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0;
975 setLatencySamples (effect->initialDelay);
979 //==============================================================================
980 void VSTPluginInstance::prepareToPlay (double sampleRate_,
981 int samplesPerBlockExpected)
983 setPlayConfigDetails (effect->numInputs, effect->numOutputs,
984 sampleRate_, samplesPerBlockExpected);
986 setLatencySamples (effect->initialDelay);
988 vstHostTime.tempo = 120.0;
989 vstHostTime.timeSigNumerator = 4;
990 vstHostTime.timeSigDenominator = 4;
991 vstHostTime.sampleRate = sampleRate_;
992 vstHostTime.samplePos = 0;
993 vstHostTime.flags = kVstNanosValid; /*| kVstTransportPlaying | kVstTempoValid | kVstTimeSigValid*/;
995 initialise();
997 if (initialised)
999 wantsMidiMessages = wantsMidiMessages
1000 || (dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0);
1002 if (wantsMidiMessages)
1003 midiEventsToSend.ensureSize (256);
1004 else
1005 midiEventsToSend.freeEvents();
1007 incomingMidi.clear();
1009 dispatch (effSetSampleRate, 0, 0, 0, (float) sampleRate_);
1010 dispatch (effSetBlockSize, 0, jmax (16, samplesPerBlockExpected), 0, 0);
1012 tempBuffer.setSize (jmax (1, effect->numOutputs), samplesPerBlockExpected);
1014 if (! isPowerOn)
1015 setPower (true);
1017 // dodgy hack to force some plugins to initialise the sample rate..
1018 if ((! hasEditor()) && getNumParameters() > 0)
1020 const float old = getParameter (0);
1021 setParameter (0, (old < 0.5f) ? 1.0f : 0.0f);
1022 setParameter (0, old);
1025 dispatch (effStartProcess, 0, 0, 0, 0);
1029 void VSTPluginInstance::releaseResources()
1031 if (initialised)
1033 dispatch (effStopProcess, 0, 0, 0, 0);
1034 setPower (false);
1037 tempBuffer.setSize (1, 1);
1038 incomingMidi.clear();
1040 midiEventsToSend.freeEvents();
1043 void VSTPluginInstance::processBlock (AudioSampleBuffer& buffer,
1044 MidiBuffer& midiMessages)
1046 const int numSamples = buffer.getNumSamples();
1048 if (initialised)
1050 AudioPlayHead* playHead = getPlayHead();
1052 if (playHead != nullptr)
1054 AudioPlayHead::CurrentPositionInfo position;
1055 playHead->getCurrentPosition (position);
1057 vstHostTime.tempo = position.bpm;
1058 vstHostTime.timeSigNumerator = position.timeSigNumerator;
1059 vstHostTime.timeSigDenominator = position.timeSigDenominator;
1060 vstHostTime.ppqPos = position.ppqPosition;
1061 vstHostTime.barStartPos = position.ppqPositionOfLastBarStart;
1062 vstHostTime.flags |= kVstTempoValid | kVstTimeSigValid | kVstPpqPosValid | kVstBarsValid;
1064 if (position.isPlaying)
1065 vstHostTime.flags |= kVstTransportPlaying;
1066 else
1067 vstHostTime.flags &= ~kVstTransportPlaying;
1070 vstHostTime.nanoSeconds = getVSTHostTimeNanoseconds();
1072 if (wantsMidiMessages)
1074 midiEventsToSend.clear();
1075 midiEventsToSend.ensureSize (1);
1077 MidiBuffer::Iterator iter (midiMessages);
1078 const uint8* midiData;
1079 int numBytesOfMidiData, samplePosition;
1081 while (iter.getNextEvent (midiData, numBytesOfMidiData, samplePosition))
1083 midiEventsToSend.addEvent (midiData, numBytesOfMidiData,
1084 jlimit (0, numSamples - 1, samplePosition));
1089 effect->dispatcher (effect, effProcessEvents, 0, 0, midiEventsToSend.events, 0);
1091 catch (...)
1095 _clearfp();
1097 if ((effect->flags & effFlagsCanReplacing) != 0)
1101 effect->processReplacing (effect, buffer.getArrayOfChannels(), buffer.getArrayOfChannels(), numSamples);
1103 catch (...)
1106 else
1108 tempBuffer.setSize (effect->numOutputs, numSamples);
1109 tempBuffer.clear();
1113 effect->process (effect, buffer.getArrayOfChannels(), tempBuffer.getArrayOfChannels(), numSamples);
1115 catch (...)
1118 for (int i = effect->numOutputs; --i >= 0;)
1119 buffer.copyFrom (i, 0, tempBuffer.getSampleData (i), numSamples);
1122 else
1124 // Not initialised, so just bypass..
1125 for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
1126 buffer.clear (i, 0, buffer.getNumSamples());
1130 // copy any incoming midi..
1131 const ScopedLock sl (midiInLock);
1133 midiMessages.swapWith (incomingMidi);
1134 incomingMidi.clear();
1138 //==============================================================================
1139 void VSTPluginInstance::handleMidiFromPlugin (const VstEvents* const events)
1141 if (events != nullptr)
1143 const ScopedLock sl (midiInLock);
1144 VSTMidiEventList::addEventsToMidiBuffer (events, incomingMidi);
1148 //==============================================================================
1149 static Array <VSTPluginWindow*> activeVSTWindows;
1151 //==============================================================================
1152 class VSTPluginWindow : public AudioProcessorEditor,
1153 #if ! JUCE_MAC
1154 public ComponentMovementWatcher,
1155 #endif
1156 public Timer
1158 public:
1159 //==============================================================================
1160 VSTPluginWindow (VSTPluginInstance& plugin_)
1161 : AudioProcessorEditor (&plugin_),
1162 #if ! JUCE_MAC
1163 ComponentMovementWatcher (this),
1164 #endif
1165 plugin (plugin_),
1166 isOpen (false),
1167 recursiveResize (false),
1168 pluginWantsKeys (false),
1169 pluginRefusesToResize (false),
1170 alreadyInside (false)
1172 #if JUCE_WINDOWS
1173 sizeCheckCount = 0;
1174 pluginHWND = 0;
1175 #elif JUCE_LINUX
1176 pluginWindow = None;
1177 pluginProc = None;
1178 #else
1179 addAndMakeVisible (innerWrapper = new InnerWrapperComponent (this));
1180 #endif
1182 activeVSTWindows.add (this);
1184 setSize (1, 1);
1185 setOpaque (true);
1186 setVisible (true);
1189 ~VSTPluginWindow()
1191 #if JUCE_MAC
1192 innerWrapper = nullptr;
1193 #else
1194 closePluginWindow();
1195 #endif
1197 activeVSTWindows.removeValue (this);
1198 plugin.editorBeingDeleted (this);
1201 //==============================================================================
1202 #if ! JUCE_MAC
1203 void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
1205 if (recursiveResize)
1206 return;
1208 Component* const topComp = getTopLevelComponent();
1210 if (topComp->getPeer() != nullptr)
1212 const Point<int> pos (topComp->getLocalPoint (this, Point<int>()));
1214 recursiveResize = true;
1216 #if JUCE_WINDOWS
1217 if (pluginHWND != 0)
1218 MoveWindow (pluginHWND, pos.getX(), pos.getY(), getWidth(), getHeight(), TRUE);
1219 #elif JUCE_LINUX
1220 if (pluginWindow != 0)
1222 XResizeWindow (display, pluginWindow, getWidth(), getHeight());
1223 XMoveWindow (display, pluginWindow, pos.getX(), pos.getY());
1224 XMapRaised (display, pluginWindow);
1226 #endif
1228 recursiveResize = false;
1232 void componentVisibilityChanged()
1234 if (isShowing())
1235 openPluginWindow();
1236 else
1237 closePluginWindow();
1239 componentMovedOrResized (true, true);
1242 void componentPeerChanged()
1244 closePluginWindow();
1245 openPluginWindow();
1247 #endif
1249 //==============================================================================
1250 bool keyStateChanged (bool)
1252 return pluginWantsKeys;
1255 bool keyPressed (const KeyPress&)
1257 return pluginWantsKeys;
1260 //==============================================================================
1261 #if JUCE_MAC
1262 void paint (Graphics& g)
1264 g.fillAll (Colours::black);
1266 #else
1267 void paint (Graphics& g)
1269 if (isOpen)
1271 ComponentPeer* const peer = getPeer();
1273 if (peer != nullptr)
1275 const Point<int> pos (getScreenPosition() - peer->getScreenPosition());
1276 peer->addMaskedRegion (pos.getX(), pos.getY(), getWidth(), getHeight());
1278 #if JUCE_LINUX
1279 if (pluginWindow != 0)
1281 const Rectangle<int> clip (g.getClipBounds());
1283 XEvent ev = { 0 };
1284 ev.xexpose.type = Expose;
1285 ev.xexpose.display = display;
1286 ev.xexpose.window = pluginWindow;
1287 ev.xexpose.x = clip.getX();
1288 ev.xexpose.y = clip.getY();
1289 ev.xexpose.width = clip.getWidth();
1290 ev.xexpose.height = clip.getHeight();
1292 sendEventToChild (&ev);
1294 #endif
1297 else
1299 g.fillAll (Colours::black);
1302 #endif
1304 //==============================================================================
1305 void timerCallback()
1307 #if JUCE_WINDOWS
1308 if (--sizeCheckCount <= 0)
1310 sizeCheckCount = 10;
1312 checkPluginWindowSize();
1314 #endif
1318 static bool reentrant = false;
1320 if (! reentrant)
1322 reentrant = true;
1323 plugin.dispatch (effEditIdle, 0, 0, 0, 0);
1324 reentrant = false;
1327 catch (...)
1331 //==============================================================================
1332 void mouseDown (const MouseEvent& e)
1334 #if JUCE_LINUX
1335 if (pluginWindow == 0)
1336 return;
1338 toFront (true);
1340 XEvent ev = { 0 };
1341 ev.xbutton.display = display;
1342 ev.xbutton.type = ButtonPress;
1343 ev.xbutton.window = pluginWindow;
1344 ev.xbutton.root = RootWindow (display, DefaultScreen (display));
1345 ev.xbutton.time = CurrentTime;
1346 ev.xbutton.x = e.x;
1347 ev.xbutton.y = e.y;
1348 ev.xbutton.x_root = e.getScreenX();
1349 ev.xbutton.y_root = e.getScreenY();
1351 translateJuceToXButtonModifiers (e, ev);
1353 sendEventToChild (&ev);
1355 #elif JUCE_WINDOWS
1356 (void) e;
1358 toFront (true);
1359 #endif
1362 void broughtToFront()
1364 activeVSTWindows.removeValue (this);
1365 activeVSTWindows.add (this);
1367 #if JUCE_MAC
1368 dispatch (effEditTop, 0, 0, 0, 0);
1369 #endif
1372 //==============================================================================
1373 private:
1374 VSTPluginInstance& plugin;
1375 bool isOpen, recursiveResize;
1376 bool pluginWantsKeys, pluginRefusesToResize, alreadyInside;
1378 #if JUCE_WINDOWS
1379 HWND pluginHWND;
1380 void* originalWndProc;
1381 int sizeCheckCount;
1382 #elif JUCE_LINUX
1383 Window pluginWindow;
1384 EventProcPtr pluginProc;
1385 #endif
1387 //==============================================================================
1388 #if JUCE_MAC
1389 void openPluginWindow (WindowRef parentWindow)
1391 if (isOpen || parentWindow == 0)
1392 return;
1394 isOpen = true;
1396 ERect* rect = nullptr;
1397 dispatch (effEditGetRect, 0, 0, &rect, 0);
1398 dispatch (effEditOpen, 0, 0, parentWindow, 0);
1400 // do this before and after like in the steinberg example
1401 dispatch (effEditGetRect, 0, 0, &rect, 0);
1402 dispatch (effGetProgram, 0, 0, 0, 0); // also in steinberg code
1404 // Install keyboard hooks
1405 pluginWantsKeys = (dispatch (effKeysRequired, 0, 0, 0, 0) == 0);
1407 // double-check it's not too tiny
1408 int w = 250, h = 150;
1410 if (rect != nullptr)
1412 w = rect->right - rect->left;
1413 h = rect->bottom - rect->top;
1415 if (w == 0 || h == 0)
1417 w = 250;
1418 h = 150;
1422 w = jmax (w, 32);
1423 h = jmax (h, 32);
1425 setSize (w, h);
1427 startTimer (18 + JUCE_NAMESPACE::Random::getSystemRandom().nextInt (5));
1428 repaint();
1431 #else
1432 void openPluginWindow()
1434 if (isOpen || getWindowHandle() == 0)
1435 return;
1437 log ("Opening VST UI: " + plugin.name);
1438 isOpen = true;
1440 ERect* rect = nullptr;
1441 dispatch (effEditGetRect, 0, 0, &rect, 0);
1442 dispatch (effEditOpen, 0, 0, getWindowHandle(), 0);
1444 // do this before and after like in the steinberg example
1445 dispatch (effEditGetRect, 0, 0, &rect, 0);
1446 dispatch (effGetProgram, 0, 0, 0, 0); // also in steinberg code
1448 // Install keyboard hooks
1449 pluginWantsKeys = (dispatch (effKeysRequired, 0, 0, 0, 0) == 0);
1451 #if JUCE_WINDOWS
1452 originalWndProc = 0;
1453 pluginHWND = GetWindow ((HWND) getWindowHandle(), GW_CHILD);
1455 if (pluginHWND == 0)
1457 isOpen = false;
1458 setSize (300, 150);
1459 return;
1462 #pragma warning (push)
1463 #pragma warning (disable: 4244)
1465 originalWndProc = (void*) GetWindowLongPtr (pluginHWND, GWLP_WNDPROC);
1467 if (! pluginWantsKeys)
1468 SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) vstHookWndProc);
1470 #pragma warning (pop)
1472 int w, h;
1473 RECT r;
1474 GetWindowRect (pluginHWND, &r);
1475 w = r.right - r.left;
1476 h = r.bottom - r.top;
1478 if (rect != nullptr)
1480 const int rw = rect->right - rect->left;
1481 const int rh = rect->bottom - rect->top;
1483 if ((rw > 50 && rh > 50 && rw < 2000 && rh < 2000 && rw != w && rh != h)
1484 || ((w == 0 && rw > 0) || (h == 0 && rh > 0)))
1486 // very dodgy logic to decide which size is right.
1487 if (abs (rw - w) > 350 || abs (rh - h) > 350)
1489 SetWindowPos (pluginHWND, 0,
1490 0, 0, rw, rh,
1491 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
1493 GetWindowRect (pluginHWND, &r);
1495 w = r.right - r.left;
1496 h = r.bottom - r.top;
1498 pluginRefusesToResize = (w != rw) || (h != rh);
1500 w = rw;
1501 h = rh;
1506 #elif JUCE_LINUX
1507 pluginWindow = getChildWindow ((Window) getWindowHandle());
1509 if (pluginWindow != 0)
1510 pluginProc = (EventProcPtr) getPropertyFromXWindow (pluginWindow,
1511 XInternAtom (display, "_XEventProc", False));
1513 int w = 250, h = 150;
1515 if (rect != nullptr)
1517 w = rect->right - rect->left;
1518 h = rect->bottom - rect->top;
1520 if (w == 0 || h == 0)
1522 w = 250;
1523 h = 150;
1527 if (pluginWindow != 0)
1528 XMapRaised (display, pluginWindow);
1529 #endif
1531 // double-check it's not too tiny
1532 w = jmax (w, 32);
1533 h = jmax (h, 32);
1535 setSize (w, h);
1537 #if JUCE_WINDOWS
1538 checkPluginWindowSize();
1539 #endif
1541 startTimer (18 + JUCE_NAMESPACE::Random::getSystemRandom().nextInt (5));
1542 repaint();
1544 #endif
1546 //==============================================================================
1547 #if ! JUCE_MAC
1548 void closePluginWindow()
1550 if (isOpen)
1552 log ("Closing VST UI: " + plugin.getName());
1553 isOpen = false;
1555 dispatch (effEditClose, 0, 0, 0, 0);
1557 #if JUCE_WINDOWS
1558 #pragma warning (push)
1559 #pragma warning (disable: 4244)
1561 if (pluginHWND != 0 && IsWindow (pluginHWND))
1562 SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) originalWndProc);
1564 #pragma warning (pop)
1566 stopTimer();
1568 if (pluginHWND != 0 && IsWindow (pluginHWND))
1569 DestroyWindow (pluginHWND);
1571 pluginHWND = 0;
1572 #elif JUCE_LINUX
1573 stopTimer();
1574 pluginWindow = 0;
1575 pluginProc = 0;
1576 #endif
1579 #endif
1581 //==============================================================================
1582 int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt)
1584 return plugin.dispatch (opcode, index, value, ptr, opt);
1587 //==============================================================================
1588 #if JUCE_WINDOWS
1589 void checkPluginWindowSize()
1591 RECT r;
1592 GetWindowRect (pluginHWND, &r);
1593 const int w = r.right - r.left;
1594 const int h = r.bottom - r.top;
1596 if (isShowing() && w > 0 && h > 0
1597 && (w != getWidth() || h != getHeight())
1598 && ! pluginRefusesToResize)
1600 setSize (w, h);
1601 sizeCheckCount = 0;
1605 // hooks to get keyboard events from VST windows..
1606 static LRESULT CALLBACK vstHookWndProc (HWND hW, UINT message, WPARAM wParam, LPARAM lParam)
1608 for (int i = activeVSTWindows.size(); --i >= 0;)
1610 const VSTPluginWindow* const w = activeVSTWindows.getUnchecked (i);
1612 if (w->pluginHWND == hW)
1614 if (message == WM_CHAR
1615 || message == WM_KEYDOWN
1616 || message == WM_SYSKEYDOWN
1617 || message == WM_KEYUP
1618 || message == WM_SYSKEYUP
1619 || message == WM_APPCOMMAND)
1621 SendMessage ((HWND) w->getTopLevelComponent()->getWindowHandle(),
1622 message, wParam, lParam);
1625 return CallWindowProc ((WNDPROC) (w->originalWndProc),
1626 (HWND) w->pluginHWND,
1627 message,
1628 wParam,
1629 lParam);
1633 return DefWindowProc (hW, message, wParam, lParam);
1635 #endif
1637 #if JUCE_LINUX
1638 //==============================================================================
1639 // overload mouse/keyboard events to forward them to the plugin's inner window..
1640 void sendEventToChild (XEvent* event)
1642 if (pluginProc != 0)
1644 // if the plugin publishes an event procedure, pass the event directly..
1645 pluginProc (event);
1647 else if (pluginWindow != 0)
1649 // if the plugin has a window, then send the event to the window so that
1650 // its message thread will pick it up..
1651 XSendEvent (display, pluginWindow, False, 0L, event);
1652 XFlush (display);
1656 void mouseEnter (const MouseEvent& e)
1658 if (pluginWindow != 0)
1660 XEvent ev = { 0 };
1661 ev.xcrossing.display = display;
1662 ev.xcrossing.type = EnterNotify;
1663 ev.xcrossing.window = pluginWindow;
1664 ev.xcrossing.root = RootWindow (display, DefaultScreen (display));
1665 ev.xcrossing.time = CurrentTime;
1666 ev.xcrossing.x = e.x;
1667 ev.xcrossing.y = e.y;
1668 ev.xcrossing.x_root = e.getScreenX();
1669 ev.xcrossing.y_root = e.getScreenY();
1670 ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab
1671 ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual
1673 translateJuceToXCrossingModifiers (e, ev);
1675 sendEventToChild (&ev);
1679 void mouseExit (const MouseEvent& e)
1681 if (pluginWindow != 0)
1683 XEvent ev = { 0 };
1684 ev.xcrossing.display = display;
1685 ev.xcrossing.type = LeaveNotify;
1686 ev.xcrossing.window = pluginWindow;
1687 ev.xcrossing.root = RootWindow (display, DefaultScreen (display));
1688 ev.xcrossing.time = CurrentTime;
1689 ev.xcrossing.x = e.x;
1690 ev.xcrossing.y = e.y;
1691 ev.xcrossing.x_root = e.getScreenX();
1692 ev.xcrossing.y_root = e.getScreenY();
1693 ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab
1694 ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual
1695 ev.xcrossing.focus = hasKeyboardFocus (true); // TODO - yes ?
1697 translateJuceToXCrossingModifiers (e, ev);
1699 sendEventToChild (&ev);
1703 void mouseMove (const MouseEvent& e)
1705 if (pluginWindow != 0)
1707 XEvent ev = { 0 };
1708 ev.xmotion.display = display;
1709 ev.xmotion.type = MotionNotify;
1710 ev.xmotion.window = pluginWindow;
1711 ev.xmotion.root = RootWindow (display, DefaultScreen (display));
1712 ev.xmotion.time = CurrentTime;
1713 ev.xmotion.is_hint = NotifyNormal;
1714 ev.xmotion.x = e.x;
1715 ev.xmotion.y = e.y;
1716 ev.xmotion.x_root = e.getScreenX();
1717 ev.xmotion.y_root = e.getScreenY();
1719 sendEventToChild (&ev);
1723 void mouseDrag (const MouseEvent& e)
1725 if (pluginWindow != 0)
1727 XEvent ev = { 0 };
1728 ev.xmotion.display = display;
1729 ev.xmotion.type = MotionNotify;
1730 ev.xmotion.window = pluginWindow;
1731 ev.xmotion.root = RootWindow (display, DefaultScreen (display));
1732 ev.xmotion.time = CurrentTime;
1733 ev.xmotion.x = e.x ;
1734 ev.xmotion.y = e.y;
1735 ev.xmotion.x_root = e.getScreenX();
1736 ev.xmotion.y_root = e.getScreenY();
1737 ev.xmotion.is_hint = NotifyNormal;
1739 translateJuceToXMotionModifiers (e, ev);
1740 sendEventToChild (&ev);
1744 void mouseUp (const MouseEvent& e)
1746 if (pluginWindow != 0)
1748 XEvent ev = { 0 };
1749 ev.xbutton.display = display;
1750 ev.xbutton.type = ButtonRelease;
1751 ev.xbutton.window = pluginWindow;
1752 ev.xbutton.root = RootWindow (display, DefaultScreen (display));
1753 ev.xbutton.time = CurrentTime;
1754 ev.xbutton.x = e.x;
1755 ev.xbutton.y = e.y;
1756 ev.xbutton.x_root = e.getScreenX();
1757 ev.xbutton.y_root = e.getScreenY();
1759 translateJuceToXButtonModifiers (e, ev);
1760 sendEventToChild (&ev);
1764 void mouseWheelMove (const MouseEvent& e,
1765 float incrementX,
1766 float incrementY)
1768 if (pluginWindow != 0)
1770 XEvent ev = { 0 };
1771 ev.xbutton.display = display;
1772 ev.xbutton.type = ButtonPress;
1773 ev.xbutton.window = pluginWindow;
1774 ev.xbutton.root = RootWindow (display, DefaultScreen (display));
1775 ev.xbutton.time = CurrentTime;
1776 ev.xbutton.x = e.x;
1777 ev.xbutton.y = e.y;
1778 ev.xbutton.x_root = e.getScreenX();
1779 ev.xbutton.y_root = e.getScreenY();
1781 translateJuceToXMouseWheelModifiers (e, incrementY, ev);
1782 sendEventToChild (&ev);
1784 // TODO - put a usleep here ?
1786 ev.xbutton.type = ButtonRelease;
1787 sendEventToChild (&ev);
1790 #endif
1792 #if JUCE_MAC
1794 #if ! JUCE_SUPPORT_CARBON
1795 #error "To build VSTs, you need to enable the JUCE_SUPPORT_CARBON flag in your config!"
1796 #endif
1798 class InnerWrapperComponent : public CarbonViewWrapperComponent
1800 public:
1801 InnerWrapperComponent (VSTPluginWindow* const owner_)
1802 : owner (owner_),
1803 alreadyInside (false)
1807 ~InnerWrapperComponent()
1809 deleteWindow();
1812 HIViewRef attachView (WindowRef windowRef, HIViewRef rootView)
1814 owner->openPluginWindow (windowRef);
1815 return 0;
1818 void removeView (HIViewRef)
1820 owner->dispatch (effEditClose, 0, 0, 0, 0);
1821 owner->dispatch (effEditSleep, 0, 0, 0, 0);
1824 bool getEmbeddedViewSize (int& w, int& h)
1826 ERect* rect = nullptr;
1827 owner->dispatch (effEditGetRect, 0, 0, &rect, 0);
1828 w = rect->right - rect->left;
1829 h = rect->bottom - rect->top;
1830 return true;
1833 void mouseDown (int x, int y)
1835 if (! alreadyInside)
1837 alreadyInside = true;
1838 getTopLevelComponent()->toFront (true);
1839 owner->dispatch (effEditMouse, x, y, 0, 0);
1840 alreadyInside = false;
1842 else
1844 PostEvent (::mouseDown, 0);
1848 void paint()
1850 ComponentPeer* const peer = getPeer();
1852 if (peer != nullptr)
1854 const Point<int> pos (getScreenPosition() - peer->getScreenPosition());
1855 ERect r;
1856 r.left = pos.getX();
1857 r.right = r.left + getWidth();
1858 r.top = pos.getY();
1859 r.bottom = r.top + getHeight();
1861 owner->dispatch (effEditDraw, 0, 0, &r, 0);
1865 private:
1866 VSTPluginWindow* const owner;
1867 bool alreadyInside;
1870 friend class InnerWrapperComponent;
1871 ScopedPointer <InnerWrapperComponent> innerWrapper;
1873 void resized()
1875 innerWrapper->setSize (getWidth(), getHeight());
1877 #endif
1879 private:
1880 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginWindow);
1883 //==============================================================================
1884 AudioProcessorEditor* VSTPluginInstance::createEditor()
1886 if (hasEditor())
1887 return new VSTPluginWindow (*this);
1889 return nullptr;
1893 //==============================================================================
1894 void VSTPluginInstance::handleAsyncUpdate()
1896 // indicates that something about the plugin has changed..
1897 updateHostDisplay();
1900 //==============================================================================
1901 bool VSTPluginInstance::restoreProgramSettings (const fxProgram* const prog)
1903 if (vst_swap (prog->chunkMagic) == 'CcnK' && vst_swap (prog->fxMagic) == 'FxCk')
1905 changeProgramName (getCurrentProgram(), prog->prgName);
1907 for (int i = 0; i < vst_swap (prog->numParams); ++i)
1908 setParameter (i, vst_swapFloat (prog->params[i]));
1910 return true;
1913 return false;
1916 bool VSTPluginInstance::loadFromFXBFile (const void* const data,
1917 const int dataSize)
1919 if (dataSize < 28)
1920 return false;
1922 const fxSet* const set = (const fxSet*) data;
1924 if ((vst_swap (set->chunkMagic) != 'CcnK' && vst_swap (set->chunkMagic) != 'KncC')
1925 || vst_swap (set->version) > fxbVersionNum)
1926 return false;
1928 if (vst_swap (set->fxMagic) == 'FxBk')
1930 // bank of programs
1931 if (vst_swap (set->numPrograms) >= 0)
1933 const int oldProg = getCurrentProgram();
1934 const int numParams = vst_swap (((const fxProgram*) (set->programs))->numParams);
1935 const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float);
1937 for (int i = 0; i < vst_swap (set->numPrograms); ++i)
1939 if (i != oldProg)
1941 const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + i * progLen);
1942 if (((const char*) prog) - ((const char*) set) >= dataSize)
1943 return false;
1945 if (vst_swap (set->numPrograms) > 0)
1946 setCurrentProgram (i);
1948 if (! restoreProgramSettings (prog))
1949 return false;
1953 if (vst_swap (set->numPrograms) > 0)
1954 setCurrentProgram (oldProg);
1956 const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen);
1957 if (((const char*) prog) - ((const char*) set) >= dataSize)
1958 return false;
1960 if (! restoreProgramSettings (prog))
1961 return false;
1964 else if (vst_swap (set->fxMagic) == 'FxCk')
1966 // single program
1967 const fxProgram* const prog = (const fxProgram*) data;
1969 if (vst_swap (prog->chunkMagic) != 'CcnK')
1970 return false;
1972 changeProgramName (getCurrentProgram(), prog->prgName);
1974 for (int i = 0; i < vst_swap (prog->numParams); ++i)
1975 setParameter (i, vst_swapFloat (prog->params[i]));
1977 else if (vst_swap (set->fxMagic) == 'FBCh' || vst_swap (set->fxMagic) == 'hCBF')
1979 // non-preset chunk
1980 const fxChunkSet* const cset = (const fxChunkSet*) data;
1982 if (vst_swap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize)
1983 return false;
1985 setChunkData (cset->chunk, vst_swap (cset->chunkSize), false);
1987 else if (vst_swap (set->fxMagic) == 'FPCh' || vst_swap (set->fxMagic) == 'hCPF')
1989 // preset chunk
1990 const fxProgramSet* const cset = (const fxProgramSet*) data;
1992 if (vst_swap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize)
1993 return false;
1995 setChunkData (cset->chunk, vst_swap (cset->chunkSize), true);
1997 changeProgramName (getCurrentProgram(), cset->name);
1999 else
2001 return false;
2004 return true;
2007 //==============================================================================
2008 void VSTPluginInstance::setParamsInProgramBlock (fxProgram* const prog)
2010 const int numParams = getNumParameters();
2012 prog->chunkMagic = vst_swap ('CcnK');
2013 prog->byteSize = 0;
2014 prog->fxMagic = vst_swap ('FxCk');
2015 prog->version = vst_swap (fxbVersionNum);
2016 prog->fxID = vst_swap (getUID());
2017 prog->fxVersion = vst_swap (getVersionNumber());
2018 prog->numParams = vst_swap (numParams);
2020 getCurrentProgramName().copyToUTF8 (prog->prgName, sizeof (prog->prgName) - 1);
2022 for (int i = 0; i < numParams; ++i)
2023 prog->params[i] = vst_swapFloat (getParameter (i));
2026 bool VSTPluginInstance::saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB)
2028 const int numPrograms = getNumPrograms();
2029 const int numParams = getNumParameters();
2031 if (usesChunks())
2033 if (isFXB)
2035 MemoryBlock chunk;
2036 getChunkData (chunk, false, maxSizeMB);
2038 const size_t totalLen = sizeof (fxChunkSet) + chunk.getSize() - 8;
2039 dest.setSize (totalLen, true);
2041 fxChunkSet* const set = (fxChunkSet*) dest.getData();
2042 set->chunkMagic = vst_swap ('CcnK');
2043 set->byteSize = 0;
2044 set->fxMagic = vst_swap ('FBCh');
2045 set->version = vst_swap (fxbVersionNum);
2046 set->fxID = vst_swap (getUID());
2047 set->fxVersion = vst_swap (getVersionNumber());
2048 set->numPrograms = vst_swap (numPrograms);
2049 set->chunkSize = vst_swap ((long) chunk.getSize());
2051 chunk.copyTo (set->chunk, 0, chunk.getSize());
2053 else
2055 MemoryBlock chunk;
2056 getChunkData (chunk, true, maxSizeMB);
2058 const size_t totalLen = sizeof (fxProgramSet) + chunk.getSize() - 8;
2059 dest.setSize (totalLen, true);
2061 fxProgramSet* const set = (fxProgramSet*) dest.getData();
2062 set->chunkMagic = vst_swap ('CcnK');
2063 set->byteSize = 0;
2064 set->fxMagic = vst_swap ('FPCh');
2065 set->version = vst_swap (fxbVersionNum);
2066 set->fxID = vst_swap (getUID());
2067 set->fxVersion = vst_swap (getVersionNumber());
2068 set->numPrograms = vst_swap (numPrograms);
2069 set->chunkSize = vst_swap ((long) chunk.getSize());
2071 getCurrentProgramName().copyToUTF8 (set->name, sizeof (set->name) - 1);
2072 chunk.copyTo (set->chunk, 0, chunk.getSize());
2075 else
2077 if (isFXB)
2079 const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float);
2080 const int len = (sizeof (fxSet) - sizeof (fxProgram)) + progLen * jmax (1, numPrograms);
2081 dest.setSize (len, true);
2083 fxSet* const set = (fxSet*) dest.getData();
2084 set->chunkMagic = vst_swap ('CcnK');
2085 set->byteSize = 0;
2086 set->fxMagic = vst_swap ('FxBk');
2087 set->version = vst_swap (fxbVersionNum);
2088 set->fxID = vst_swap (getUID());
2089 set->fxVersion = vst_swap (getVersionNumber());
2090 set->numPrograms = vst_swap (numPrograms);
2092 const int oldProgram = getCurrentProgram();
2093 MemoryBlock oldSettings;
2094 createTempParameterStore (oldSettings);
2096 setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + oldProgram * progLen));
2098 for (int i = 0; i < numPrograms; ++i)
2100 if (i != oldProgram)
2102 setCurrentProgram (i);
2103 setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + i * progLen));
2107 setCurrentProgram (oldProgram);
2108 restoreFromTempParameterStore (oldSettings);
2110 else
2112 const int totalLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float);
2113 dest.setSize (totalLen, true);
2115 setParamsInProgramBlock ((fxProgram*) dest.getData());
2119 return true;
2122 void VSTPluginInstance::getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const
2124 if (usesChunks())
2126 void* data = nullptr;
2127 const int bytes = dispatch (effGetChunk, isPreset ? 1 : 0, 0, &data, 0.0f);
2129 if (data != nullptr && bytes <= maxSizeMB * 1024 * 1024)
2131 mb.setSize (bytes);
2132 mb.copyFrom (data, 0, bytes);
2137 void VSTPluginInstance::setChunkData (const char* data, int size, bool isPreset)
2139 if (size > 0 && usesChunks())
2141 dispatch (effSetChunk, isPreset ? 1 : 0, size, (void*) data, 0.0f);
2143 if (! isPreset)
2144 updateStoredProgramNames();
2148 //==============================================================================
2149 void VSTPluginInstance::timerCallback()
2151 if (dispatch (effIdle, 0, 0, 0, 0) == 0)
2152 stopTimer();
2155 int VSTPluginInstance::dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) const
2157 int result = 0;
2159 if (effect != nullptr)
2161 const ScopedLock sl (lock);
2162 const IdleCallRecursionPreventer icrp;
2166 #if JUCE_MAC
2167 if (module->resFileId != 0)
2168 UseResFile (module->resFileId);
2169 #endif
2171 result = effect->dispatcher (effect, opcode, index, value, ptr, opt);
2173 #if JUCE_MAC
2174 module->resFileId = CurResFile();
2175 #endif
2177 catch (...)
2181 return result;
2184 //==============================================================================
2185 namespace
2187 static const int defaultVSTSampleRateValue = 16384;
2188 static const int defaultVSTBlockSizeValue = 512;
2190 // handles non plugin-specific callbacks..
2191 VstIntPtr handleGeneralCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt)
2193 (void) index;
2194 (void) value;
2195 (void) opt;
2197 switch (opcode)
2199 case audioMasterCanDo:
2201 static const char* canDos[] = { "supplyIdle",
2202 "sendVstEvents",
2203 "sendVstMidiEvent",
2204 "sendVstTimeInfo",
2205 "receiveVstEvents",
2206 "receiveVstMidiEvent",
2207 "supportShell",
2208 "shellCategory" };
2210 for (int i = 0; i < numElementsInArray (canDos); ++i)
2211 if (strcmp (canDos[i], (const char*) ptr) == 0)
2212 return 1;
2214 return 0;
2217 case audioMasterVersion: return 0x2400;
2218 case audioMasterCurrentId: return shellUIDToCreate;
2219 case audioMasterGetNumAutomatableParameters: return 0;
2220 case audioMasterGetAutomationState: return 1;
2221 case audioMasterGetVendorVersion: return 0x0101;
2223 case audioMasterGetVendorString:
2224 case audioMasterGetProductString:
2226 String hostName ("Juce VST Host");
2228 if (JUCEApplication::getInstance() != nullptr)
2229 hostName = JUCEApplication::getInstance()->getApplicationName();
2231 hostName.copyToUTF8 ((char*) ptr, jmin (kVstMaxVendorStrLen, kVstMaxProductStrLen) - 1);
2232 break;
2235 case audioMasterGetSampleRate: return (VstIntPtr) defaultVSTSampleRateValue;
2236 case audioMasterGetBlockSize: return (VstIntPtr) defaultVSTBlockSizeValue;
2237 case audioMasterSetOutputSampleRate: return 0;
2239 default:
2240 DBG ("*** Unhandled VST Callback: " + String ((int) opcode));
2241 break;
2244 return 0;
2248 // handles callbacks for a specific plugin
2249 VstIntPtr VSTPluginInstance::handleCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt)
2251 switch (opcode)
2253 case audioMasterAutomate:
2254 sendParamChangeMessageToListeners (index, opt);
2255 break;
2257 case audioMasterProcessEvents:
2258 handleMidiFromPlugin ((const VstEvents*) ptr);
2259 break;
2261 case audioMasterGetTime:
2262 #if JUCE_MSVC
2263 #pragma warning (push)
2264 #pragma warning (disable: 4311)
2265 #endif
2267 return (VstIntPtr) &vstHostTime;
2269 #if JUCE_MSVC
2270 #pragma warning (pop)
2271 #endif
2272 break;
2274 case audioMasterIdle:
2275 if (insideVSTCallback == 0 && MessageManager::getInstance()->isThisTheMessageThread())
2277 const IdleCallRecursionPreventer icrp;
2279 #if JUCE_MAC
2280 if (getActiveEditor() != nullptr)
2281 dispatch (effEditIdle, 0, 0, 0, 0);
2282 #endif
2284 juce_callAnyTimersSynchronously();
2286 handleUpdateNowIfNeeded();
2288 for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
2289 ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow();
2291 break;
2293 case audioMasterUpdateDisplay:
2294 triggerAsyncUpdate();
2295 break;
2297 case audioMasterTempoAt:
2298 // returns (10000 * bpm)
2299 break;
2301 case audioMasterNeedIdle:
2302 startTimer (50);
2303 break;
2305 case audioMasterSizeWindow:
2306 if (getActiveEditor() != nullptr)
2307 getActiveEditor()->setSize (index, value);
2309 return 1;
2311 case audioMasterGetSampleRate:
2312 return (VstIntPtr) (getSampleRate() > 0 ? getSampleRate() : defaultVSTSampleRateValue);
2314 case audioMasterGetBlockSize:
2315 return (VstIntPtr) (getBlockSize() > 0 ? getBlockSize() : defaultVSTBlockSizeValue);
2317 case audioMasterWantMidi:
2318 wantsMidiMessages = true;
2319 break;
2321 case audioMasterGetDirectory:
2322 #if JUCE_MAC
2323 return (VstIntPtr) (void*) &module->parentDirFSSpec;
2324 #else
2325 return (VstIntPtr) (pointer_sized_uint) module->fullParentDirectoryPathName.toUTF8().getAddress();
2326 #endif
2328 case audioMasterGetAutomationState:
2329 // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write
2330 break;
2332 // none of these are handled (yet)..
2333 case audioMasterBeginEdit:
2334 case audioMasterEndEdit:
2335 case audioMasterSetTime:
2336 case audioMasterPinConnected:
2337 case audioMasterGetParameterQuantization:
2338 case audioMasterIOChanged:
2339 case audioMasterGetInputLatency:
2340 case audioMasterGetOutputLatency:
2341 case audioMasterGetPreviousPlug:
2342 case audioMasterGetNextPlug:
2343 case audioMasterWillReplaceOrAccumulate:
2344 case audioMasterGetCurrentProcessLevel:
2345 case audioMasterOfflineStart:
2346 case audioMasterOfflineRead:
2347 case audioMasterOfflineWrite:
2348 case audioMasterOfflineGetCurrentPass:
2349 case audioMasterOfflineGetCurrentMetaPass:
2350 case audioMasterVendorSpecific:
2351 case audioMasterSetIcon:
2352 case audioMasterGetLanguage:
2353 case audioMasterOpenWindow:
2354 case audioMasterCloseWindow:
2355 break;
2357 default:
2358 return handleGeneralCallback (opcode, index, value, ptr, opt);
2361 return 0;
2364 // entry point for all callbacks from the plugin
2365 static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
2369 if (effect != nullptr && effect->resvd2 != 0)
2371 return ((VSTPluginInstance*)(effect->resvd2))
2372 ->handleCallback (opcode, index, value, ptr, opt);
2375 return handleGeneralCallback (opcode, index, value, ptr, opt);
2377 catch (...)
2379 return 0;
2383 //==============================================================================
2384 const String VSTPluginInstance::getVersion() const
2386 unsigned int v = dispatch (effGetVendorVersion, 0, 0, 0, 0);
2388 String s;
2390 if (v == 0 || v == -1)
2391 v = getVersionNumber();
2393 if (v != 0)
2395 int versionBits[4];
2396 int n = 0;
2398 while (v != 0)
2400 versionBits [n++] = (v & 0xff);
2401 v >>= 8;
2404 s << 'V';
2406 while (n > 0)
2408 s << versionBits [--n];
2410 if (n > 0)
2411 s << '.';
2415 return s;
2418 int VSTPluginInstance::getUID() const
2420 int uid = effect != nullptr ? effect->uniqueID : 0;
2422 if (uid == 0)
2423 uid = module->file.hashCode();
2425 return uid;
2428 const String VSTPluginInstance::getCategory() const
2430 const char* result = nullptr;
2432 switch (dispatch (effGetPlugCategory, 0, 0, 0, 0))
2434 case kPlugCategEffect: result = "Effect"; break;
2435 case kPlugCategSynth: result = "Synth"; break;
2436 case kPlugCategAnalysis: result = "Anaylsis"; break;
2437 case kPlugCategMastering: result = "Mastering"; break;
2438 case kPlugCategSpacializer: result = "Spacial"; break;
2439 case kPlugCategRoomFx: result = "Reverb"; break;
2440 case kPlugSurroundFx: result = "Surround"; break;
2441 case kPlugCategRestoration: result = "Restoration"; break;
2442 case kPlugCategGenerator: result = "Tone generation"; break;
2443 default: break;
2446 return result;
2449 //==============================================================================
2450 float VSTPluginInstance::getParameter (int index)
2452 if (effect != nullptr && isPositiveAndBelow (index, (int) effect->numParams))
2456 const ScopedLock sl (lock);
2457 return effect->getParameter (effect, index);
2459 catch (...)
2464 return 0.0f;
2467 void VSTPluginInstance::setParameter (int index, float newValue)
2469 if (effect != nullptr && isPositiveAndBelow (index, (int) effect->numParams))
2473 const ScopedLock sl (lock);
2475 if (effect->getParameter (effect, index) != newValue)
2476 effect->setParameter (effect, index, newValue);
2478 catch (...)
2484 const String VSTPluginInstance::getParameterName (int index)
2486 if (effect != nullptr)
2488 jassert (index >= 0 && index < effect->numParams);
2490 char nm [256] = { 0 };
2491 dispatch (effGetParamName, index, 0, nm, 0);
2492 return String (nm).trim();
2495 return String::empty;
2498 const String VSTPluginInstance::getParameterLabel (int index) const
2500 if (effect != nullptr)
2502 jassert (index >= 0 && index < effect->numParams);
2504 char nm [256] = { 0 };
2505 dispatch (effGetParamLabel, index, 0, nm, 0);
2506 return String (nm).trim();
2509 return String::empty;
2512 const String VSTPluginInstance::getParameterText (int index)
2514 if (effect != nullptr)
2516 jassert (index >= 0 && index < effect->numParams);
2518 char nm [256] = { 0 };
2519 dispatch (effGetParamDisplay, index, 0, nm, 0);
2520 return String (nm).trim();
2523 return String::empty;
2526 bool VSTPluginInstance::isParameterAutomatable (int index) const
2528 if (effect != nullptr)
2530 jassert (index >= 0 && index < effect->numParams);
2531 return dispatch (effCanBeAutomated, index, 0, 0, 0) != 0;
2534 return false;
2537 void VSTPluginInstance::createTempParameterStore (MemoryBlock& dest)
2539 dest.setSize (64 + 4 * getNumParameters());
2540 dest.fillWith (0);
2542 getCurrentProgramName().copyToUTF8 ((char*) dest.getData(), 63);
2544 float* const p = (float*) (((char*) dest.getData()) + 64);
2545 for (int i = 0; i < getNumParameters(); ++i)
2546 p[i] = getParameter(i);
2549 void VSTPluginInstance::restoreFromTempParameterStore (const MemoryBlock& m)
2551 changeProgramName (getCurrentProgram(), (const char*) m.getData());
2553 float* p = (float*) (((char*) m.getData()) + 64);
2554 for (int i = 0; i < getNumParameters(); ++i)
2555 setParameter (i, p[i]);
2558 //==============================================================================
2559 void VSTPluginInstance::setCurrentProgram (int newIndex)
2561 if (getNumPrograms() > 0 && newIndex != getCurrentProgram())
2562 dispatch (effSetProgram, 0, jlimit (0, getNumPrograms() - 1, newIndex), 0, 0);
2565 const String VSTPluginInstance::getProgramName (int index)
2567 if (index == getCurrentProgram())
2569 return getCurrentProgramName();
2571 else if (effect != nullptr)
2573 char nm [256] = { 0 };
2575 if (dispatch (effGetProgramNameIndexed,
2576 jlimit (0, getNumPrograms(), index),
2577 -1, nm, 0) != 0)
2579 return String (nm).trim();
2583 return programNames [index];
2586 void VSTPluginInstance::changeProgramName (int index, const String& newName)
2588 if (index == getCurrentProgram())
2590 if (getNumPrograms() > 0 && newName != getCurrentProgramName())
2591 dispatch (effSetProgramName, 0, 0, (void*) newName.substring (0, 24).toUTF8().getAddress(), 0.0f);
2593 else
2595 jassertfalse; // xxx not implemented!
2599 void VSTPluginInstance::updateStoredProgramNames()
2601 if (effect != nullptr && getNumPrograms() > 0)
2603 char nm [256] = { 0 };
2605 // only do this if the plugin can't use indexed names..
2606 if (dispatch (effGetProgramNameIndexed, 0, -1, nm, 0) == 0)
2608 const int oldProgram = getCurrentProgram();
2609 MemoryBlock oldSettings;
2610 createTempParameterStore (oldSettings);
2612 for (int i = 0; i < getNumPrograms(); ++i)
2614 setCurrentProgram (i);
2615 getCurrentProgramName(); // (this updates the list)
2618 setCurrentProgram (oldProgram);
2619 restoreFromTempParameterStore (oldSettings);
2624 const String VSTPluginInstance::getCurrentProgramName()
2626 if (effect != nullptr)
2628 char nm [256] = { 0 };
2629 dispatch (effGetProgramName, 0, 0, nm, 0);
2631 const int index = getCurrentProgram();
2632 if (programNames[index].isEmpty())
2634 while (programNames.size() < index)
2635 programNames.add (String::empty);
2637 programNames.set (index, String (nm).trim());
2640 return String (nm).trim();
2643 return String::empty;
2646 //==============================================================================
2647 const String VSTPluginInstance::getInputChannelName (int index) const
2649 if (index >= 0 && index < getNumInputChannels())
2651 VstPinProperties pinProps;
2652 if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0)
2653 return String (pinProps.label, sizeof (pinProps.label));
2656 return String::empty;
2659 bool VSTPluginInstance::isInputChannelStereoPair (int index) const
2661 if (index < 0 || index >= getNumInputChannels())
2662 return false;
2664 VstPinProperties pinProps;
2665 if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0)
2666 return (pinProps.flags & kVstPinIsStereo) != 0;
2668 return true;
2671 const String VSTPluginInstance::getOutputChannelName (int index) const
2673 if (index >= 0 && index < getNumOutputChannels())
2675 VstPinProperties pinProps;
2676 if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0)
2677 return String (pinProps.label, sizeof (pinProps.label));
2680 return String::empty;
2683 bool VSTPluginInstance::isOutputChannelStereoPair (int index) const
2685 if (index < 0 || index >= getNumOutputChannels())
2686 return false;
2688 VstPinProperties pinProps;
2689 if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0)
2690 return (pinProps.flags & kVstPinIsStereo) != 0;
2692 return true;
2695 //==============================================================================
2696 void VSTPluginInstance::setPower (const bool on)
2698 dispatch (effMainsChanged, 0, on ? 1 : 0, 0, 0);
2699 isPowerOn = on;
2702 //==============================================================================
2703 const int defaultMaxSizeMB = 64;
2705 void VSTPluginInstance::getStateInformation (MemoryBlock& destData)
2707 saveToFXBFile (destData, true, defaultMaxSizeMB);
2710 void VSTPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData)
2712 saveToFXBFile (destData, false, defaultMaxSizeMB);
2715 void VSTPluginInstance::setStateInformation (const void* data, int sizeInBytes)
2717 loadFromFXBFile (data, sizeInBytes);
2720 void VSTPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes)
2722 loadFromFXBFile (data, sizeInBytes);
2725 //==============================================================================
2726 //==============================================================================
2727 VSTPluginFormat::VSTPluginFormat()
2731 VSTPluginFormat::~VSTPluginFormat()
2735 void VSTPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& results,
2736 const String& fileOrIdentifier)
2738 if (! fileMightContainThisPluginType (fileOrIdentifier))
2739 return;
2741 PluginDescription desc;
2742 desc.fileOrIdentifier = fileOrIdentifier;
2743 desc.uid = 0;
2745 ScopedPointer <VSTPluginInstance> instance (dynamic_cast <VSTPluginInstance*> (createInstanceFromDescription (desc)));
2747 if (instance == 0)
2748 return;
2752 #if JUCE_MAC
2753 if (instance->module->resFileId != 0)
2754 UseResFile (instance->module->resFileId);
2755 #endif
2757 instance->fillInPluginDescription (desc);
2759 VstPlugCategory category = (VstPlugCategory) instance->dispatch (effGetPlugCategory, 0, 0, 0, 0);
2761 if (category != kPlugCategShell)
2763 // Normal plugin...
2764 results.add (new PluginDescription (desc));
2766 instance->dispatch (effOpen, 0, 0, 0, 0);
2768 else
2770 // It's a shell plugin, so iterate all the subtypes...
2771 for (;;)
2773 char shellEffectName [64] = { 0 };
2774 const int uid = instance->dispatch (effShellGetNextPlugin, 0, 0, shellEffectName, 0);
2776 if (uid == 0)
2778 break;
2780 else
2782 desc.uid = uid;
2783 desc.name = shellEffectName;
2784 desc.descriptiveName = shellEffectName;
2786 bool alreadyThere = false;
2788 for (int i = results.size(); --i >= 0;)
2790 PluginDescription* const d = results.getUnchecked(i);
2792 if (d->isDuplicateOf (desc))
2794 alreadyThere = true;
2795 break;
2799 if (! alreadyThere)
2800 results.add (new PluginDescription (desc));
2805 catch (...)
2807 // crashed while loading...
2811 AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const PluginDescription& desc)
2813 ScopedPointer <VSTPluginInstance> result;
2815 if (fileMightContainThisPluginType (desc.fileOrIdentifier))
2817 File file (desc.fileOrIdentifier);
2819 const File previousWorkingDirectory (File::getCurrentWorkingDirectory());
2820 file.getParentDirectory().setAsCurrentWorkingDirectory();
2822 const ReferenceCountedObjectPtr <ModuleHandle> module (ModuleHandle::findOrCreateModule (file));
2824 if (module != nullptr)
2826 shellUIDToCreate = desc.uid;
2828 result = new VSTPluginInstance (module);
2830 if (result->effect != nullptr)
2832 result->effect->resvd2 = (VstIntPtr) (pointer_sized_int) (VSTPluginInstance*) result;
2833 result->initialise();
2835 else
2837 result = nullptr;
2841 previousWorkingDirectory.setAsCurrentWorkingDirectory();
2844 return result.release();
2847 bool VSTPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier)
2849 const File f (fileOrIdentifier);
2851 #if JUCE_MAC
2852 if (f.isDirectory() && f.hasFileExtension (".vst"))
2853 return true;
2855 #if JUCE_PPC
2856 FSRef fileRef;
2857 if (PlatformUtilities::makeFSRefFromPath (&fileRef, f.getFullPathName()))
2859 const short resFileId = FSOpenResFile (&fileRef, fsRdPerm);
2861 if (resFileId != -1)
2863 const int numEffects = Count1Resources ('aEff');
2864 CloseResFile (resFileId);
2866 if (numEffects > 0)
2867 return true;
2870 #endif
2872 return false;
2873 #elif JUCE_WINDOWS
2874 return f.existsAsFile() && f.hasFileExtension (".dll");
2875 #elif JUCE_LINUX
2876 return f.existsAsFile() && f.hasFileExtension (".so");
2877 #endif
2880 String VSTPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier)
2882 return fileOrIdentifier;
2885 bool VSTPluginFormat::doesPluginStillExist (const PluginDescription& desc)
2887 return File (desc.fileOrIdentifier).exists();
2890 StringArray VSTPluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive)
2892 StringArray results;
2894 for (int j = 0; j < directoriesToSearch.getNumPaths(); ++j)
2895 recursiveFileSearch (results, directoriesToSearch [j], recursive);
2897 return results;
2900 void VSTPluginFormat::recursiveFileSearch (StringArray& results, const File& dir, const bool recursive)
2902 // avoid allowing the dir iterator to be recursive, because we want to avoid letting it delve inside
2903 // .component or .vst directories.
2904 DirectoryIterator iter (dir, false, "*", File::findFilesAndDirectories);
2906 while (iter.next())
2908 const File f (iter.getFile());
2909 bool isPlugin = false;
2911 if (fileMightContainThisPluginType (f.getFullPathName()))
2913 isPlugin = true;
2914 results.add (f.getFullPathName());
2917 if (recursive && (! isPlugin) && f.isDirectory())
2918 recursiveFileSearch (results, f, true);
2922 FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch()
2924 #if JUCE_MAC
2925 return FileSearchPath ("~/Library/Audio/Plug-Ins/VST;/Library/Audio/Plug-Ins/VST");
2926 #elif JUCE_WINDOWS
2927 const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName());
2929 return FileSearchPath (programFiles + "\\Steinberg\\VstPlugins");
2930 #elif JUCE_LINUX
2931 return FileSearchPath ("/usr/lib/vst");
2932 #endif
2936 END_JUCE_NAMESPACE
2938 #endif
2940 #undef log
2942 #endif