Add new stuff from VST wrapper; Basic processing works
[juce-lv2.git] / juce / source / src / audio / plugin_client / VST / juce_VST_Wrapper.cpp
blob50857b023aab5b833fdb47303b5b791252a80a04
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 #define LINUX 1
28 #ifdef _MSC_VER
29 #pragma warning (disable : 4996 4100)
30 #endif
32 #ifdef _WIN32
33 #include <windows.h>
34 #elif defined (LINUX)
35 #include <X11/Xlib.h>
36 #include <X11/Xutil.h>
37 #include <X11/Xatom.h>
38 #undef KeyPress
39 #else
40 #include <Carbon/Carbon.h>
41 #endif
43 #ifdef PRAGMA_ALIGN_SUPPORTED
44 #undef PRAGMA_ALIGN_SUPPORTED
45 #define PRAGMA_ALIGN_SUPPORTED 1
46 #endif
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
55 VST developer.
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
63 #ifdef __GNUC__
64 #define __cdecl
65 #endif
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"
75 #endif
77 #else
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"
86 #endif
88 #define __aeffect__ // (needed for juce_VSTMidiEventList.h to work)
90 typedef long VstInt32;
91 typedef long VstIntPtr;
93 enum Vst2StringConstants
95 kVstMaxNameLen = 64,
96 kVstMaxLabelLen = 64,
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;
131 #endif
133 //==============================================================================
134 #ifdef _MSC_VER
135 #pragma pack (push, 8)
136 #endif
138 #include "../juce_PluginHeaders.h"
139 #include "../juce_PluginHostType.h"
141 #ifdef _MSC_VER
142 #pragma pack (pop)
143 #endif
145 #undef MemoryBlock
147 class JuceVSTWrapper;
148 static bool recursionCheck = false;
149 static JUCE_NAMESPACE::uint32 lastMasterIdleCall = 0;
151 BEGIN_JUCE_NAMESPACE
152 extern void juce_callAnyTimersSynchronously();
154 #if JUCE_MAC
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);
161 #endif
163 #if JUCE_LINUX
164 extern Display* display;
165 #endif
166 END_JUCE_NAMESPACE
169 //==============================================================================
170 #if JUCE_WINDOWS
172 namespace
174 HWND findMDIParentOf (HWND w)
176 const int frameThickness = GetSystemMetrics (SM_CYFIXEDFRAME);
178 while (w != 0)
180 HWND parent = GetParent (w);
182 if (parent == 0)
183 break;
185 TCHAR windowType[32] = { 0 };
186 GetClassName (parent, windowType, 31);
188 if (String (windowType).equalsIgnoreCase ("MDIClient"))
189 return parent;
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)
199 break;
201 w = parent;
203 if (dw == 2 * frameThickness)
204 break;
207 return w;
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,
221 hs.pt.y));
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);
243 mouseWheelHook = 0;
248 //==============================================================================
249 #elif JUCE_LINUX
251 class SharedMessageThread : public Thread
253 public:
254 SharedMessageThread()
255 : Thread ("VstMessageThread"),
256 initialised (false)
258 startThread (7);
260 while (! initialised)
261 sleep (1);
264 ~SharedMessageThread()
266 signalThreadShouldExit();
267 JUCEApplication::quit();
268 waitForThreadToExit (5000);
269 clearSingletonInstance();
272 void run()
274 initialiseJuce_GUI();
275 initialised = true;
277 MessageManager::getInstance()->setCurrentThreadAsMessageThread();
279 while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250))
283 juce_DeclareSingleton (SharedMessageThread, false);
285 private:
286 bool initialised;
289 juce_ImplementSingleton (SharedMessageThread)
291 #endif
293 static Array<void*> activePlugins;
295 //==============================================================================
297 This is an AudioEffectX object that holds and wraps our AudioProcessor...
299 class JuceVSTWrapper : public AudioEffectX,
300 private Timer,
301 public AudioProcessorListener,
302 public AudioPlayHead
304 public:
305 //==============================================================================
306 JuceVSTWrapper (audioMasterCallback audioMaster, AudioProcessor* const filter_)
307 : AudioEffectX (audioMaster, filter_->getNumPrograms(), filter_->getNumParameters()),
308 filter (filter_),
309 chunkMemoryTime (0),
310 speakerIn (kSpeakerArrEmpty),
311 speakerOut (kSpeakerArrEmpty),
312 numInChans (JucePlugin_MaxNumInputChannels),
313 numOutChans (JucePlugin_MaxNumOutputChannels),
314 isProcessing (false),
315 hasShutdown (false),
316 firstProcessCallback (true),
317 shouldDeleteEditor (false),
318 hostWindow (0)
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
330 wantEvents();
331 #endif
333 setNumInputs (numInChans);
334 setNumOutputs (numOutChans);
336 canProcessReplacing (true);
338 #if ! JUCE_USE_VSTSDK_2_4
339 hasVu (false);
340 hasClip (false);
341 #endif
343 isSynth ((JucePlugin_IsSynth) != 0);
344 noTail (((JucePlugin_SilenceInProducesSilenceOut) != 0) && (JucePlugin_TailLengthSeconds <= 0));
345 setInitialDelay (filter->getLatencySamples());
346 programsAreChunks (true);
348 activePlugins.add (this);
351 ~JuceVSTWrapper()
353 JUCE_AUTORELEASEPOOL
356 #if JUCE_LINUX
357 MessageManagerLock mmLock;
358 #endif
359 stopTimer();
360 deleteEditor (false);
362 hasShutdown = true;
364 delete filter;
365 filter = 0;
367 jassert (editorComp == 0);
369 channels.free();
370 deleteTempChannels();
372 jassert (activePlugins.contains (this));
373 activePlugins.removeValue (this);
376 if (activePlugins.size() == 0)
378 #if JUCE_LINUX
379 SharedMessageThread::deleteInstance();
380 #endif
381 shutdownJuce_GUI();
385 void open()
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;
390 else
391 cEffect.flags &= ~effFlagsHasEditor;
394 void close()
396 // Note: most hosts call this on the UI thread, but wavelab doesn't, so be careful in here.
397 stopTimer();
399 if (MessageManager::getInstance()->isThisTheMessageThread())
400 deleteEditor (false);
403 //==============================================================================
404 bool getEffectName (char* name)
406 String (JucePlugin_Name).copyToUTF8 (name, 64);
407 return true;
410 bool getVendorString (char* text)
412 String (JucePlugin_Manufacturer).copyToUTF8 (text, 64);
413 return true;
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)
423 VstInt32 result = 0;
425 if (strcmp (text, "receiveVstEvents") == 0
426 || strcmp (text, "receiveVstMidiEvent") == 0
427 || strcmp (text, "receiveVstMidiEvents") == 0)
429 #if JucePlugin_WantsMidiInput
430 result = 1;
431 #else
432 result = -1;
433 #endif
435 else if (strcmp (text, "sendVstEvents") == 0
436 || strcmp (text, "sendVstMidiEvent") == 0
437 || strcmp (text, "sendVstMidiEvents") == 0)
439 #if JucePlugin_ProducesMidiOutput
440 result = 1;
441 #else
442 result = -1;
443 #endif
445 else if (strcmp (text, "receiveVstTimeInfo") == 0
446 || strcmp (text, "conformsToWindowRules") == 0)
448 result = 1;
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.
454 result = -1;
457 return result;
460 bool getInputProperties (VstInt32 index, VstPinProperties* properties)
462 if (filter == nullptr || index >= JucePlugin_MaxNumInputChannels)
463 return false;
465 setPinProperties (*properties, filter->getInputChannelName ((int) index),
466 speakerIn, filter->isInputChannelStereoPair ((int) index));
467 return true;
470 bool getOutputProperties (VstInt32 index, VstPinProperties* properties)
472 if (filter == nullptr || index >= JucePlugin_MaxNumOutputChannels)
473 return false;
475 setPinProperties (*properties, filter->getOutputChannelName ((int) index),
476 speakerOut, filter->isOutputChannelStereoPair ((int) index));
477 return true;
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;
491 else
493 properties.flags = kVstPinIsActive;
494 properties.arrangementType = 0;
496 if (isPair)
497 properties.flags |= kVstPinIsStereo;
501 //==============================================================================
502 VstInt32 processEvents (VstEvents* events)
504 #if JucePlugin_WantsMidiInput
505 VSTMidiEventList::addEventsToMidiBuffer (events, midiEvents);
506 return 1;
507 #else
508 return 0;
509 #endif
512 void process (float** inputs, float** outputs, VstInt32 numSamples)
514 const int numIn = numInChans;
515 const int numOut = numOutChans;
517 AudioSampleBuffer temp (numIn, numSamples);
518 int i;
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
540 // to do it here..)
541 if (! isProcessing)
542 resume();
544 filter->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */);
546 #if JUCE_WINDOWS
547 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
548 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST)
549 filter->setNonRealtime (true);
550 #endif
553 #if JUCE_DEBUG && ! JucePlugin_ProducesMidiOutput
554 const int numMidiEventsComingIn = midiEvents.getNumEvents();
555 #endif
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);
570 else
572 int i;
573 for (i = 0; i < numOut; ++i)
575 float* chan = tempChannels.getUnchecked(i);
577 if (chan == 0)
579 chan = outputs[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);
590 break;
595 if (i < numIn && chan != inputs[i])
596 memcpy (chan, inputs[i], sizeof (float) * numSamples);
598 channels[i] = chan;
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);
630 #else
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
634 getting thrown away.
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
643 to the output.
645 jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn);
646 #endif
648 midiEvents.clear();
652 //==============================================================================
653 VstInt32 startProcess() { return 0; }
654 VstInt32 stopProcess() { return 0; }
656 void resume()
658 if (filter != nullptr)
660 isProcessing = true;
661 channels.calloc (numInChans + numOutChans);
663 double rate = getSampleRate();
664 jassert (rate > 0);
665 if (rate <= 0.0)
666 rate = 44100.0;
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);
681 midiEvents.clear();
683 setInitialDelay (filter->getLatencySamples());
685 AudioEffectX::resume();
687 #if JucePlugin_ProducesMidiOutput
688 outgoingEvents.ensureSize (512);
689 #endif
691 #if JucePlugin_WantsMidiInput && ! JUCE_USE_VSTSDK_2_4
692 wantEvents();
693 #endif
697 void suspend()
699 if (filter != nullptr)
701 AudioEffectX::suspend();
703 filter->releaseResources();
704 outgoingEvents.freeEvents();
706 isProcessing = false;
707 channels.free();
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)
719 return false;
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;
728 else
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;
741 double fps = 1.0;
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);
766 else
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;
775 return true;
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);
807 return true;
810 return false;
813 //==============================================================================
814 float getParameter (VstInt32 index)
816 if (filter == nullptr)
817 return 0.0f;
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*)
860 updateDisplay();
863 bool canParameterBeAutomated (VstInt32 index)
865 return filter != nullptr && filter->isParameterAutomatable ((int) index);
868 class ChannelConfigComparator
870 public:
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;
878 return 0;
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());
909 return true;
913 return false;
916 //==============================================================================
917 VstInt32 getChunk (void** data, bool onlyStoreCurrentProgramData)
919 if (filter == nullptr)
920 return 0;
922 chunkMemory.setSize (0);
923 if (onlyStoreCurrentProgramData)
924 filter->getCurrentProgramStateInformation (chunkMemory);
925 else
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)
940 return 0;
942 chunkMemory.setSize (0);
943 chunkMemoryTime = 0;
945 if (byteSize > 0 && data != nullptr)
947 if (onlyRestoreCurrentProgramData)
948 filter->setCurrentProgramStateInformation (data, byteSize);
949 else
950 filter->setStateInformation (data, byteSize);
953 return 0;
956 void timerCallback()
958 if (shouldDeleteEditor)
960 shouldDeleteEditor = false;
961 deleteEditor (true);
964 if (chunkMemoryTime > 0
965 && chunkMemoryTime < JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000
966 && ! recursionCheck)
968 chunkMemoryTime = 0;
969 chunkMemory.setSize (0);
972 #if JUCE_MAC
973 if (hostWindow != 0)
974 checkWindowVisibility (hostWindow, editorComp);
975 #endif
977 tryMasterIdle();
980 void tryMasterIdle()
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;
991 masterIdle();
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)
1018 return;
1020 if (editorComp == nullptr)
1022 AudioProcessorEditor* const ed = filter->createEditorIfNeeded();
1024 if (ed != nullptr)
1026 cEffect.flags |= effFlagsHasEditor;
1027 ed->setOpaque (true);
1028 ed->setVisible (true);
1030 editorComp = new EditorCompWrapper (*this, ed);
1032 else
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;
1060 return;
1064 #if JUCE_MAC
1065 if (hostWindow != 0)
1067 detachComponentFromWindowRef (editorComp, hostWindow);
1068 hostWindow = 0;
1070 #endif
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);
1081 #if JUCE_LINUX
1082 hostWindow = 0;
1083 #endif
1085 recursionCheck = false;
1088 VstIntPtr dispatcher (VstInt32 opCode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
1090 if (hasShutdown)
1091 return 0;
1093 if (opCode == effEditIdle)
1095 doIdleCallback();
1096 return 0;
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);
1107 createEditorComp();
1109 if (editorComp != nullptr)
1111 editorComp->setOpaque (true);
1112 editorComp->setVisible (false);
1114 #if JUCE_WINDOWS
1115 editorComp->addToDesktop (0, ptr);
1116 hostWindow = (HWND) ptr;
1117 #elif JUCE_LINUX
1118 editorComp->addToDesktop (0);
1119 hostWindow = (Window) ptr;
1120 Window editorWnd = (Window) editorComp->getWindowHandle();
1121 XReparentWindow (display, editorWnd, hostWindow, 0, 0);
1122 #else
1123 hostWindow = attachComponentToWindowRef (editorComp, (WindowRef) ptr);
1124 #endif
1125 editorComp->setVisible (true);
1127 return 1;
1130 else if (opCode == effEditClose)
1132 checkWhetherMessageThreadIsCorrect();
1133 const MessageManagerLock mmLock;
1134 deleteEditor (true);
1135 return 0;
1137 else if (opCode == effEditGetRect)
1139 checkWhetherMessageThreadIsCorrect();
1140 const MessageManagerLock mmLock;
1141 createEditorComp();
1143 if (editorComp != nullptr)
1145 editorSize.left = 0;
1146 editorSize.top = 0;
1147 editorSize.right = (VstInt16) editorComp->getWidth();
1148 editorSize.bottom = (VstInt16) editorComp->getHeight();
1150 *((ERect**) ptr) = &editorSize;
1152 return (VstIntPtr) (pointer_sized_int) &editorSize;
1154 else
1156 return 0;
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..
1170 #if JUCE_MAC
1171 setNativeHostWindowSize (hostWindow, editorComp, newWidth, newHeight, getHostType());
1173 #elif JUCE_LINUX
1174 // (Currently, all linux hosts support sizeWindow, so this should never need to happen)
1175 editorComp->setSize (newWidth, newHeight);
1177 #else
1178 int dw = 0;
1179 int dh = 0;
1180 const int frameThickness = GetSystemMetrics (SM_CYFIXEDFRAME);
1182 HWND w = (HWND) editorComp->getWindowHandle();
1184 while (w != 0)
1186 HWND parent = GetParent (w);
1188 if (parent == 0)
1189 break;
1191 TCHAR windowType [32] = { 0 };
1192 GetClassName (parent, windowType, 31);
1194 if (String (windowType).equalsIgnoreCase ("MDIClient"))
1195 break;
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);
1207 w = parent;
1209 if (dw == 2 * frameThickness)
1210 break;
1212 if (dw > 100 || dh > 100)
1213 w = 0;
1216 if (w != 0)
1217 SetWindowPos (w, 0, 0, 0, newWidth + dw, newHeight + dh,
1218 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
1219 #endif
1222 if (editorComp->getPeer() != nullptr)
1223 editorComp->getPeer()->handleMovedOrResized();
1227 static PluginHostType& getHostType()
1229 static PluginHostType hostType;
1230 return 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,
1237 public AsyncUpdater
1239 public:
1240 EditorCompWrapper (JuceVSTWrapper& wrapper_, AudioProcessorEditor* editor)
1241 : wrapper (wrapper_)
1243 setOpaque (true);
1244 editor->setOpaque (true);
1246 setBounds (editor->getBounds());
1247 editor->setTopLeftPosition (0, 0);
1248 addAndMakeVisible (editor);
1250 #if JUCE_WINDOWS
1251 if (! getHostType().isReceptor())
1252 addMouseListener (this, true);
1254 registerMouseWheelHook();
1255 #endif
1258 ~EditorCompWrapper()
1260 #if JUCE_WINDOWS
1261 unregisterMouseWheelHook();
1262 #endif
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
1275 // waiting.
1276 triggerAsyncUpdate();
1279 #if JUCE_MAC
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);
1285 return true;
1287 #endif
1289 AudioProcessorEditor* getEditorComp() const
1291 return dynamic_cast <AudioProcessorEditor*> (getChildComponent (0));
1294 void resized()
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.
1312 setSize (cw, ch);
1313 #else
1314 XResizeWindow (display, (Window) getWindowHandle(), cw, ch);
1315 #endif
1317 #if JUCE_MAC
1318 wrapper.resizeHostWindow (cw, ch); // (doing this a second time seems to be necessary in tracktion)
1319 #endif
1322 void handleAsyncUpdate()
1324 wrapper.tryMasterIdle();
1327 #if JUCE_WINDOWS
1328 void mouseDown (const MouseEvent&)
1330 broughtToFront();
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());
1339 if (parent != 0)
1340 SetWindowPos (parent, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
1342 #endif
1344 private:
1345 //==============================================================================
1346 JuceVSTWrapper& wrapper;
1347 FakeMouseMoveGenerator fakeMouseGenerator;
1349 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorCompWrapper);
1352 //==============================================================================
1353 private:
1354 AudioProcessor* filter;
1355 JUCE_NAMESPACE::MemoryBlock chunkMemory;
1356 JUCE_NAMESPACE::uint32 chunkMemoryTime;
1357 ScopedPointer<EditorCompWrapper> editorComp;
1358 ERect editorSize;
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()
1367 #if JUCE_MAC
1368 void* hostWindow;
1369 #elif JUCE_LINUX
1370 Window hostWindow;
1371 #else
1372 HWND hostWindow;
1373 #endif
1375 //==============================================================================
1376 #if JUCE_WINDOWS
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
1390 public:
1391 MessageThreadCallback (bool& triggered_) : triggered (triggered_) {}
1393 void messageCallback()
1395 triggered = true;
1398 private:
1399 bool& triggered;
1402 (new MessageThreadCallback (messageThreadIsDefinitelyCorrect))->post();
1406 #else
1407 static void checkWhetherMessageThreadIsCorrect() {}
1408 #endif
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 //==============================================================================
1433 namespace
1435 AEffect* pluginEntryPoint (audioMasterCallback audioMaster)
1437 JUCE_AUTORELEASEPOOL
1438 initialiseJuce_GUI();
1442 if (audioMaster (0, audioMasterVersion, 0, 0, 0, 0) != 0)
1444 #if JUCE_LINUX
1445 MessageManagerLock mmLock;
1446 #endif
1448 AudioProcessor* const filter = createPluginFilter();
1450 if (filter != nullptr)
1452 JuceVSTWrapper* const wrapper = new JuceVSTWrapper (audioMaster, filter);
1453 return wrapper->getAeffect();
1457 catch (...)
1460 return nullptr;
1464 //==============================================================================
1465 // Mac startup code..
1466 #if JUCE_MAC
1468 extern "C" __attribute__ ((visibility("default"))) AEffect* VSTPluginMain (audioMasterCallback audioMaster)
1470 initialiseMac();
1471 return pluginEntryPoint (audioMaster);
1474 extern "C" __attribute__ ((visibility("default"))) AEffect* main_macho (audioMasterCallback audioMaster)
1476 initialiseMac();
1477 return pluginEntryPoint (audioMaster);
1480 //==============================================================================
1481 // Linux startup code..
1482 #elif JUCE_LINUX
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..
1503 #else
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);
1515 #endif
1517 #if JucePlugin_Build_RTAS
1518 BOOL WINAPI DllMainVST (HINSTANCE instance, DWORD dwReason, LPVOID)
1519 #else
1520 extern "C" BOOL WINAPI DllMain (HINSTANCE instance, DWORD dwReason, LPVOID)
1521 #endif
1523 if (dwReason == DLL_PROCESS_ATTACH)
1524 PlatformUtilities::setCurrentModuleInstanceHandle (instance);
1526 return TRUE;
1528 #endif
1530 #endif