Restart!
[juce-lv2.git] / juce / source / src / audio / plugin_client / VST / juce_VST_Wrapper.cpp
blob67d9a07275d89a5a354c12c97c494bdc7f73f7cd
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 #ifdef _MSC_VER
27 #pragma warning (disable : 4996 4100)
28 #endif
30 #ifdef _WIN32
31 #include <windows.h>
32 #elif defined (LINUX)
33 #include <X11/Xlib.h>
34 #include <X11/Xutil.h>
35 #include <X11/Xatom.h>
36 #undef KeyPress
37 #else
38 #include <Carbon/Carbon.h>
39 #endif
41 #ifdef PRAGMA_ALIGN_SUPPORTED
42 #undef PRAGMA_ALIGN_SUPPORTED
43 #define PRAGMA_ALIGN_SUPPORTED 1
44 #endif
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
53 VST developer.
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
61 #ifdef __GNUC__
62 #define __cdecl
63 #endif
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"
73 #endif
75 #else
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"
84 #endif
86 #define __aeffect__ // (needed for juce_VSTMidiEventList.h to work)
88 typedef long VstInt32;
89 typedef long VstIntPtr;
91 enum Vst2StringConstants
93 kVstMaxNameLen = 64,
94 kVstMaxLabelLen = 64,
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;
129 #endif
131 //==============================================================================
132 #ifdef _MSC_VER
133 #pragma pack (push, 8)
134 #endif
136 #include "../juce_PluginHeaders.h"
137 #include "../juce_PluginHostType.h"
139 #ifdef _MSC_VER
140 #pragma pack (pop)
141 #endif
143 #undef MemoryBlock
145 class JuceVSTWrapper;
146 static bool recursionCheck = false;
147 static JUCE_NAMESPACE::uint32 lastMasterIdleCall = 0;
149 BEGIN_JUCE_NAMESPACE
150 extern void juce_callAnyTimersSynchronously();
152 #if JUCE_MAC
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);
159 #endif
161 #if JUCE_LINUX
162 extern Display* display;
163 #endif
164 END_JUCE_NAMESPACE
167 //==============================================================================
168 #if JUCE_WINDOWS
170 namespace
172 HWND findMDIParentOf (HWND w)
174 const int frameThickness = GetSystemMetrics (SM_CYFIXEDFRAME);
176 while (w != 0)
178 HWND parent = GetParent (w);
180 if (parent == 0)
181 break;
183 TCHAR windowType[32] = { 0 };
184 GetClassName (parent, windowType, 31);
186 if (String (windowType).equalsIgnoreCase ("MDIClient"))
187 return parent;
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)
197 break;
199 w = parent;
201 if (dw == 2 * frameThickness)
202 break;
205 return w;
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,
219 hs.pt.y));
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);
241 mouseWheelHook = 0;
246 //==============================================================================
247 #elif JUCE_LINUX
249 class SharedMessageThread : public Thread
251 public:
252 SharedMessageThread()
253 : Thread ("VstMessageThread"),
254 initialised (false)
256 startThread (7);
258 while (! initialised)
259 sleep (1);
262 ~SharedMessageThread()
264 signalThreadShouldExit();
265 JUCEApplication::quit();
266 waitForThreadToExit (5000);
267 clearSingletonInstance();
270 void run()
272 initialiseJuce_GUI();
273 initialised = true;
275 MessageManager::getInstance()->setCurrentThreadAsMessageThread();
277 while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250))
281 juce_DeclareSingleton (SharedMessageThread, false);
283 private:
284 bool initialised;
287 juce_ImplementSingleton (SharedMessageThread)
289 #endif
291 static Array<void*> activePlugins;
293 //==============================================================================
295 This is an AudioEffectX object that holds and wraps our AudioProcessor...
297 class JuceVSTWrapper : public AudioEffectX,
298 private Timer,
299 public AudioProcessorListener,
300 public AudioPlayHead
302 public:
303 //==============================================================================
304 JuceVSTWrapper (audioMasterCallback audioMaster, AudioProcessor* const filter_)
305 : AudioEffectX (audioMaster, filter_->getNumPrograms(), filter_->getNumParameters()),
306 filter (filter_),
307 chunkMemoryTime (0),
308 speakerIn (kSpeakerArrEmpty),
309 speakerOut (kSpeakerArrEmpty),
310 numInChans (JucePlugin_MaxNumInputChannels),
311 numOutChans (JucePlugin_MaxNumOutputChannels),
312 isProcessing (false),
313 hasShutdown (false),
314 firstProcessCallback (true),
315 shouldDeleteEditor (false),
316 hostWindow (0)
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
328 wantEvents();
329 #endif
331 setNumInputs (numInChans);
332 setNumOutputs (numOutChans);
334 canProcessReplacing (true);
336 #if ! JUCE_USE_VSTSDK_2_4
337 hasVu (false);
338 hasClip (false);
339 #endif
341 isSynth ((JucePlugin_IsSynth) != 0);
342 noTail (((JucePlugin_SilenceInProducesSilenceOut) != 0) && (JucePlugin_TailLengthSeconds <= 0));
343 setInitialDelay (filter->getLatencySamples());
344 programsAreChunks (true);
346 activePlugins.add (this);
349 ~JuceVSTWrapper()
351 JUCE_AUTORELEASEPOOL
354 #if JUCE_LINUX
355 MessageManagerLock mmLock;
356 #endif
357 stopTimer();
358 deleteEditor (false);
360 hasShutdown = true;
362 delete filter;
363 filter = 0;
365 jassert (editorComp == 0);
367 channels.free();
368 deleteTempChannels();
370 jassert (activePlugins.contains (this));
371 activePlugins.removeValue (this);
374 if (activePlugins.size() == 0)
376 #if JUCE_LINUX
377 SharedMessageThread::deleteInstance();
378 #endif
379 shutdownJuce_GUI();
383 void open()
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;
388 else
389 cEffect.flags &= ~effFlagsHasEditor;
392 void close()
394 // Note: most hosts call this on the UI thread, but wavelab doesn't, so be careful in here.
395 stopTimer();
397 if (MessageManager::getInstance()->isThisTheMessageThread())
398 deleteEditor (false);
401 //==============================================================================
402 bool getEffectName (char* name)
404 String (JucePlugin_Name).copyToUTF8 (name, 64);
405 return true;
408 bool getVendorString (char* text)
410 String (JucePlugin_Manufacturer).copyToUTF8 (text, 64);
411 return true;
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)
421 VstInt32 result = 0;
423 if (strcmp (text, "receiveVstEvents") == 0
424 || strcmp (text, "receiveVstMidiEvent") == 0
425 || strcmp (text, "receiveVstMidiEvents") == 0)
427 #if JucePlugin_WantsMidiInput
428 result = 1;
429 #else
430 result = -1;
431 #endif
433 else if (strcmp (text, "sendVstEvents") == 0
434 || strcmp (text, "sendVstMidiEvent") == 0
435 || strcmp (text, "sendVstMidiEvents") == 0)
437 #if JucePlugin_ProducesMidiOutput
438 result = 1;
439 #else
440 result = -1;
441 #endif
443 else if (strcmp (text, "receiveVstTimeInfo") == 0
444 || strcmp (text, "conformsToWindowRules") == 0)
446 result = 1;
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.
452 result = -1;
455 return result;
458 bool getInputProperties (VstInt32 index, VstPinProperties* properties)
460 if (filter == nullptr || index >= JucePlugin_MaxNumInputChannels)
461 return false;
463 setPinProperties (*properties, filter->getInputChannelName ((int) index),
464 speakerIn, filter->isInputChannelStereoPair ((int) index));
465 return true;
468 bool getOutputProperties (VstInt32 index, VstPinProperties* properties)
470 if (filter == nullptr || index >= JucePlugin_MaxNumOutputChannels)
471 return false;
473 setPinProperties (*properties, filter->getOutputChannelName ((int) index),
474 speakerOut, filter->isOutputChannelStereoPair ((int) index));
475 return true;
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;
489 else
491 properties.flags = kVstPinIsActive;
492 properties.arrangementType = 0;
494 if (isPair)
495 properties.flags |= kVstPinIsStereo;
499 //==============================================================================
500 VstInt32 processEvents (VstEvents* events)
502 #if JucePlugin_WantsMidiInput
503 VSTMidiEventList::addEventsToMidiBuffer (events, midiEvents);
504 return 1;
505 #else
506 return 0;
507 #endif
510 void process (float** inputs, float** outputs, VstInt32 numSamples)
512 const int numIn = numInChans;
513 const int numOut = numOutChans;
515 AudioSampleBuffer temp (numIn, numSamples);
516 int i;
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
538 // to do it here..)
539 if (! isProcessing)
540 resume();
542 filter->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */);
544 #if JUCE_WINDOWS
545 if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
546 && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST)
547 filter->setNonRealtime (true);
548 #endif
551 #if JUCE_DEBUG && ! JucePlugin_ProducesMidiOutput
552 const int numMidiEventsComingIn = midiEvents.getNumEvents();
553 #endif
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);
568 else
570 int i;
571 for (i = 0; i < numOut; ++i)
573 float* chan = tempChannels.getUnchecked(i);
575 if (chan == 0)
577 chan = outputs[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);
588 break;
593 if (i < numIn && chan != inputs[i])
594 memcpy (chan, inputs[i], sizeof (float) * numSamples);
596 channels[i] = chan;
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);
628 #else
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
632 getting thrown away.
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
641 to the output.
643 jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn);
644 #endif
646 midiEvents.clear();
650 //==============================================================================
651 VstInt32 startProcess() { return 0; }
652 VstInt32 stopProcess() { return 0; }
654 void resume()
656 if (filter != nullptr)
658 isProcessing = true;
659 channels.calloc (numInChans + numOutChans);
661 double rate = getSampleRate();
662 jassert (rate > 0);
663 if (rate <= 0.0)
664 rate = 44100.0;
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);
679 midiEvents.clear();
681 setInitialDelay (filter->getLatencySamples());
683 AudioEffectX::resume();
685 #if JucePlugin_ProducesMidiOutput
686 outgoingEvents.ensureSize (512);
687 #endif
689 #if JucePlugin_WantsMidiInput && ! JUCE_USE_VSTSDK_2_4
690 wantEvents();
691 #endif
695 void suspend()
697 if (filter != nullptr)
699 AudioEffectX::suspend();
701 filter->releaseResources();
702 outgoingEvents.freeEvents();
704 isProcessing = false;
705 channels.free();
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)
717 return false;
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;
726 else
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;
739 double fps = 1.0;
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);
764 else
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;
773 return true;
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);
805 return true;
808 return false;
811 //==============================================================================
812 float getParameter (VstInt32 index)
814 if (filter == nullptr)
815 return 0.0f;
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*)
858 updateDisplay();
861 bool canParameterBeAutomated (VstInt32 index)
863 return filter != nullptr && filter->isParameterAutomatable ((int) index);
866 class ChannelConfigComparator
868 public:
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;
876 return 0;
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());
907 return true;
911 return false;
914 //==============================================================================
915 VstInt32 getChunk (void** data, bool onlyStoreCurrentProgramData)
917 if (filter == nullptr)
918 return 0;
920 chunkMemory.setSize (0);
921 if (onlyStoreCurrentProgramData)
922 filter->getCurrentProgramStateInformation (chunkMemory);
923 else
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)
938 return 0;
940 chunkMemory.setSize (0);
941 chunkMemoryTime = 0;
943 if (byteSize > 0 && data != nullptr)
945 if (onlyRestoreCurrentProgramData)
946 filter->setCurrentProgramStateInformation (data, byteSize);
947 else
948 filter->setStateInformation (data, byteSize);
951 return 0;
954 void timerCallback()
956 if (shouldDeleteEditor)
958 shouldDeleteEditor = false;
959 deleteEditor (true);
962 if (chunkMemoryTime > 0
963 && chunkMemoryTime < JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000
964 && ! recursionCheck)
966 chunkMemoryTime = 0;
967 chunkMemory.setSize (0);
970 #if JUCE_MAC
971 if (hostWindow != 0)
972 checkWindowVisibility (hostWindow, editorComp);
973 #endif
975 tryMasterIdle();
978 void tryMasterIdle()
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;
989 masterIdle();
990 recursionCheck = false;
995 void doIdleCallback()
997 // (wavelab calls this on a separate thread and causes a deadlock)..
998 if (MessageManager::getInstance()->isThisTheMessageThread()
999 && ! recursionCheck)
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)
1016 return;
1018 if (editorComp == nullptr)
1020 AudioProcessorEditor* const ed = filter->createEditorIfNeeded();
1022 if (ed != nullptr)
1024 cEffect.flags |= effFlagsHasEditor;
1025 ed->setOpaque (true);
1026 ed->setVisible (true);
1028 editorComp = new EditorCompWrapper (*this, ed);
1030 else
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;
1058 return;
1062 #if JUCE_MAC
1063 if (hostWindow != 0)
1065 detachComponentFromWindowRef (editorComp, hostWindow);
1066 hostWindow = 0;
1068 #endif
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);
1079 #if JUCE_LINUX
1080 hostWindow = 0;
1081 #endif
1083 recursionCheck = false;
1086 VstIntPtr dispatcher (VstInt32 opCode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
1088 if (hasShutdown)
1089 return 0;
1091 if (opCode == effEditIdle)
1093 doIdleCallback();
1094 return 0;
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);
1105 createEditorComp();
1107 if (editorComp != nullptr)
1109 editorComp->setOpaque (true);
1110 editorComp->setVisible (false);
1112 #if JUCE_WINDOWS
1113 editorComp->addToDesktop (0, ptr);
1114 hostWindow = (HWND) ptr;
1115 #elif JUCE_LINUX
1116 editorComp->addToDesktop (0);
1117 hostWindow = (Window) ptr;
1118 Window editorWnd = (Window) editorComp->getWindowHandle();
1119 XReparentWindow (display, editorWnd, hostWindow, 0, 0);
1120 #else
1121 hostWindow = attachComponentToWindowRef (editorComp, (WindowRef) ptr);
1122 #endif
1123 editorComp->setVisible (true);
1125 return 1;
1128 else if (opCode == effEditClose)
1130 checkWhetherMessageThreadIsCorrect();
1131 const MessageManagerLock mmLock;
1132 deleteEditor (true);
1133 return 0;
1135 else if (opCode == effEditGetRect)
1137 checkWhetherMessageThreadIsCorrect();
1138 const MessageManagerLock mmLock;
1139 createEditorComp();
1141 if (editorComp != nullptr)
1143 editorSize.left = 0;
1144 editorSize.top = 0;
1145 editorSize.right = (VstInt16) editorComp->getWidth();
1146 editorSize.bottom = (VstInt16) editorComp->getHeight();
1148 *((ERect**) ptr) = &editorSize;
1150 return (VstIntPtr) (pointer_sized_int) &editorSize;
1152 else
1154 return 0;
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..
1168 #if JUCE_MAC
1169 setNativeHostWindowSize (hostWindow, editorComp, newWidth, newHeight, getHostType());
1171 #elif JUCE_LINUX
1172 // (Currently, all linux hosts support sizeWindow, so this should never need to happen)
1173 editorComp->setSize (newWidth, newHeight);
1175 #else
1176 int dw = 0;
1177 int dh = 0;
1178 const int frameThickness = GetSystemMetrics (SM_CYFIXEDFRAME);
1180 HWND w = (HWND) editorComp->getWindowHandle();
1182 while (w != 0)
1184 HWND parent = GetParent (w);
1186 if (parent == 0)
1187 break;
1189 TCHAR windowType [32] = { 0 };
1190 GetClassName (parent, windowType, 31);
1192 if (String (windowType).equalsIgnoreCase ("MDIClient"))
1193 break;
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);
1205 w = parent;
1207 if (dw == 2 * frameThickness)
1208 break;
1210 if (dw > 100 || dh > 100)
1211 w = 0;
1214 if (w != 0)
1215 SetWindowPos (w, 0, 0, 0, newWidth + dw, newHeight + dh,
1216 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
1217 #endif
1220 if (editorComp->getPeer() != nullptr)
1221 editorComp->getPeer()->handleMovedOrResized();
1225 static PluginHostType& getHostType()
1227 static PluginHostType hostType;
1228 return 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,
1235 public AsyncUpdater
1237 public:
1238 EditorCompWrapper (JuceVSTWrapper& wrapper_, AudioProcessorEditor* editor)
1239 : wrapper (wrapper_)
1241 setOpaque (true);
1242 editor->setOpaque (true);
1244 setBounds (editor->getBounds());
1245 editor->setTopLeftPosition (0, 0);
1246 addAndMakeVisible (editor);
1248 #if JUCE_WINDOWS
1249 if (! getHostType().isReceptor())
1250 addMouseListener (this, true);
1252 registerMouseWheelHook();
1253 #endif
1256 ~EditorCompWrapper()
1258 #if JUCE_WINDOWS
1259 unregisterMouseWheelHook();
1260 #endif
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
1273 // waiting.
1274 triggerAsyncUpdate();
1277 #if JUCE_MAC
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);
1283 return true;
1285 #endif
1287 AudioProcessorEditor* getEditorComp() const
1289 return dynamic_cast <AudioProcessorEditor*> (getChildComponent (0));
1292 void resized()
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.
1310 setSize (cw, ch);
1311 #else
1312 XResizeWindow (display, (Window) getWindowHandle(), cw, ch);
1313 #endif
1315 #if JUCE_MAC
1316 wrapper.resizeHostWindow (cw, ch); // (doing this a second time seems to be necessary in tracktion)
1317 #endif
1320 void handleAsyncUpdate()
1322 wrapper.tryMasterIdle();
1325 #if JUCE_WINDOWS
1326 void mouseDown (const MouseEvent&)
1328 broughtToFront();
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());
1337 if (parent != 0)
1338 SetWindowPos (parent, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
1340 #endif
1342 private:
1343 //==============================================================================
1344 JuceVSTWrapper& wrapper;
1345 FakeMouseMoveGenerator fakeMouseGenerator;
1347 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorCompWrapper);
1350 //==============================================================================
1351 private:
1352 AudioProcessor* filter;
1353 JUCE_NAMESPACE::MemoryBlock chunkMemory;
1354 JUCE_NAMESPACE::uint32 chunkMemoryTime;
1355 ScopedPointer<EditorCompWrapper> editorComp;
1356 ERect editorSize;
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()
1365 #if JUCE_MAC
1366 void* hostWindow;
1367 #elif JUCE_LINUX
1368 Window hostWindow;
1369 #else
1370 HWND hostWindow;
1371 #endif
1373 //==============================================================================
1374 #if JUCE_WINDOWS
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
1388 public:
1389 MessageThreadCallback (bool& triggered_) : triggered (triggered_) {}
1391 void messageCallback()
1393 triggered = true;
1396 private:
1397 bool& triggered;
1400 (new MessageThreadCallback (messageThreadIsDefinitelyCorrect))->post();
1404 #else
1405 static void checkWhetherMessageThreadIsCorrect() {}
1406 #endif
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 //==============================================================================
1431 namespace
1433 AEffect* pluginEntryPoint (audioMasterCallback audioMaster)
1435 JUCE_AUTORELEASEPOOL
1436 initialiseJuce_GUI();
1440 if (audioMaster (0, audioMasterVersion, 0, 0, 0, 0) != 0)
1442 #if JUCE_LINUX
1443 MessageManagerLock mmLock;
1444 #endif
1446 AudioProcessor* const filter = createPluginFilter();
1448 if (filter != nullptr)
1450 JuceVSTWrapper* const wrapper = new JuceVSTWrapper (audioMaster, filter);
1451 return wrapper->getAeffect();
1455 catch (...)
1458 return nullptr;
1462 //==============================================================================
1463 // Mac startup code..
1464 #if JUCE_MAC
1466 extern "C" __attribute__ ((visibility("default"))) AEffect* VSTPluginMain (audioMasterCallback audioMaster)
1468 initialiseMac();
1469 return pluginEntryPoint (audioMaster);
1472 extern "C" __attribute__ ((visibility("default"))) AEffect* main_macho (audioMasterCallback audioMaster)
1474 initialiseMac();
1475 return pluginEntryPoint (audioMaster);
1478 //==============================================================================
1479 // Linux startup code..
1480 #elif JUCE_LINUX
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..
1501 #else
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);
1513 #endif
1515 #if JucePlugin_Build_RTAS
1516 BOOL WINAPI DllMainVST (HINSTANCE instance, DWORD dwReason, LPVOID)
1517 #else
1518 extern "C" BOOL WINAPI DllMain (HINSTANCE instance, DWORD dwReason, LPVOID)
1519 #endif
1521 if (dwReason == DLL_PROCESS_ATTACH)
1522 PlatformUtilities::setCurrentModuleInstanceHandle (instance);
1524 return TRUE;
1526 #endif
1528 #endif