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 ==============================================================================
29 #pragma warning (disable : 4996 4100)
36 #include <X11/Xutil.h>
37 #include <X11/Xatom.h>
40 #include <Carbon/Carbon.h>
43 #ifdef PRAGMA_ALIGN_SUPPORTED
44 #undef PRAGMA_ALIGN_SUPPORTED
45 #define PRAGMA_ALIGN_SUPPORTED 1
48 #include "../juce_IncludeCharacteristics.h"
50 #if JucePlugin_Build_VST
52 //==============================================================================
53 /* These files come with the Steinberg VST SDK - to get them, you'll need to
54 visit the Steinberg website and jump through some hoops to sign up as a
57 Then, you'll need to make sure your include path contains your "vstsdk2.3" or
58 "vstsdk2.4" directory.
60 Note that the JUCE_USE_VSTSDK_2_4 macro should be defined in JucePluginCharacteristics.h
62 #if JUCE_USE_VSTSDK_2_4
67 // VSTSDK V2.4 includes..
68 #include <public.sdk/source/vst2.x/audioeffectx.h>
69 #include <public.sdk/source/vst2.x/aeffeditor.h>
70 #include <public.sdk/source/vst2.x/audioeffectx.cpp>
71 #include <public.sdk/source/vst2.x/audioeffect.cpp>
73 #if ! VST_2_4_EXTENSIONS
74 #error "You're probably trying to include the wrong VSTSDK version - make sure your include path matches the JUCE_USE_VSTSDK_2_4 flag"
78 // VSTSDK V2.3 includes..
79 #include <source/common/audioeffectx.h>
80 #include <source/common/AEffEditor.hpp>
81 #include <source/common/audioeffectx.cpp>
82 #include <source/common/AudioEffect.cpp>
84 #if (! VST_2_3_EXTENSIONS) || VST_2_4_EXTENSIONS
85 #error "You're probably trying to include the wrong VSTSDK version - make sure your include path matches the JUCE_USE_VSTSDK_2_4 flag"
88 #define __aeffect__ // (needed for juce_VSTMidiEventList.h to work)
90 typedef long VstInt32
;
91 typedef long VstIntPtr
;
93 enum Vst2StringConstants
97 kVstMaxShortLabelLen
= 8,
98 kVstMaxCategLabelLen
= 24,
99 kVstMaxFileNameLen
= 100
102 enum VstSmpteFrameRate
104 kVstSmpte24fps
= 0, // 24 fps
105 kVstSmpte25fps
= 1, // 25 fps
106 kVstSmpte2997fps
= 2, // 29.97 fps
107 kVstSmpte30fps
= 3, // 30 fps
108 kVstSmpte2997dfps
= 4, // 29.97 drop
109 kVstSmpte30dfps
= 5, // 30 drop
110 kVstSmpteFilm16mm
= 6, // Film 16mm
111 kVstSmpteFilm35mm
= 7, // Film 35mm
112 kVstSmpte239fps
= 10, // HDTV: 23.976 fps
113 kVstSmpte249fps
= 11, // HDTV: 24.976 fps
114 kVstSmpte599fps
= 12, // HDTV: 59.94 fps
115 kVstSmpte60fps
= 13 // HDTV: 60 fps
118 struct VstMidiSysexEvent
120 VstInt32 type
; // #kVstSysexType
121 VstInt32 byteSize
; // sizeof (VstMidiSysexEvent)
122 VstInt32 deltaFrames
; // sample frames related to the current block start sample position
123 VstInt32 flags
; // none defined yet (should be zero)
124 VstInt32 dumpBytes
; // byte size of sysexDump
125 VstIntPtr resvd1
; // zero (Reserved for future use)
126 char* sysexDump
; // sysex dump
127 VstIntPtr resvd2
; // zero (Reserved for future use)
130 typedef int VstSpeakerArrangementType
;
133 //==============================================================================
135 #pragma pack (push, 8)
138 #include "../juce_PluginHeaders.h"
139 #include "../juce_PluginHostType.h"
147 class JuceVSTWrapper
;
148 static bool recursionCheck
= false;
149 static JUCE_NAMESPACE::uint32 lastMasterIdleCall
= 0;
152 extern void juce_callAnyTimersSynchronously();
155 extern void initialiseMac();
156 extern void* attachComponentToWindowRef (Component
* component
, void* windowRef
);
157 extern void detachComponentFromWindowRef (Component
* component
, void* nsWindow
);
158 extern void setNativeHostWindowSize (void* nsWindow
, Component
* editorComp
, int newWidth
, int newHeight
, const PluginHostType
& host
);
159 extern void checkWindowVisibility (void* nsWindow
, Component
* component
);
160 extern void forwardCurrentKeyEventToHost (Component
* component
);
164 extern Display
* display
;
169 //==============================================================================
174 HWND
findMDIParentOf (HWND w
)
176 const int frameThickness
= GetSystemMetrics (SM_CYFIXEDFRAME
);
180 HWND parent
= GetParent (w
);
185 TCHAR windowType
[32] = { 0 };
186 GetClassName (parent
, windowType
, 31);
188 if (String (windowType
).equalsIgnoreCase ("MDIClient"))
191 RECT windowPos
, parentPos
;
192 GetWindowRect (w
, &windowPos
);
193 GetWindowRect (parent
, &parentPos
);
195 const int dw
= (parentPos
.right
- parentPos
.left
) - (windowPos
.right
- windowPos
.left
);
196 const int dh
= (parentPos
.bottom
- parentPos
.top
) - (windowPos
.bottom
- windowPos
.top
);
198 if (dw
> 100 || dh
> 100)
203 if (dw
== 2 * frameThickness
)
210 //==============================================================================
211 static HHOOK mouseWheelHook
= 0;
212 static int mouseHookUsers
= 0;
214 LRESULT CALLBACK
mouseWheelHookCallback (int nCode
, WPARAM wParam
, LPARAM lParam
)
216 if (nCode
>= 0 && wParam
== WM_MOUSEWHEEL
)
218 const MOUSEHOOKSTRUCTEX
& hs
= *(MOUSEHOOKSTRUCTEX
*) lParam
;
220 Component
* const comp
= Desktop::getInstance().findComponentAt (Point
<int> (hs
.pt
.x
,
222 if (comp
!= nullptr && comp
->getWindowHandle() != 0)
223 return PostMessage ((HWND
) comp
->getWindowHandle(), WM_MOUSEWHEEL
,
224 hs
.mouseData
& 0xffff0000, (hs
.pt
.x
& 0xffff) | (hs
.pt
.y
<< 16));
227 return CallNextHookEx (mouseWheelHook
, nCode
, wParam
, lParam
);
230 void registerMouseWheelHook()
232 if (mouseHookUsers
++ == 0)
233 mouseWheelHook
= SetWindowsHookEx (WH_MOUSE
, mouseWheelHookCallback
,
234 (HINSTANCE
) PlatformUtilities::getCurrentModuleInstanceHandle(),
235 GetCurrentThreadId());
238 void unregisterMouseWheelHook()
240 if (--mouseHookUsers
== 0 && mouseWheelHook
!= 0)
242 UnhookWindowsHookEx (mouseWheelHook
);
248 //==============================================================================
251 class SharedMessageThread
: public Thread
254 SharedMessageThread()
255 : Thread ("VstMessageThread"),
260 while (! initialised
)
264 ~SharedMessageThread()
266 signalThreadShouldExit();
267 JUCEApplication::quit();
268 waitForThreadToExit (5000);
269 clearSingletonInstance();
274 initialiseJuce_GUI();
277 MessageManager::getInstance()->setCurrentThreadAsMessageThread();
279 while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250))
283 juce_DeclareSingleton (SharedMessageThread
, false);
289 juce_ImplementSingleton (SharedMessageThread
)
293 static Array
<void*> activePlugins
;
295 //==============================================================================
297 This is an AudioEffectX object that holds and wraps our AudioProcessor...
299 class JuceVSTWrapper
: public AudioEffectX
,
301 public AudioProcessorListener
,
305 //==============================================================================
306 JuceVSTWrapper (audioMasterCallback audioMaster
, AudioProcessor
* const filter_
)
307 : AudioEffectX (audioMaster
, filter_
->getNumPrograms(), filter_
->getNumParameters()),
310 speakerIn (kSpeakerArrEmpty
),
311 speakerOut (kSpeakerArrEmpty
),
312 numInChans (JucePlugin_MaxNumInputChannels
),
313 numOutChans (JucePlugin_MaxNumOutputChannels
),
314 isProcessing (false),
316 firstProcessCallback (true),
317 shouldDeleteEditor (false),
320 filter
->setPlayConfigDetails (numInChans
, numOutChans
, 0, 0);
321 filter
->setPlayHead (this);
322 filter
->addListener (this);
324 cEffect
.flags
|= effFlagsHasEditor
;
325 cEffect
.version
= (long) (JucePlugin_VersionCode
);
327 setUniqueID ((int) (JucePlugin_VSTUniqueID
));
329 #if JucePlugin_WantsMidiInput && ! JUCE_USE_VSTSDK_2_4
333 setNumInputs (numInChans
);
334 setNumOutputs (numOutChans
);
336 canProcessReplacing (true);
338 #if ! JUCE_USE_VSTSDK_2_4
343 isSynth ((JucePlugin_IsSynth
) != 0);
344 noTail (((JucePlugin_SilenceInProducesSilenceOut
) != 0) && (JucePlugin_TailLengthSeconds
<= 0));
345 setInitialDelay (filter
->getLatencySamples());
346 programsAreChunks (true);
348 activePlugins
.add (this);
357 MessageManagerLock mmLock
;
360 deleteEditor (false);
367 jassert (editorComp
== 0);
370 deleteTempChannels();
372 jassert (activePlugins
.contains (this));
373 activePlugins
.removeValue (this);
376 if (activePlugins
.size() == 0)
379 SharedMessageThread::deleteInstance();
387 // Note: most hosts call this on the UI thread, but wavelab doesn't, so be careful in here.
388 if (filter
->hasEditor())
389 cEffect
.flags
|= effFlagsHasEditor
;
391 cEffect
.flags
&= ~effFlagsHasEditor
;
396 // Note: most hosts call this on the UI thread, but wavelab doesn't, so be careful in here.
399 if (MessageManager::getInstance()->isThisTheMessageThread())
400 deleteEditor (false);
403 //==============================================================================
404 bool getEffectName (char* name
)
406 String (JucePlugin_Name
).copyToUTF8 (name
, 64);
410 bool getVendorString (char* text
)
412 String (JucePlugin_Manufacturer
).copyToUTF8 (text
, 64);
416 bool getProductString (char* text
) { return getEffectName (text
); }
417 VstInt32
getVendorVersion() { return JucePlugin_VersionCode
; }
418 VstPlugCategory
getPlugCategory() { return JucePlugin_VSTCategory
; }
419 bool keysRequired() { return (JucePlugin_EditorRequiresKeyboardFocus
) != 0; }
421 VstInt32
canDo (char* text
)
425 if (strcmp (text
, "receiveVstEvents") == 0
426 || strcmp (text
, "receiveVstMidiEvent") == 0
427 || strcmp (text
, "receiveVstMidiEvents") == 0)
429 #if JucePlugin_WantsMidiInput
435 else if (strcmp (text
, "sendVstEvents") == 0
436 || strcmp (text
, "sendVstMidiEvent") == 0
437 || strcmp (text
, "sendVstMidiEvents") == 0)
439 #if JucePlugin_ProducesMidiOutput
445 else if (strcmp (text
, "receiveVstTimeInfo") == 0
446 || strcmp (text
, "conformsToWindowRules") == 0)
450 else if (strcmp (text
, "openCloseAnyThread") == 0)
452 // This tells Wavelab to use the UI thread to invoke open/close,
453 // like all other hosts do.
460 bool getInputProperties (VstInt32 index
, VstPinProperties
* properties
)
462 if (filter
== nullptr || index
>= JucePlugin_MaxNumInputChannels
)
465 setPinProperties (*properties
, filter
->getInputChannelName ((int) index
),
466 speakerIn
, filter
->isInputChannelStereoPair ((int) index
));
470 bool getOutputProperties (VstInt32 index
, VstPinProperties
* properties
)
472 if (filter
== nullptr || index
>= JucePlugin_MaxNumOutputChannels
)
475 setPinProperties (*properties
, filter
->getOutputChannelName ((int) index
),
476 speakerOut
, filter
->isOutputChannelStereoPair ((int) index
));
480 static void setPinProperties (VstPinProperties
& properties
, const String
& name
,
481 VstSpeakerArrangementType type
, const bool isPair
)
483 name
.copyToUTF8 (properties
.label
, kVstMaxLabelLen
- 1);
484 name
.copyToUTF8 (properties
.shortLabel
, kVstMaxShortLabelLen
- 1);
486 if (type
!= kSpeakerArrEmpty
)
488 properties
.flags
= kVstPinUseSpeaker
;
489 properties
.arrangementType
= type
;
493 properties
.flags
= kVstPinIsActive
;
494 properties
.arrangementType
= 0;
497 properties
.flags
|= kVstPinIsStereo
;
501 //==============================================================================
502 VstInt32
processEvents (VstEvents
* events
)
504 #if JucePlugin_WantsMidiInput
505 VSTMidiEventList::addEventsToMidiBuffer (events
, midiEvents
);
512 void process (float** inputs
, float** outputs
, VstInt32 numSamples
)
514 const int numIn
= numInChans
;
515 const int numOut
= numOutChans
;
517 AudioSampleBuffer
temp (numIn
, numSamples
);
519 for (i
= numIn
; --i
>= 0;)
520 memcpy (temp
.getSampleData (i
), outputs
[i
], sizeof (float) * numSamples
);
522 processReplacing (inputs
, outputs
, numSamples
);
524 AudioSampleBuffer
dest (outputs
, numOut
, numSamples
);
526 for (i
= jmin (numIn
, numOut
); --i
>= 0;)
527 dest
.addFrom (i
, 0, temp
, i
, 0, numSamples
);
530 void processReplacing (float** inputs
, float** outputs
, VstInt32 numSamples
)
532 if (firstProcessCallback
)
534 firstProcessCallback
= false;
536 // if this fails, the host hasn't called resume() before processing
537 jassert (isProcessing
);
539 // (tragically, some hosts actually need this, although it's stupid to have
544 filter
->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */);
547 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
548 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST
)
549 filter
->setNonRealtime (true);
553 #if JUCE_DEBUG && ! JucePlugin_ProducesMidiOutput
554 const int numMidiEventsComingIn
= midiEvents
.getNumEvents();
557 jassert (activePlugins
.contains (this));
560 const ScopedLock
sl (filter
->getCallbackLock());
562 const int numIn
= numInChans
;
563 const int numOut
= numOutChans
;
565 if (filter
->isSuspended())
567 for (int i
= 0; i
< numOut
; ++i
)
568 zeromem (outputs
[i
], sizeof (float) * numSamples
);
573 for (i
= 0; i
< numOut
; ++i
)
575 float* chan
= tempChannels
.getUnchecked(i
);
581 // if some output channels are disabled, some hosts supply the same buffer
582 // for multiple channels - this buggers up our method of copying the
583 // inputs over the outputs, so we need to create unique temp buffers in this case..
584 for (int j
= i
; --j
>= 0;)
586 if (outputs
[j
] == chan
)
588 chan
= new float [blockSize
* 2];
589 tempChannels
.set (i
, chan
);
595 if (i
< numIn
&& chan
!= inputs
[i
])
596 memcpy (chan
, inputs
[i
], sizeof (float) * numSamples
);
601 for (; i
< numIn
; ++i
)
602 channels
[i
] = inputs
[i
];
604 AudioSampleBuffer
chans (channels
, jmax (numIn
, numOut
), numSamples
);
606 filter
->processBlock (chans
, midiEvents
);
610 if (! midiEvents
.isEmpty())
612 #if JucePlugin_ProducesMidiOutput
613 const int numEvents
= midiEvents
.getNumEvents();
615 outgoingEvents
.ensureSize (numEvents
);
616 outgoingEvents
.clear();
618 const JUCE_NAMESPACE::uint8
* midiEventData
;
619 int midiEventSize
, midiEventPosition
;
620 MidiBuffer::Iterator
i (midiEvents
);
622 while (i
.getNextEvent (midiEventData
, midiEventSize
, midiEventPosition
))
624 jassert (midiEventPosition
>= 0 && midiEventPosition
< numSamples
);
626 outgoingEvents
.addEvent (midiEventData
, midiEventSize
, midiEventPosition
);
629 sendVstEventsToHost (outgoingEvents
.events
);
631 /* This assertion is caused when you've added some events to the
632 midiMessages array in your processBlock() method, which usually means
633 that you're trying to send them somewhere. But in this case they're
636 If your plugin does want to send midi messages, you'll need to set
637 the JucePlugin_ProducesMidiOutput macro to 1 in your
638 JucePluginCharacteristics.h file.
640 If you don't want to produce any midi output, then you should clear the
641 midiMessages array at the end of your processBlock() method, to
642 indicate that you don't want any of the events to be passed through
645 jassert (midiEvents
.getNumEvents() <= numMidiEventsComingIn
);
652 //==============================================================================
653 VstInt32
startProcess() { return 0; }
654 VstInt32
stopProcess() { return 0; }
658 if (filter
!= nullptr)
661 channels
.calloc (numInChans
+ numOutChans
);
663 double rate
= getSampleRate();
668 const int blockSize
= getBlockSize();
669 jassert (blockSize
> 0);
671 firstProcessCallback
= true;
673 filter
->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */);
674 filter
->setPlayConfigDetails (numInChans
, numOutChans
, rate
, blockSize
);
676 deleteTempChannels();
678 filter
->prepareToPlay (rate
, blockSize
);
680 midiEvents
.ensureSize (2048);
683 setInitialDelay (filter
->getLatencySamples());
685 AudioEffectX::resume();
687 #if JucePlugin_ProducesMidiOutput
688 outgoingEvents
.ensureSize (512);
691 #if JucePlugin_WantsMidiInput && ! JUCE_USE_VSTSDK_2_4
699 if (filter
!= nullptr)
701 AudioEffectX::suspend();
703 filter
->releaseResources();
704 outgoingEvents
.freeEvents();
706 isProcessing
= false;
709 deleteTempChannels();
713 bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo
& info
)
715 const VstTimeInfo
* const ti
= getTimeInfo (kVstPpqPosValid
| kVstTempoValid
| kVstBarsValid
//| kVstCyclePosValid
716 | kVstTimeSigValid
| kVstSmpteValid
| kVstClockValid
);
718 if (ti
== nullptr || ti
->sampleRate
<= 0)
721 info
.bpm
= (ti
->flags
& kVstTempoValid
) != 0 ? ti
->tempo
: 0.0;
723 if ((ti
->flags
& kVstTimeSigValid
) != 0)
725 info
.timeSigNumerator
= ti
->timeSigNumerator
;
726 info
.timeSigDenominator
= ti
->timeSigDenominator
;
730 info
.timeSigNumerator
= 4;
731 info
.timeSigDenominator
= 4;
734 info
.timeInSeconds
= ti
->samplePos
/ ti
->sampleRate
;
735 info
.ppqPosition
= (ti
->flags
& kVstPpqPosValid
) != 0 ? ti
->ppqPos
: 0.0;
736 info
.ppqPositionOfLastBarStart
= (ti
->flags
& kVstBarsValid
) != 0 ? ti
->barStartPos
: 0.0;
738 if ((ti
->flags
& kVstSmpteValid
) != 0)
740 AudioPlayHead::FrameRateType rate
= AudioPlayHead::fpsUnknown
;
743 switch (ti
->smpteFrameRate
)
745 case kVstSmpte24fps
: rate
= AudioPlayHead::fps24
; fps
= 24.0; break;
746 case kVstSmpte25fps
: rate
= AudioPlayHead::fps25
; fps
= 25.0; break;
747 case kVstSmpte2997fps
: rate
= AudioPlayHead::fps2997
; fps
= 29.97; break;
748 case kVstSmpte30fps
: rate
= AudioPlayHead::fps30
; fps
= 30.0; break;
749 case kVstSmpte2997dfps
: rate
= AudioPlayHead::fps2997drop
; fps
= 29.97; break;
750 case kVstSmpte30dfps
: rate
= AudioPlayHead::fps30drop
; fps
= 30.0; break;
752 case kVstSmpteFilm16mm
:
753 case kVstSmpteFilm35mm
: fps
= 24.0; break;
755 case kVstSmpte239fps
: fps
= 23.976; break;
756 case kVstSmpte249fps
: fps
= 24.976; break;
757 case kVstSmpte599fps
: fps
= 59.94; break;
758 case kVstSmpte60fps
: fps
= 60; break;
760 default: jassertfalse
; // unknown frame-rate..
763 info
.frameRate
= rate
;
764 info
.editOriginTime
= ti
->smpteOffset
/ (80.0 * fps
);
768 info
.frameRate
= AudioPlayHead::fpsUnknown
;
769 info
.editOriginTime
= 0;
772 info
.isRecording
= (ti
->flags
& kVstTransportRecording
) != 0;
773 info
.isPlaying
= (ti
->flags
& kVstTransportPlaying
) != 0 || info
.isRecording
;
778 //==============================================================================
779 VstInt32
getProgram()
781 return filter
!= nullptr ? filter
->getCurrentProgram() : 0;
784 void setProgram (VstInt32 program
)
786 if (filter
!= nullptr)
787 filter
->setCurrentProgram (program
);
790 void setProgramName (char* name
)
792 if (filter
!= nullptr)
793 filter
->changeProgramName (filter
->getCurrentProgram(), name
);
796 void getProgramName (char* name
)
798 if (filter
!= nullptr)
799 filter
->getProgramName (filter
->getCurrentProgram()).copyToUTF8 (name
, 24);
802 bool getProgramNameIndexed (VstInt32
/*category*/, VstInt32 index
, char* text
)
804 if (filter
!= nullptr && isPositiveAndBelow (index
, filter
->getNumPrograms()))
806 filter
->getProgramName (index
).copyToUTF8 (text
, 24);
813 //==============================================================================
814 float getParameter (VstInt32 index
)
816 if (filter
== nullptr)
819 jassert (isPositiveAndBelow (index
, filter
->getNumParameters()));
820 return filter
->getParameter (index
);
823 void setParameter (VstInt32 index
, float value
)
825 if (filter
!= nullptr)
827 jassert (isPositiveAndBelow (index
, filter
->getNumParameters()));
828 filter
->setParameter (index
, value
);
832 void getParameterDisplay (VstInt32 index
, char* text
)
834 if (filter
!= nullptr)
836 jassert (isPositiveAndBelow (index
, filter
->getNumParameters()));
837 filter
->getParameterText (index
).copyToUTF8 (text
, 24); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more.
841 void getParameterName (VstInt32 index
, char* text
)
843 if (filter
!= nullptr)
845 jassert (isPositiveAndBelow (index
, filter
->getNumParameters()));
846 filter
->getParameterName (index
).copyToUTF8 (text
, 16); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more.
850 void audioProcessorParameterChanged (AudioProcessor
*, int index
, float newValue
)
852 setParameterAutomated (index
, newValue
);
855 void audioProcessorParameterChangeGestureBegin (AudioProcessor
*, int index
) { beginEdit (index
); }
856 void audioProcessorParameterChangeGestureEnd (AudioProcessor
*, int index
) { endEdit (index
); }
858 void audioProcessorChanged (AudioProcessor
*)
863 bool canParameterBeAutomated (VstInt32 index
)
865 return filter
!= nullptr && filter
->isParameterAutomatable ((int) index
);
868 class ChannelConfigComparator
871 static int compareElements (const short* const first
, const short* const second
) noexcept
873 if (first
[0] < second
[0]) return -1;
874 else if (first
[0] > second
[0]) return 1;
875 else if (first
[1] < second
[1]) return -1;
876 else if (first
[1] > second
[1]) return 1;
882 bool setSpeakerArrangement (VstSpeakerArrangement
* pluginInput
,
883 VstSpeakerArrangement
* pluginOutput
)
885 short channelConfigs
[][2] = { JucePlugin_PreferredChannelConfigurations
};
887 Array
<short*> channelConfigsSorted
;
888 ChannelConfigComparator comp
;
890 for (int i
= 0; i
< numElementsInArray (channelConfigs
); ++i
)
891 channelConfigsSorted
.addSorted (comp
, channelConfigs
[i
]);
893 for (int i
= channelConfigsSorted
.size(); --i
>= 0;)
895 const short* const config
= channelConfigsSorted
.getUnchecked(i
);
896 bool inCountMatches
= (config
[0] == pluginInput
->numChannels
);
897 bool outCountMatches
= (config
[1] == pluginOutput
->numChannels
);
899 if (inCountMatches
&& outCountMatches
)
901 speakerIn
= (VstSpeakerArrangementType
) pluginInput
->type
;
902 speakerOut
= (VstSpeakerArrangementType
) pluginOutput
->type
;
903 numInChans
= pluginInput
->numChannels
;
904 numOutChans
= pluginOutput
->numChannels
;
906 filter
->setPlayConfigDetails (numInChans
, numOutChans
,
907 filter
->getSampleRate(),
908 filter
->getBlockSize());
916 //==============================================================================
917 VstInt32
getChunk (void** data
, bool onlyStoreCurrentProgramData
)
919 if (filter
== nullptr)
922 chunkMemory
.setSize (0);
923 if (onlyStoreCurrentProgramData
)
924 filter
->getCurrentProgramStateInformation (chunkMemory
);
926 filter
->getStateInformation (chunkMemory
);
928 *data
= (void*) chunkMemory
.getData();
930 // because the chunk is only needed temporarily by the host (or at least you'd
931 // hope so) we'll give it a while and then free it in the timer callback.
932 chunkMemoryTime
= JUCE_NAMESPACE::Time::getApproximateMillisecondCounter();
934 return (VstInt32
) chunkMemory
.getSize();
937 VstInt32
setChunk (void* data
, VstInt32 byteSize
, bool onlyRestoreCurrentProgramData
)
939 if (filter
== nullptr)
942 chunkMemory
.setSize (0);
945 if (byteSize
> 0 && data
!= nullptr)
947 if (onlyRestoreCurrentProgramData
)
948 filter
->setCurrentProgramStateInformation (data
, byteSize
);
950 filter
->setStateInformation (data
, byteSize
);
958 if (shouldDeleteEditor
)
960 shouldDeleteEditor
= false;
964 if (chunkMemoryTime
> 0
965 && chunkMemoryTime
< JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000
969 chunkMemory
.setSize (0);
974 checkWindowVisibility (hostWindow
, editorComp
);
982 if (Component::isMouseButtonDownAnywhere() && ! recursionCheck
)
984 const JUCE_NAMESPACE::uint32 now
= JUCE_NAMESPACE::Time::getMillisecondCounter();
986 if (now
> lastMasterIdleCall
+ 20 && editorComp
!= nullptr)
988 lastMasterIdleCall
= now
;
990 recursionCheck
= true;
992 recursionCheck
= false;
997 void doIdleCallback()
999 // (wavelab calls this on a separate thread and causes a deadlock)..
1000 if (MessageManager::getInstance()->isThisTheMessageThread()
1001 && ! recursionCheck
)
1003 recursionCheck
= true;
1005 JUCE_AUTORELEASEPOOL
1006 juce_callAnyTimersSynchronously();
1008 for (int i
= ComponentPeer::getNumPeers(); --i
>= 0;)
1009 ComponentPeer::getPeer (i
)->performAnyPendingRepaintsNow();
1011 recursionCheck
= false;
1015 void createEditorComp()
1017 if (hasShutdown
|| filter
== nullptr)
1020 if (editorComp
== nullptr)
1022 AudioProcessorEditor
* const ed
= filter
->createEditorIfNeeded();
1026 cEffect
.flags
|= effFlagsHasEditor
;
1027 ed
->setOpaque (true);
1028 ed
->setVisible (true);
1030 editorComp
= new EditorCompWrapper (*this, ed
);
1034 cEffect
.flags
&= ~effFlagsHasEditor
;
1038 shouldDeleteEditor
= false;
1041 void deleteEditor (bool canDeleteLaterIfModal
)
1043 JUCE_AUTORELEASEPOOL
1044 PopupMenu::dismissAllActiveMenus();
1046 jassert (! recursionCheck
);
1047 recursionCheck
= true;
1049 if (editorComp
!= nullptr)
1051 Component
* const modalComponent
= Component::getCurrentlyModalComponent();
1052 if (modalComponent
!= nullptr)
1054 modalComponent
->exitModalState (0);
1056 if (canDeleteLaterIfModal
)
1058 shouldDeleteEditor
= true;
1059 recursionCheck
= false;
1065 if (hostWindow
!= 0)
1067 detachComponentFromWindowRef (editorComp
, hostWindow
);
1072 filter
->editorBeingDeleted (editorComp
->getEditorComp());
1074 editorComp
= nullptr;
1076 // there's some kind of component currently modal, but the host
1077 // is trying to delete our plugin. You should try to avoid this happening..
1078 jassert (Component::getCurrentlyModalComponent() == nullptr);
1085 recursionCheck
= false;
1088 VstIntPtr
dispatcher (VstInt32 opCode
, VstInt32 index
, VstIntPtr value
, void* ptr
, float opt
)
1093 if (opCode
== effEditIdle
)
1098 else if (opCode
== effEditOpen
)
1100 checkWhetherMessageThreadIsCorrect();
1101 const MessageManagerLock mmLock
;
1102 jassert (! recursionCheck
);
1104 startTimer (1000 / 4); // performs misc housekeeping chores
1106 deleteEditor (true);
1109 if (editorComp
!= nullptr)
1111 editorComp
->setOpaque (true);
1112 editorComp
->setVisible (false);
1115 editorComp
->addToDesktop (0, ptr
);
1116 hostWindow
= (HWND
) ptr
;
1118 editorComp
->addToDesktop (0);
1119 hostWindow
= (Window
) ptr
;
1120 Window editorWnd
= (Window
) editorComp
->getWindowHandle();
1121 XReparentWindow (display
, editorWnd
, hostWindow
, 0, 0);
1123 hostWindow
= attachComponentToWindowRef (editorComp
, (WindowRef
) ptr
);
1125 editorComp
->setVisible (true);
1130 else if (opCode
== effEditClose
)
1132 checkWhetherMessageThreadIsCorrect();
1133 const MessageManagerLock mmLock
;
1134 deleteEditor (true);
1137 else if (opCode
== effEditGetRect
)
1139 checkWhetherMessageThreadIsCorrect();
1140 const MessageManagerLock mmLock
;
1143 if (editorComp
!= nullptr)
1145 editorSize
.left
= 0;
1147 editorSize
.right
= (VstInt16
) editorComp
->getWidth();
1148 editorSize
.bottom
= (VstInt16
) editorComp
->getHeight();
1150 *((ERect
**) ptr
) = &editorSize
;
1152 return (VstIntPtr
) (pointer_sized_int
) &editorSize
;
1160 return AudioEffectX::dispatcher (opCode
, index
, value
, ptr
, opt
);
1163 void resizeHostWindow (int newWidth
, int newHeight
)
1165 if (editorComp
!= nullptr)
1167 if (! (canHostDo (const_cast <char*> ("sizeWindow")) && sizeWindow (newWidth
, newHeight
)))
1169 // some hosts don't support the sizeWindow call, so do it manually..
1171 setNativeHostWindowSize (hostWindow
, editorComp
, newWidth
, newHeight
, getHostType());
1174 // (Currently, all linux hosts support sizeWindow, so this should never need to happen)
1175 editorComp
->setSize (newWidth
, newHeight
);
1180 const int frameThickness
= GetSystemMetrics (SM_CYFIXEDFRAME
);
1182 HWND w
= (HWND
) editorComp
->getWindowHandle();
1186 HWND parent
= GetParent (w
);
1191 TCHAR windowType
[32] = { 0 };
1192 GetClassName (parent
, windowType
, 31);
1194 if (String (windowType
).equalsIgnoreCase ("MDIClient"))
1197 RECT windowPos
, parentPos
;
1198 GetWindowRect (w
, &windowPos
);
1199 GetWindowRect (parent
, &parentPos
);
1201 SetWindowPos (w
, 0, 0, 0, newWidth
+ dw
, newHeight
+ dh
,
1202 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOOWNERZORDER
);
1204 dw
= (parentPos
.right
- parentPos
.left
) - (windowPos
.right
- windowPos
.left
);
1205 dh
= (parentPos
.bottom
- parentPos
.top
) - (windowPos
.bottom
- windowPos
.top
);
1209 if (dw
== 2 * frameThickness
)
1212 if (dw
> 100 || dh
> 100)
1217 SetWindowPos (w
, 0, 0, 0, newWidth
+ dw
, newHeight
+ dh
,
1218 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOOWNERZORDER
);
1222 if (editorComp
->getPeer() != nullptr)
1223 editorComp
->getPeer()->handleMovedOrResized();
1227 static PluginHostType
& getHostType()
1229 static PluginHostType hostType
;
1233 //==============================================================================
1234 // A component to hold the AudioProcessorEditor, and cope with some housekeeping
1235 // chores when it changes or repaints.
1236 class EditorCompWrapper
: public Component
,
1240 EditorCompWrapper (JuceVSTWrapper
& wrapper_
, AudioProcessorEditor
* editor
)
1241 : wrapper (wrapper_
)
1244 editor
->setOpaque (true);
1246 setBounds (editor
->getBounds());
1247 editor
->setTopLeftPosition (0, 0);
1248 addAndMakeVisible (editor
);
1251 if (! getHostType().isReceptor())
1252 addMouseListener (this, true);
1254 registerMouseWheelHook();
1258 ~EditorCompWrapper()
1261 unregisterMouseWheelHook();
1264 deleteAllChildren(); // note that we can't use a ScopedPointer because the editor may
1265 // have been transferred to another parent which takes over ownership.
1268 void paint (Graphics
&) {}
1270 void paintOverChildren (Graphics
&)
1272 // this causes an async call to masterIdle() to help
1273 // creaky old DAWs like Nuendo repaint themselves while we're
1274 // repainting. Otherwise they just seem to give up and sit there
1276 triggerAsyncUpdate();
1280 bool keyPressed (const KeyPress
& kp
)
1282 // If we have an unused keypress, move the key-focus to a host window
1283 // and re-inject the event..
1284 forwardCurrentKeyEventToHost (this);
1289 AudioProcessorEditor
* getEditorComp() const
1291 return dynamic_cast <AudioProcessorEditor
*> (getChildComponent (0));
1296 Component
* const editor
= getChildComponent(0);
1298 if (editor
!= nullptr)
1299 editor
->setBounds (getLocalBounds());
1302 void childBoundsChanged (Component
* child
)
1304 child
->setTopLeftPosition (0, 0);
1306 const int cw
= child
->getWidth();
1307 const int ch
= child
->getHeight();
1309 wrapper
.resizeHostWindow (cw
, ch
);
1311 #if ! JUCE_LINUX // setSize() on linux causes renoise and energyxt to fail.
1314 XResizeWindow (display
, (Window
) getWindowHandle(), cw
, ch
);
1318 wrapper
.resizeHostWindow (cw
, ch
); // (doing this a second time seems to be necessary in tracktion)
1322 void handleAsyncUpdate()
1324 wrapper
.tryMasterIdle();
1328 void mouseDown (const MouseEvent
&)
1333 void broughtToFront()
1335 // for hosts like nuendo, need to also pop the MDI container to the
1336 // front when our comp is clicked on.
1337 HWND parent
= findMDIParentOf ((HWND
) getWindowHandle());
1340 SetWindowPos (parent
, HWND_TOP
, 0, 0, 0, 0, SWP_NOMOVE
| SWP_NOSIZE
);
1345 //==============================================================================
1346 JuceVSTWrapper
& wrapper
;
1347 FakeMouseMoveGenerator fakeMouseGenerator
;
1349 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorCompWrapper
);
1352 //==============================================================================
1354 AudioProcessor
* filter
;
1355 JUCE_NAMESPACE::MemoryBlock chunkMemory
;
1356 JUCE_NAMESPACE::uint32 chunkMemoryTime
;
1357 ScopedPointer
<EditorCompWrapper
> editorComp
;
1359 MidiBuffer midiEvents
;
1360 VSTMidiEventList outgoingEvents
;
1361 VstSpeakerArrangementType speakerIn
, speakerOut
;
1362 int numInChans
, numOutChans
;
1363 bool isProcessing
, hasShutdown
, firstProcessCallback
, shouldDeleteEditor
;
1364 HeapBlock
<float*> channels
;
1365 Array
<float*> tempChannels
; // see note in processReplacing()
1375 //==============================================================================
1377 // Workarounds for Wavelab's happy-go-lucky use of threads.
1378 static void checkWhetherMessageThreadIsCorrect()
1380 if (getHostType().isWavelab() || getHostType().isCubaseBridged())
1382 static bool messageThreadIsDefinitelyCorrect
= false;
1384 if (! messageThreadIsDefinitelyCorrect
)
1386 MessageManager::getInstance()->setCurrentThreadAsMessageThread();
1388 class MessageThreadCallback
: public CallbackMessage
1391 MessageThreadCallback (bool& triggered_
) : triggered (triggered_
) {}
1393 void messageCallback()
1402 (new MessageThreadCallback (messageThreadIsDefinitelyCorrect
))->post();
1407 static void checkWhetherMessageThreadIsCorrect() {}
1410 //==============================================================================
1411 void deleteTempChannels()
1413 for (int i
= tempChannels
.size(); --i
>= 0;)
1414 delete[] (tempChannels
.getUnchecked(i
));
1416 tempChannels
.clear();
1418 if (filter
!= nullptr)
1419 tempChannels
.insertMultiple (0, 0, filter
->getNumInputChannels() + filter
->getNumOutputChannels());
1422 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVSTWrapper
);
1425 //==============================================================================
1426 /** Somewhere in the codebase of your plugin, you need to implement this function
1427 and make it create an instance of the filter subclass that you're building.
1429 extern AudioProcessor
* JUCE_CALLTYPE
createPluginFilter();
1432 //==============================================================================
1435 AEffect
* pluginEntryPoint (audioMasterCallback audioMaster
)
1437 JUCE_AUTORELEASEPOOL
1438 initialiseJuce_GUI();
1442 if (audioMaster (0, audioMasterVersion
, 0, 0, 0, 0) != 0)
1445 MessageManagerLock mmLock
;
1448 AudioProcessor
* const filter
= createPluginFilter();
1450 if (filter
!= nullptr)
1452 JuceVSTWrapper
* const wrapper
= new JuceVSTWrapper (audioMaster
, filter
);
1453 return wrapper
->getAeffect();
1464 //==============================================================================
1465 // Mac startup code..
1468 extern "C" __attribute__ ((visibility("default"))) AEffect
* VSTPluginMain (audioMasterCallback audioMaster
)
1471 return pluginEntryPoint (audioMaster
);
1474 extern "C" __attribute__ ((visibility("default"))) AEffect
* main_macho (audioMasterCallback audioMaster
)
1477 return pluginEntryPoint (audioMaster
);
1480 //==============================================================================
1481 // Linux startup code..
1484 extern "C" __attribute__ ((visibility("default"))) AEffect
* VSTPluginMain (audioMasterCallback audioMaster
)
1486 SharedMessageThread::getInstance();
1487 return pluginEntryPoint (audioMaster
);
1490 extern "C" __attribute__ ((visibility("default"))) AEffect
* main_plugin (audioMasterCallback audioMaster
) asm ("main");
1492 extern "C" __attribute__ ((visibility("default"))) AEffect
* main_plugin (audioMasterCallback audioMaster
)
1494 return VSTPluginMain (audioMaster
);
1497 // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash!
1498 __attribute__((constructor
)) void myPluginInit() {}
1499 __attribute__((destructor
)) void myPluginFini() {}
1501 //==============================================================================
1502 // Win32 startup code..
1505 extern "C" __declspec (dllexport
) AEffect
* VSTPluginMain (audioMasterCallback audioMaster
)
1507 return pluginEntryPoint (audioMaster
);
1510 #ifndef _WIN64 // (can't compile this on win64, but it's not needed anyway with VST2.4)
1511 extern "C" __declspec (dllexport
) void* main (audioMasterCallback audioMaster
)
1513 return (void*) pluginEntryPoint (audioMaster
);
1517 #if JucePlugin_Build_RTAS
1518 BOOL WINAPI
DllMainVST (HINSTANCE instance
, DWORD dwReason
, LPVOID
)
1520 extern "C" BOOL WINAPI
DllMain (HINSTANCE instance
, DWORD dwReason
, LPVOID
)
1523 if (dwReason
== DLL_PROCESS_ATTACH
)
1524 PlatformUtilities::setCurrentModuleInstanceHandle (instance
);