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 ==============================================================================
27 #pragma warning (disable : 4996 4100)
34 #include <X11/Xutil.h>
35 #include <X11/Xatom.h>
38 #include <Carbon/Carbon.h>
41 #ifdef PRAGMA_ALIGN_SUPPORTED
42 #undef PRAGMA_ALIGN_SUPPORTED
43 #define PRAGMA_ALIGN_SUPPORTED 1
46 #include "../juce_IncludeCharacteristics.h"
48 #if JucePlugin_Build_VST || 1
50 //==============================================================================
51 /* These files come with the Steinberg VST SDK - to get them, you'll need to
52 visit the Steinberg website and jump through some hoops to sign up as a
55 Then, you'll need to make sure your include path contains your "vstsdk2.3" or
56 "vstsdk2.4" directory.
58 Note that the JUCE_USE_VSTSDK_2_4 macro should be defined in JucePluginCharacteristics.h
60 #if JUCE_USE_VSTSDK_2_4
65 // VSTSDK V2.4 includes..
66 #include <public.sdk/source/vst2.x/audioeffectx.h>
67 #include <public.sdk/source/vst2.x/aeffeditor.h>
68 #include <public.sdk/source/vst2.x/audioeffectx.cpp>
69 #include <public.sdk/source/vst2.x/audioeffect.cpp>
71 #if ! VST_2_4_EXTENSIONS
72 #error "You're probably trying to include the wrong VSTSDK version - make sure your include path matches the JUCE_USE_VSTSDK_2_4 flag"
76 // VSTSDK V2.3 includes..
77 #include <source/common/audioeffectx.h>
78 #include <source/common/AEffEditor.hpp>
79 #include <source/common/audioeffectx.cpp>
80 #include <source/common/AudioEffect.cpp>
82 #if (! VST_2_3_EXTENSIONS) || VST_2_4_EXTENSIONS
83 #error "You're probably trying to include the wrong VSTSDK version - make sure your include path matches the JUCE_USE_VSTSDK_2_4 flag"
86 #define __aeffect__ // (needed for juce_VSTMidiEventList.h to work)
88 typedef long VstInt32
;
89 typedef long VstIntPtr
;
91 enum Vst2StringConstants
95 kVstMaxShortLabelLen
= 8,
96 kVstMaxCategLabelLen
= 24,
97 kVstMaxFileNameLen
= 100
100 enum VstSmpteFrameRate
102 kVstSmpte24fps
= 0, // 24 fps
103 kVstSmpte25fps
= 1, // 25 fps
104 kVstSmpte2997fps
= 2, // 29.97 fps
105 kVstSmpte30fps
= 3, // 30 fps
106 kVstSmpte2997dfps
= 4, // 29.97 drop
107 kVstSmpte30dfps
= 5, // 30 drop
108 kVstSmpteFilm16mm
= 6, // Film 16mm
109 kVstSmpteFilm35mm
= 7, // Film 35mm
110 kVstSmpte239fps
= 10, // HDTV: 23.976 fps
111 kVstSmpte249fps
= 11, // HDTV: 24.976 fps
112 kVstSmpte599fps
= 12, // HDTV: 59.94 fps
113 kVstSmpte60fps
= 13 // HDTV: 60 fps
116 struct VstMidiSysexEvent
118 VstInt32 type
; // #kVstSysexType
119 VstInt32 byteSize
; // sizeof (VstMidiSysexEvent)
120 VstInt32 deltaFrames
; // sample frames related to the current block start sample position
121 VstInt32 flags
; // none defined yet (should be zero)
122 VstInt32 dumpBytes
; // byte size of sysexDump
123 VstIntPtr resvd1
; // zero (Reserved for future use)
124 char* sysexDump
; // sysex dump
125 VstIntPtr resvd2
; // zero (Reserved for future use)
128 typedef int VstSpeakerArrangementType
;
131 //==============================================================================
133 #pragma pack (push, 8)
136 #include "../juce_PluginHeaders.h"
137 #include "../juce_PluginHostType.h"
145 class JuceVSTWrapper
;
146 static bool recursionCheck
= false;
147 static JUCE_NAMESPACE::uint32 lastMasterIdleCall
= 0;
150 extern void juce_callAnyTimersSynchronously();
153 extern void initialiseMac();
154 extern void* attachComponentToWindowRef (Component
* component
, void* windowRef
);
155 extern void detachComponentFromWindowRef (Component
* component
, void* nsWindow
);
156 extern void setNativeHostWindowSize (void* nsWindow
, Component
* editorComp
, int newWidth
, int newHeight
, const PluginHostType
& host
);
157 extern void checkWindowVisibility (void* nsWindow
, Component
* component
);
158 extern void forwardCurrentKeyEventToHost (Component
* component
);
162 extern Display
* display
;
167 //==============================================================================
172 HWND
findMDIParentOf (HWND w
)
174 const int frameThickness
= GetSystemMetrics (SM_CYFIXEDFRAME
);
178 HWND parent
= GetParent (w
);
183 TCHAR windowType
[32] = { 0 };
184 GetClassName (parent
, windowType
, 31);
186 if (String (windowType
).equalsIgnoreCase ("MDIClient"))
189 RECT windowPos
, parentPos
;
190 GetWindowRect (w
, &windowPos
);
191 GetWindowRect (parent
, &parentPos
);
193 const int dw
= (parentPos
.right
- parentPos
.left
) - (windowPos
.right
- windowPos
.left
);
194 const int dh
= (parentPos
.bottom
- parentPos
.top
) - (windowPos
.bottom
- windowPos
.top
);
196 if (dw
> 100 || dh
> 100)
201 if (dw
== 2 * frameThickness
)
208 //==============================================================================
209 static HHOOK mouseWheelHook
= 0;
210 static int mouseHookUsers
= 0;
212 LRESULT CALLBACK
mouseWheelHookCallback (int nCode
, WPARAM wParam
, LPARAM lParam
)
214 if (nCode
>= 0 && wParam
== WM_MOUSEWHEEL
)
216 const MOUSEHOOKSTRUCTEX
& hs
= *(MOUSEHOOKSTRUCTEX
*) lParam
;
218 Component
* const comp
= Desktop::getInstance().findComponentAt (Point
<int> (hs
.pt
.x
,
220 if (comp
!= nullptr && comp
->getWindowHandle() != 0)
221 return PostMessage ((HWND
) comp
->getWindowHandle(), WM_MOUSEWHEEL
,
222 hs
.mouseData
& 0xffff0000, (hs
.pt
.x
& 0xffff) | (hs
.pt
.y
<< 16));
225 return CallNextHookEx (mouseWheelHook
, nCode
, wParam
, lParam
);
228 void registerMouseWheelHook()
230 if (mouseHookUsers
++ == 0)
231 mouseWheelHook
= SetWindowsHookEx (WH_MOUSE
, mouseWheelHookCallback
,
232 (HINSTANCE
) PlatformUtilities::getCurrentModuleInstanceHandle(),
233 GetCurrentThreadId());
236 void unregisterMouseWheelHook()
238 if (--mouseHookUsers
== 0 && mouseWheelHook
!= 0)
240 UnhookWindowsHookEx (mouseWheelHook
);
246 //==============================================================================
249 class SharedMessageThread
: public Thread
252 SharedMessageThread()
253 : Thread ("VstMessageThread"),
258 while (! initialised
)
262 ~SharedMessageThread()
264 signalThreadShouldExit();
265 JUCEApplication::quit();
266 waitForThreadToExit (5000);
267 clearSingletonInstance();
272 initialiseJuce_GUI();
275 MessageManager::getInstance()->setCurrentThreadAsMessageThread();
277 while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250))
281 juce_DeclareSingleton (SharedMessageThread
, false);
287 juce_ImplementSingleton (SharedMessageThread
)
291 static Array
<void*> activePlugins
;
293 //==============================================================================
295 This is an AudioEffectX object that holds and wraps our AudioProcessor...
297 class JuceVSTWrapper
: public AudioEffectX
,
299 public AudioProcessorListener
,
303 //==============================================================================
304 JuceVSTWrapper (audioMasterCallback audioMaster
, AudioProcessor
* const filter_
)
305 : AudioEffectX (audioMaster
, filter_
->getNumPrograms(), filter_
->getNumParameters()),
308 speakerIn (kSpeakerArrEmpty
),
309 speakerOut (kSpeakerArrEmpty
),
310 numInChans (JucePlugin_MaxNumInputChannels
),
311 numOutChans (JucePlugin_MaxNumOutputChannels
),
312 isProcessing (false),
314 firstProcessCallback (true),
315 shouldDeleteEditor (false),
318 filter
->setPlayConfigDetails (numInChans
, numOutChans
, 0, 0);
319 filter
->setPlayHead (this);
320 filter
->addListener (this);
322 cEffect
.flags
|= effFlagsHasEditor
;
323 cEffect
.version
= (long) (JucePlugin_VersionCode
);
325 setUniqueID ((int) (JucePlugin_VSTUniqueID
));
327 #if JucePlugin_WantsMidiInput && ! JUCE_USE_VSTSDK_2_4
331 setNumInputs (numInChans
);
332 setNumOutputs (numOutChans
);
334 canProcessReplacing (true);
336 #if ! JUCE_USE_VSTSDK_2_4
341 isSynth ((JucePlugin_IsSynth
) != 0);
342 noTail (((JucePlugin_SilenceInProducesSilenceOut
) != 0) && (JucePlugin_TailLengthSeconds
<= 0));
343 setInitialDelay (filter
->getLatencySamples());
344 programsAreChunks (true);
346 activePlugins
.add (this);
355 MessageManagerLock mmLock
;
358 deleteEditor (false);
365 jassert (editorComp
== 0);
368 deleteTempChannels();
370 jassert (activePlugins
.contains (this));
371 activePlugins
.removeValue (this);
374 if (activePlugins
.size() == 0)
377 SharedMessageThread::deleteInstance();
385 // Note: most hosts call this on the UI thread, but wavelab doesn't, so be careful in here.
386 if (filter
->hasEditor())
387 cEffect
.flags
|= effFlagsHasEditor
;
389 cEffect
.flags
&= ~effFlagsHasEditor
;
394 // Note: most hosts call this on the UI thread, but wavelab doesn't, so be careful in here.
397 if (MessageManager::getInstance()->isThisTheMessageThread())
398 deleteEditor (false);
401 //==============================================================================
402 bool getEffectName (char* name
)
404 String (JucePlugin_Name
).copyToUTF8 (name
, 64);
408 bool getVendorString (char* text
)
410 String (JucePlugin_Manufacturer
).copyToUTF8 (text
, 64);
414 bool getProductString (char* text
) { return getEffectName (text
); }
415 VstInt32
getVendorVersion() { return JucePlugin_VersionCode
; }
416 VstPlugCategory
getPlugCategory() { return JucePlugin_VSTCategory
; }
417 bool keysRequired() { return (JucePlugin_EditorRequiresKeyboardFocus
) != 0; }
419 VstInt32
canDo (char* text
)
423 if (strcmp (text
, "receiveVstEvents") == 0
424 || strcmp (text
, "receiveVstMidiEvent") == 0
425 || strcmp (text
, "receiveVstMidiEvents") == 0)
427 #if JucePlugin_WantsMidiInput
433 else if (strcmp (text
, "sendVstEvents") == 0
434 || strcmp (text
, "sendVstMidiEvent") == 0
435 || strcmp (text
, "sendVstMidiEvents") == 0)
437 #if JucePlugin_ProducesMidiOutput
443 else if (strcmp (text
, "receiveVstTimeInfo") == 0
444 || strcmp (text
, "conformsToWindowRules") == 0)
448 else if (strcmp (text
, "openCloseAnyThread") == 0)
450 // This tells Wavelab to use the UI thread to invoke open/close,
451 // like all other hosts do.
458 bool getInputProperties (VstInt32 index
, VstPinProperties
* properties
)
460 if (filter
== nullptr || index
>= JucePlugin_MaxNumInputChannels
)
463 setPinProperties (*properties
, filter
->getInputChannelName ((int) index
),
464 speakerIn
, filter
->isInputChannelStereoPair ((int) index
));
468 bool getOutputProperties (VstInt32 index
, VstPinProperties
* properties
)
470 if (filter
== nullptr || index
>= JucePlugin_MaxNumOutputChannels
)
473 setPinProperties (*properties
, filter
->getOutputChannelName ((int) index
),
474 speakerOut
, filter
->isOutputChannelStereoPair ((int) index
));
478 static void setPinProperties (VstPinProperties
& properties
, const String
& name
,
479 VstSpeakerArrangementType type
, const bool isPair
)
481 name
.copyToUTF8 (properties
.label
, kVstMaxLabelLen
- 1);
482 name
.copyToUTF8 (properties
.shortLabel
, kVstMaxShortLabelLen
- 1);
484 if (type
!= kSpeakerArrEmpty
)
486 properties
.flags
= kVstPinUseSpeaker
;
487 properties
.arrangementType
= type
;
491 properties
.flags
= kVstPinIsActive
;
492 properties
.arrangementType
= 0;
495 properties
.flags
|= kVstPinIsStereo
;
499 //==============================================================================
500 VstInt32
processEvents (VstEvents
* events
)
502 #if JucePlugin_WantsMidiInput
503 VSTMidiEventList::addEventsToMidiBuffer (events
, midiEvents
);
510 void process (float** inputs
, float** outputs
, VstInt32 numSamples
)
512 const int numIn
= numInChans
;
513 const int numOut
= numOutChans
;
515 AudioSampleBuffer
temp (numIn
, numSamples
);
517 for (i
= numIn
; --i
>= 0;)
518 memcpy (temp
.getSampleData (i
), outputs
[i
], sizeof (float) * numSamples
);
520 processReplacing (inputs
, outputs
, numSamples
);
522 AudioSampleBuffer
dest (outputs
, numOut
, numSamples
);
524 for (i
= jmin (numIn
, numOut
); --i
>= 0;)
525 dest
.addFrom (i
, 0, temp
, i
, 0, numSamples
);
528 void processReplacing (float** inputs
, float** outputs
, VstInt32 numSamples
)
530 if (firstProcessCallback
)
532 firstProcessCallback
= false;
534 // if this fails, the host hasn't called resume() before processing
535 jassert (isProcessing
);
537 // (tragically, some hosts actually need this, although it's stupid to have
542 filter
->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */);
545 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
546 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST
)
547 filter
->setNonRealtime (true);
551 #if JUCE_DEBUG && ! JucePlugin_ProducesMidiOutput
552 const int numMidiEventsComingIn
= midiEvents
.getNumEvents();
555 jassert (activePlugins
.contains (this));
558 const ScopedLock
sl (filter
->getCallbackLock());
560 const int numIn
= numInChans
;
561 const int numOut
= numOutChans
;
563 if (filter
->isSuspended())
565 for (int i
= 0; i
< numOut
; ++i
)
566 zeromem (outputs
[i
], sizeof (float) * numSamples
);
571 for (i
= 0; i
< numOut
; ++i
)
573 float* chan
= tempChannels
.getUnchecked(i
);
579 // if some output channels are disabled, some hosts supply the same buffer
580 // for multiple channels - this buggers up our method of copying the
581 // inputs over the outputs, so we need to create unique temp buffers in this case..
582 for (int j
= i
; --j
>= 0;)
584 if (outputs
[j
] == chan
)
586 chan
= new float [blockSize
* 2];
587 tempChannels
.set (i
, chan
);
593 if (i
< numIn
&& chan
!= inputs
[i
])
594 memcpy (chan
, inputs
[i
], sizeof (float) * numSamples
);
599 for (; i
< numIn
; ++i
)
600 channels
[i
] = inputs
[i
];
602 AudioSampleBuffer
chans (channels
, jmax (numIn
, numOut
), numSamples
);
604 filter
->processBlock (chans
, midiEvents
);
608 if (! midiEvents
.isEmpty())
610 #if JucePlugin_ProducesMidiOutput
611 const int numEvents
= midiEvents
.getNumEvents();
613 outgoingEvents
.ensureSize (numEvents
);
614 outgoingEvents
.clear();
616 const JUCE_NAMESPACE::uint8
* midiEventData
;
617 int midiEventSize
, midiEventPosition
;
618 MidiBuffer::Iterator
i (midiEvents
);
620 while (i
.getNextEvent (midiEventData
, midiEventSize
, midiEventPosition
))
622 jassert (midiEventPosition
>= 0 && midiEventPosition
< numSamples
);
624 outgoingEvents
.addEvent (midiEventData
, midiEventSize
, midiEventPosition
);
627 sendVstEventsToHost (outgoingEvents
.events
);
629 /* This assertion is caused when you've added some events to the
630 midiMessages array in your processBlock() method, which usually means
631 that you're trying to send them somewhere. But in this case they're
634 If your plugin does want to send midi messages, you'll need to set
635 the JucePlugin_ProducesMidiOutput macro to 1 in your
636 JucePluginCharacteristics.h file.
638 If you don't want to produce any midi output, then you should clear the
639 midiMessages array at the end of your processBlock() method, to
640 indicate that you don't want any of the events to be passed through
643 jassert (midiEvents
.getNumEvents() <= numMidiEventsComingIn
);
650 //==============================================================================
651 VstInt32
startProcess() { return 0; }
652 VstInt32
stopProcess() { return 0; }
656 if (filter
!= nullptr)
659 channels
.calloc (numInChans
+ numOutChans
);
661 double rate
= getSampleRate();
666 const int blockSize
= getBlockSize();
667 jassert (blockSize
> 0);
669 firstProcessCallback
= true;
671 filter
->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */);
672 filter
->setPlayConfigDetails (numInChans
, numOutChans
, rate
, blockSize
);
674 deleteTempChannels();
676 filter
->prepareToPlay (rate
, blockSize
);
678 midiEvents
.ensureSize (2048);
681 setInitialDelay (filter
->getLatencySamples());
683 AudioEffectX::resume();
685 #if JucePlugin_ProducesMidiOutput
686 outgoingEvents
.ensureSize (512);
689 #if JucePlugin_WantsMidiInput && ! JUCE_USE_VSTSDK_2_4
697 if (filter
!= nullptr)
699 AudioEffectX::suspend();
701 filter
->releaseResources();
702 outgoingEvents
.freeEvents();
704 isProcessing
= false;
707 deleteTempChannels();
711 bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo
& info
)
713 const VstTimeInfo
* const ti
= getTimeInfo (kVstPpqPosValid
| kVstTempoValid
| kVstBarsValid
//| kVstCyclePosValid
714 | kVstTimeSigValid
| kVstSmpteValid
| kVstClockValid
);
716 if (ti
== nullptr || ti
->sampleRate
<= 0)
719 info
.bpm
= (ti
->flags
& kVstTempoValid
) != 0 ? ti
->tempo
: 0.0;
721 if ((ti
->flags
& kVstTimeSigValid
) != 0)
723 info
.timeSigNumerator
= ti
->timeSigNumerator
;
724 info
.timeSigDenominator
= ti
->timeSigDenominator
;
728 info
.timeSigNumerator
= 4;
729 info
.timeSigDenominator
= 4;
732 info
.timeInSeconds
= ti
->samplePos
/ ti
->sampleRate
;
733 info
.ppqPosition
= (ti
->flags
& kVstPpqPosValid
) != 0 ? ti
->ppqPos
: 0.0;
734 info
.ppqPositionOfLastBarStart
= (ti
->flags
& kVstBarsValid
) != 0 ? ti
->barStartPos
: 0.0;
736 if ((ti
->flags
& kVstSmpteValid
) != 0)
738 AudioPlayHead::FrameRateType rate
= AudioPlayHead::fpsUnknown
;
741 switch (ti
->smpteFrameRate
)
743 case kVstSmpte24fps
: rate
= AudioPlayHead::fps24
; fps
= 24.0; break;
744 case kVstSmpte25fps
: rate
= AudioPlayHead::fps25
; fps
= 25.0; break;
745 case kVstSmpte2997fps
: rate
= AudioPlayHead::fps2997
; fps
= 29.97; break;
746 case kVstSmpte30fps
: rate
= AudioPlayHead::fps30
; fps
= 30.0; break;
747 case kVstSmpte2997dfps
: rate
= AudioPlayHead::fps2997drop
; fps
= 29.97; break;
748 case kVstSmpte30dfps
: rate
= AudioPlayHead::fps30drop
; fps
= 30.0; break;
750 case kVstSmpteFilm16mm
:
751 case kVstSmpteFilm35mm
: fps
= 24.0; break;
753 case kVstSmpte239fps
: fps
= 23.976; break;
754 case kVstSmpte249fps
: fps
= 24.976; break;
755 case kVstSmpte599fps
: fps
= 59.94; break;
756 case kVstSmpte60fps
: fps
= 60; break;
758 default: jassertfalse
; // unknown frame-rate..
761 info
.frameRate
= rate
;
762 info
.editOriginTime
= ti
->smpteOffset
/ (80.0 * fps
);
766 info
.frameRate
= AudioPlayHead::fpsUnknown
;
767 info
.editOriginTime
= 0;
770 info
.isRecording
= (ti
->flags
& kVstTransportRecording
) != 0;
771 info
.isPlaying
= (ti
->flags
& kVstTransportPlaying
) != 0 || info
.isRecording
;
776 //==============================================================================
777 VstInt32
getProgram()
779 return filter
!= nullptr ? filter
->getCurrentProgram() : 0;
782 void setProgram (VstInt32 program
)
784 if (filter
!= nullptr)
785 filter
->setCurrentProgram (program
);
788 void setProgramName (char* name
)
790 if (filter
!= nullptr)
791 filter
->changeProgramName (filter
->getCurrentProgram(), name
);
794 void getProgramName (char* name
)
796 if (filter
!= nullptr)
797 filter
->getProgramName (filter
->getCurrentProgram()).copyToUTF8 (name
, 24);
800 bool getProgramNameIndexed (VstInt32
/*category*/, VstInt32 index
, char* text
)
802 if (filter
!= nullptr && isPositiveAndBelow (index
, filter
->getNumPrograms()))
804 filter
->getProgramName (index
).copyToUTF8 (text
, 24);
811 //==============================================================================
812 float getParameter (VstInt32 index
)
814 if (filter
== nullptr)
817 jassert (isPositiveAndBelow (index
, filter
->getNumParameters()));
818 return filter
->getParameter (index
);
821 void setParameter (VstInt32 index
, float value
)
823 if (filter
!= nullptr)
825 jassert (isPositiveAndBelow (index
, filter
->getNumParameters()));
826 filter
->setParameter (index
, value
);
830 void getParameterDisplay (VstInt32 index
, char* text
)
832 if (filter
!= nullptr)
834 jassert (isPositiveAndBelow (index
, filter
->getNumParameters()));
835 filter
->getParameterText (index
).copyToUTF8 (text
, 24); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more.
839 void getParameterName (VstInt32 index
, char* text
)
841 if (filter
!= nullptr)
843 jassert (isPositiveAndBelow (index
, filter
->getNumParameters()));
844 filter
->getParameterName (index
).copyToUTF8 (text
, 16); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more.
848 void audioProcessorParameterChanged (AudioProcessor
*, int index
, float newValue
)
850 setParameterAutomated (index
, newValue
);
853 void audioProcessorParameterChangeGestureBegin (AudioProcessor
*, int index
) { beginEdit (index
); }
854 void audioProcessorParameterChangeGestureEnd (AudioProcessor
*, int index
) { endEdit (index
); }
856 void audioProcessorChanged (AudioProcessor
*)
861 bool canParameterBeAutomated (VstInt32 index
)
863 return filter
!= nullptr && filter
->isParameterAutomatable ((int) index
);
866 class ChannelConfigComparator
869 static int compareElements (const short* const first
, const short* const second
) noexcept
871 if (first
[0] < second
[0]) return -1;
872 else if (first
[0] > second
[0]) return 1;
873 else if (first
[1] < second
[1]) return -1;
874 else if (first
[1] > second
[1]) return 1;
880 bool setSpeakerArrangement (VstSpeakerArrangement
* pluginInput
,
881 VstSpeakerArrangement
* pluginOutput
)
883 short channelConfigs
[][2] = { JucePlugin_PreferredChannelConfigurations
};
885 Array
<short*> channelConfigsSorted
;
886 ChannelConfigComparator comp
;
888 for (int i
= 0; i
< numElementsInArray (channelConfigs
); ++i
)
889 channelConfigsSorted
.addSorted (comp
, channelConfigs
[i
]);
891 for (int i
= channelConfigsSorted
.size(); --i
>= 0;)
893 const short* const config
= channelConfigsSorted
.getUnchecked(i
);
894 bool inCountMatches
= (config
[0] == pluginInput
->numChannels
);
895 bool outCountMatches
= (config
[1] == pluginOutput
->numChannels
);
897 if (inCountMatches
&& outCountMatches
)
899 speakerIn
= (VstSpeakerArrangementType
) pluginInput
->type
;
900 speakerOut
= (VstSpeakerArrangementType
) pluginOutput
->type
;
901 numInChans
= pluginInput
->numChannels
;
902 numOutChans
= pluginOutput
->numChannels
;
904 filter
->setPlayConfigDetails (numInChans
, numOutChans
,
905 filter
->getSampleRate(),
906 filter
->getBlockSize());
914 //==============================================================================
915 VstInt32
getChunk (void** data
, bool onlyStoreCurrentProgramData
)
917 if (filter
== nullptr)
920 chunkMemory
.setSize (0);
921 if (onlyStoreCurrentProgramData
)
922 filter
->getCurrentProgramStateInformation (chunkMemory
);
924 filter
->getStateInformation (chunkMemory
);
926 *data
= (void*) chunkMemory
.getData();
928 // because the chunk is only needed temporarily by the host (or at least you'd
929 // hope so) we'll give it a while and then free it in the timer callback.
930 chunkMemoryTime
= JUCE_NAMESPACE::Time::getApproximateMillisecondCounter();
932 return (VstInt32
) chunkMemory
.getSize();
935 VstInt32
setChunk (void* data
, VstInt32 byteSize
, bool onlyRestoreCurrentProgramData
)
937 if (filter
== nullptr)
940 chunkMemory
.setSize (0);
943 if (byteSize
> 0 && data
!= nullptr)
945 if (onlyRestoreCurrentProgramData
)
946 filter
->setCurrentProgramStateInformation (data
, byteSize
);
948 filter
->setStateInformation (data
, byteSize
);
956 if (shouldDeleteEditor
)
958 shouldDeleteEditor
= false;
962 if (chunkMemoryTime
> 0
963 && chunkMemoryTime
< JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000
967 chunkMemory
.setSize (0);
972 checkWindowVisibility (hostWindow
, editorComp
);
980 if (Component::isMouseButtonDownAnywhere() && ! recursionCheck
)
982 const JUCE_NAMESPACE::uint32 now
= JUCE_NAMESPACE::Time::getMillisecondCounter();
984 if (now
> lastMasterIdleCall
+ 20 && editorComp
!= nullptr)
986 lastMasterIdleCall
= now
;
988 recursionCheck
= true;
990 recursionCheck
= false;
995 void doIdleCallback()
997 // (wavelab calls this on a separate thread and causes a deadlock)..
998 if (MessageManager::getInstance()->isThisTheMessageThread()
1001 recursionCheck
= true;
1003 JUCE_AUTORELEASEPOOL
1004 juce_callAnyTimersSynchronously();
1006 for (int i
= ComponentPeer::getNumPeers(); --i
>= 0;)
1007 ComponentPeer::getPeer (i
)->performAnyPendingRepaintsNow();
1009 recursionCheck
= false;
1013 void createEditorComp()
1015 if (hasShutdown
|| filter
== nullptr)
1018 if (editorComp
== nullptr)
1020 AudioProcessorEditor
* const ed
= filter
->createEditorIfNeeded();
1024 cEffect
.flags
|= effFlagsHasEditor
;
1025 ed
->setOpaque (true);
1026 ed
->setVisible (true);
1028 editorComp
= new EditorCompWrapper (*this, ed
);
1032 cEffect
.flags
&= ~effFlagsHasEditor
;
1036 shouldDeleteEditor
= false;
1039 void deleteEditor (bool canDeleteLaterIfModal
)
1041 JUCE_AUTORELEASEPOOL
1042 PopupMenu::dismissAllActiveMenus();
1044 jassert (! recursionCheck
);
1045 recursionCheck
= true;
1047 if (editorComp
!= nullptr)
1049 Component
* const modalComponent
= Component::getCurrentlyModalComponent();
1050 if (modalComponent
!= nullptr)
1052 modalComponent
->exitModalState (0);
1054 if (canDeleteLaterIfModal
)
1056 shouldDeleteEditor
= true;
1057 recursionCheck
= false;
1063 if (hostWindow
!= 0)
1065 detachComponentFromWindowRef (editorComp
, hostWindow
);
1070 filter
->editorBeingDeleted (editorComp
->getEditorComp());
1072 editorComp
= nullptr;
1074 // there's some kind of component currently modal, but the host
1075 // is trying to delete our plugin. You should try to avoid this happening..
1076 jassert (Component::getCurrentlyModalComponent() == nullptr);
1083 recursionCheck
= false;
1086 VstIntPtr
dispatcher (VstInt32 opCode
, VstInt32 index
, VstIntPtr value
, void* ptr
, float opt
)
1091 if (opCode
== effEditIdle
)
1096 else if (opCode
== effEditOpen
)
1098 checkWhetherMessageThreadIsCorrect();
1099 const MessageManagerLock mmLock
;
1100 jassert (! recursionCheck
);
1102 startTimer (1000 / 4); // performs misc housekeeping chores
1104 deleteEditor (true);
1107 if (editorComp
!= nullptr)
1109 editorComp
->setOpaque (true);
1110 editorComp
->setVisible (false);
1113 editorComp
->addToDesktop (0, ptr
);
1114 hostWindow
= (HWND
) ptr
;
1116 editorComp
->addToDesktop (0);
1117 hostWindow
= (Window
) ptr
;
1118 Window editorWnd
= (Window
) editorComp
->getWindowHandle();
1119 XReparentWindow (display
, editorWnd
, hostWindow
, 0, 0);
1121 hostWindow
= attachComponentToWindowRef (editorComp
, (WindowRef
) ptr
);
1123 editorComp
->setVisible (true);
1128 else if (opCode
== effEditClose
)
1130 checkWhetherMessageThreadIsCorrect();
1131 const MessageManagerLock mmLock
;
1132 deleteEditor (true);
1135 else if (opCode
== effEditGetRect
)
1137 checkWhetherMessageThreadIsCorrect();
1138 const MessageManagerLock mmLock
;
1141 if (editorComp
!= nullptr)
1143 editorSize
.left
= 0;
1145 editorSize
.right
= (VstInt16
) editorComp
->getWidth();
1146 editorSize
.bottom
= (VstInt16
) editorComp
->getHeight();
1148 *((ERect
**) ptr
) = &editorSize
;
1150 return (VstIntPtr
) (pointer_sized_int
) &editorSize
;
1158 return AudioEffectX::dispatcher (opCode
, index
, value
, ptr
, opt
);
1161 void resizeHostWindow (int newWidth
, int newHeight
)
1163 if (editorComp
!= nullptr)
1165 if (! (canHostDo (const_cast <char*> ("sizeWindow")) && sizeWindow (newWidth
, newHeight
)))
1167 // some hosts don't support the sizeWindow call, so do it manually..
1169 setNativeHostWindowSize (hostWindow
, editorComp
, newWidth
, newHeight
, getHostType());
1172 // (Currently, all linux hosts support sizeWindow, so this should never need to happen)
1173 editorComp
->setSize (newWidth
, newHeight
);
1178 const int frameThickness
= GetSystemMetrics (SM_CYFIXEDFRAME
);
1180 HWND w
= (HWND
) editorComp
->getWindowHandle();
1184 HWND parent
= GetParent (w
);
1189 TCHAR windowType
[32] = { 0 };
1190 GetClassName (parent
, windowType
, 31);
1192 if (String (windowType
).equalsIgnoreCase ("MDIClient"))
1195 RECT windowPos
, parentPos
;
1196 GetWindowRect (w
, &windowPos
);
1197 GetWindowRect (parent
, &parentPos
);
1199 SetWindowPos (w
, 0, 0, 0, newWidth
+ dw
, newHeight
+ dh
,
1200 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOOWNERZORDER
);
1202 dw
= (parentPos
.right
- parentPos
.left
) - (windowPos
.right
- windowPos
.left
);
1203 dh
= (parentPos
.bottom
- parentPos
.top
) - (windowPos
.bottom
- windowPos
.top
);
1207 if (dw
== 2 * frameThickness
)
1210 if (dw
> 100 || dh
> 100)
1215 SetWindowPos (w
, 0, 0, 0, newWidth
+ dw
, newHeight
+ dh
,
1216 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOOWNERZORDER
);
1220 if (editorComp
->getPeer() != nullptr)
1221 editorComp
->getPeer()->handleMovedOrResized();
1225 static PluginHostType
& getHostType()
1227 static PluginHostType hostType
;
1231 //==============================================================================
1232 // A component to hold the AudioProcessorEditor, and cope with some housekeeping
1233 // chores when it changes or repaints.
1234 class EditorCompWrapper
: public Component
,
1238 EditorCompWrapper (JuceVSTWrapper
& wrapper_
, AudioProcessorEditor
* editor
)
1239 : wrapper (wrapper_
)
1242 editor
->setOpaque (true);
1244 setBounds (editor
->getBounds());
1245 editor
->setTopLeftPosition (0, 0);
1246 addAndMakeVisible (editor
);
1249 if (! getHostType().isReceptor())
1250 addMouseListener (this, true);
1252 registerMouseWheelHook();
1256 ~EditorCompWrapper()
1259 unregisterMouseWheelHook();
1262 deleteAllChildren(); // note that we can't use a ScopedPointer because the editor may
1263 // have been transferred to another parent which takes over ownership.
1266 void paint (Graphics
&) {}
1268 void paintOverChildren (Graphics
&)
1270 // this causes an async call to masterIdle() to help
1271 // creaky old DAWs like Nuendo repaint themselves while we're
1272 // repainting. Otherwise they just seem to give up and sit there
1274 triggerAsyncUpdate();
1278 bool keyPressed (const KeyPress
& kp
)
1280 // If we have an unused keypress, move the key-focus to a host window
1281 // and re-inject the event..
1282 forwardCurrentKeyEventToHost (this);
1287 AudioProcessorEditor
* getEditorComp() const
1289 return dynamic_cast <AudioProcessorEditor
*> (getChildComponent (0));
1294 Component
* const editor
= getChildComponent(0);
1296 if (editor
!= nullptr)
1297 editor
->setBounds (getLocalBounds());
1300 void childBoundsChanged (Component
* child
)
1302 child
->setTopLeftPosition (0, 0);
1304 const int cw
= child
->getWidth();
1305 const int ch
= child
->getHeight();
1307 wrapper
.resizeHostWindow (cw
, ch
);
1309 #if ! JUCE_LINUX // setSize() on linux causes renoise and energyxt to fail.
1312 XResizeWindow (display
, (Window
) getWindowHandle(), cw
, ch
);
1316 wrapper
.resizeHostWindow (cw
, ch
); // (doing this a second time seems to be necessary in tracktion)
1320 void handleAsyncUpdate()
1322 wrapper
.tryMasterIdle();
1326 void mouseDown (const MouseEvent
&)
1331 void broughtToFront()
1333 // for hosts like nuendo, need to also pop the MDI container to the
1334 // front when our comp is clicked on.
1335 HWND parent
= findMDIParentOf ((HWND
) getWindowHandle());
1338 SetWindowPos (parent
, HWND_TOP
, 0, 0, 0, 0, SWP_NOMOVE
| SWP_NOSIZE
);
1343 //==============================================================================
1344 JuceVSTWrapper
& wrapper
;
1345 FakeMouseMoveGenerator fakeMouseGenerator
;
1347 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorCompWrapper
);
1350 //==============================================================================
1352 AudioProcessor
* filter
;
1353 JUCE_NAMESPACE::MemoryBlock chunkMemory
;
1354 JUCE_NAMESPACE::uint32 chunkMemoryTime
;
1355 ScopedPointer
<EditorCompWrapper
> editorComp
;
1357 MidiBuffer midiEvents
;
1358 VSTMidiEventList outgoingEvents
;
1359 VstSpeakerArrangementType speakerIn
, speakerOut
;
1360 int numInChans
, numOutChans
;
1361 bool isProcessing
, hasShutdown
, firstProcessCallback
, shouldDeleteEditor
;
1362 HeapBlock
<float*> channels
;
1363 Array
<float*> tempChannels
; // see note in processReplacing()
1373 //==============================================================================
1375 // Workarounds for Wavelab's happy-go-lucky use of threads.
1376 static void checkWhetherMessageThreadIsCorrect()
1378 if (getHostType().isWavelab() || getHostType().isCubaseBridged())
1380 static bool messageThreadIsDefinitelyCorrect
= false;
1382 if (! messageThreadIsDefinitelyCorrect
)
1384 MessageManager::getInstance()->setCurrentThreadAsMessageThread();
1386 class MessageThreadCallback
: public CallbackMessage
1389 MessageThreadCallback (bool& triggered_
) : triggered (triggered_
) {}
1391 void messageCallback()
1400 (new MessageThreadCallback (messageThreadIsDefinitelyCorrect
))->post();
1405 static void checkWhetherMessageThreadIsCorrect() {}
1408 //==============================================================================
1409 void deleteTempChannels()
1411 for (int i
= tempChannels
.size(); --i
>= 0;)
1412 delete[] (tempChannels
.getUnchecked(i
));
1414 tempChannels
.clear();
1416 if (filter
!= nullptr)
1417 tempChannels
.insertMultiple (0, 0, filter
->getNumInputChannels() + filter
->getNumOutputChannels());
1420 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVSTWrapper
);
1423 //==============================================================================
1424 /** Somewhere in the codebase of your plugin, you need to implement this function
1425 and make it create an instance of the filter subclass that you're building.
1427 extern AudioProcessor
* JUCE_CALLTYPE
createPluginFilter();
1430 //==============================================================================
1433 AEffect
* pluginEntryPoint (audioMasterCallback audioMaster
)
1435 JUCE_AUTORELEASEPOOL
1436 initialiseJuce_GUI();
1440 if (audioMaster (0, audioMasterVersion
, 0, 0, 0, 0) != 0)
1443 MessageManagerLock mmLock
;
1446 AudioProcessor
* const filter
= createPluginFilter();
1448 if (filter
!= nullptr)
1450 JuceVSTWrapper
* const wrapper
= new JuceVSTWrapper (audioMaster
, filter
);
1451 return wrapper
->getAeffect();
1462 //==============================================================================
1463 // Mac startup code..
1466 extern "C" __attribute__ ((visibility("default"))) AEffect
* VSTPluginMain (audioMasterCallback audioMaster
)
1469 return pluginEntryPoint (audioMaster
);
1472 extern "C" __attribute__ ((visibility("default"))) AEffect
* main_macho (audioMasterCallback audioMaster
)
1475 return pluginEntryPoint (audioMaster
);
1478 //==============================================================================
1479 // Linux startup code..
1482 extern "C" __attribute__ ((visibility("default"))) AEffect
* VSTPluginMain (audioMasterCallback audioMaster
)
1484 SharedMessageThread::getInstance();
1485 return pluginEntryPoint (audioMaster
);
1488 extern "C" __attribute__ ((visibility("default"))) AEffect
* main_plugin (audioMasterCallback audioMaster
) asm ("main");
1490 extern "C" __attribute__ ((visibility("default"))) AEffect
* main_plugin (audioMasterCallback audioMaster
)
1492 return VSTPluginMain (audioMaster
);
1495 // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash!
1496 __attribute__((constructor
)) void myPluginInit() {}
1497 __attribute__((destructor
)) void myPluginFini() {}
1499 //==============================================================================
1500 // Win32 startup code..
1503 extern "C" __declspec (dllexport
) AEffect
* VSTPluginMain (audioMasterCallback audioMaster
)
1505 return pluginEntryPoint (audioMaster
);
1508 #ifndef _WIN64 // (can't compile this on win64, but it's not needed anyway with VST2.4)
1509 extern "C" __declspec (dllexport
) void* main (audioMasterCallback audioMaster
)
1511 return (void*) pluginEntryPoint (audioMaster
);
1515 #if JucePlugin_Build_RTAS
1516 BOOL WINAPI
DllMainVST (HINSTANCE instance
, DWORD dwReason
, LPVOID
)
1518 extern "C" BOOL WINAPI
DllMain (HINSTANCE instance
, DWORD dwReason
, LPVOID
)
1521 if (dwReason
== DLL_PROCESS_ATTACH
)
1522 PlatformUtilities::setCurrentModuleInstanceHandle (instance
);