From 73f17dba08aa2eb5b1e214de1f60d6d8bb519d4c Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Fri, 10 Sep 2010 22:47:38 +0100 Subject: [PATCH] Minor clean-ups. --- .../wrapper/VST/juce_VST_Wrapper.cpp | 3178 ++++++++--------- juce_amalgamated.h | 6 +- src/gui/components/juce_Component.h | 4 +- .../components/juce_ModalComponentManager.h | 2 + 4 files changed, 1597 insertions(+), 1593 deletions(-) diff --git a/extras/audio plugins/wrapper/VST/juce_VST_Wrapper.cpp b/extras/audio plugins/wrapper/VST/juce_VST_Wrapper.cpp index 2647cee2cd..ebb8b78ac5 100644 --- a/extras/audio plugins/wrapper/VST/juce_VST_Wrapper.cpp +++ b/extras/audio plugins/wrapper/VST/juce_VST_Wrapper.cpp @@ -1,1589 +1,1589 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-10 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -#ifdef _MSC_VER - #pragma warning (disable : 4996 4100) -#endif - -#ifdef _WIN32 - #include -#elif defined (LINUX) - #include - #include - #include - #undef KeyPress -#else - #include -#endif - -#ifdef PRAGMA_ALIGN_SUPPORTED - #undef PRAGMA_ALIGN_SUPPORTED - #define PRAGMA_ALIGN_SUPPORTED 1 -#endif - -#include "../juce_IncludeCharacteristics.h" - -#if JucePlugin_Build_VST - -//============================================================================== -/* These files come with the Steinberg VST SDK - to get them, you'll need to - visit the Steinberg website and jump through some hoops to sign up as a - VST developer. - - Then, you'll need to make sure your include path contains your "vstsdk2.3" or - "vstsdk2.4" directory. - - Note that the JUCE_USE_VSTSDK_2_4 macro should be defined in JucePluginCharacteristics.h -*/ -#if JUCE_USE_VSTSDK_2_4 - #ifdef __GNUC__ - #define __cdecl - #endif - - // VSTSDK V2.4 includes.. - #include "public.sdk/source/vst2.x/audioeffectx.h" - #include "public.sdk/source/vst2.x/aeffeditor.h" - #include "public.sdk/source/vst2.x/audioeffectx.cpp" - #include "public.sdk/source/vst2.x/audioeffect.cpp" - - #if ! VST_2_4_EXTENSIONS - #error // You're probably trying to include the wrong VSTSDK version - make sure your include path matches the JUCE_USE_VSTSDK_2_4 flag - #endif - -#else - // VSTSDK V2.3 includes.. - #include "source/common/audioeffectx.h" - #include "source/common/AEffEditor.hpp" - #include "source/common/audioeffectx.cpp" - #include "source/common/AudioEffect.cpp" - - #if (! VST_2_3_EXTENSIONS) || VST_2_4_EXTENSIONS - #error // You're probably trying to include the wrong VSTSDK version - make sure your include path matches the JUCE_USE_VSTSDK_2_4 flag - #endif - - #define __aeffect__ // (needed for juce_VSTMidiEventList.h to work) - - typedef long VstInt32; - typedef long VstIntPtr; - enum Vst2StringConstants - { - kVstMaxNameLen = 64, - kVstMaxLabelLen = 64, - kVstMaxShortLabelLen = 8, - kVstMaxCategLabelLen = 24, - kVstMaxFileNameLen = 100 - }; - - enum VstSmpteFrameRate - { - kVstSmpte24fps = 0, ///< 24 fps - kVstSmpte25fps = 1, ///< 25 fps - kVstSmpte2997fps = 2, ///< 29.97 fps - kVstSmpte30fps = 3, ///< 30 fps - kVstSmpte2997dfps = 4, ///< 29.97 drop - kVstSmpte30dfps = 5, ///< 30 drop - kVstSmpteFilm16mm = 6, ///< Film 16mm - kVstSmpteFilm35mm = 7, ///< Film 35mm - kVstSmpte239fps = 10, ///< HDTV: 23.976 fps - kVstSmpte249fps = 11, ///< HDTV: 24.976 fps - kVstSmpte599fps = 12, ///< HDTV: 59.94 fps - kVstSmpte60fps = 13 ///< HDTV: 60 fps - }; - - struct VstMidiSysexEvent - { - VstInt32 type; ///< #kVstSysexType - VstInt32 byteSize; ///< sizeof (VstMidiSysexEvent) - VstInt32 deltaFrames; ///< sample frames related to the current block start sample position - VstInt32 flags; ///< none defined yet (should be zero) - VstInt32 dumpBytes; ///< byte size of sysexDump - VstIntPtr resvd1; ///< zero (Reserved for future use) - char* sysexDump; ///< sysex dump - VstIntPtr resvd2; ///< zero (Reserved for future use) - }; - - typedef int VstSpeakerArrangementType; -#endif - -//============================================================================== -#ifdef _MSC_VER - #pragma pack (push, 8) -#endif - -#include "../juce_PluginHeaders.h" -#include "../juce_PluginHostType.h" - - -#ifdef _MSC_VER - #pragma pack (pop) -#endif - -#undef MemoryBlock - -class JuceVSTWrapper; -static bool recursionCheck = false; -static JUCE_NAMESPACE::uint32 lastMasterIdleCall = 0; - -BEGIN_JUCE_NAMESPACE - extern void juce_callAnyTimersSynchronously(); - - #if JUCE_MAC - extern void initialiseMac(); - extern void* attachComponentToWindowRef (Component* component, void* windowRef); - extern void detachComponentFromWindowRef (Component* component, void* nsWindow); - extern void setNativeHostWindowSize (void* nsWindow, Component* editorComp, int newWidth, int newHeight); - extern void checkWindowVisibility (void* nsWindow, Component* component); - extern void forwardCurrentKeyEventToHost (Component* component); - #endif - - #if JUCE_LINUX - extern Display* display; - #endif -END_JUCE_NAMESPACE - - -//============================================================================== -#if JUCE_WINDOWS - -static HWND findMDIParentOf (HWND w) -{ - const int frameThickness = GetSystemMetrics (SM_CYFIXEDFRAME); - - while (w != 0) - { - HWND parent = GetParent (w); - - if (parent == 0) - break; - - TCHAR windowType [32]; - zeromem (windowType, sizeof (windowType)); - GetClassName (parent, windowType, 31); - - if (String (windowType).equalsIgnoreCase ("MDIClient")) - { - w = parent; - break; - } - - RECT windowPos; - GetWindowRect (w, &windowPos); - - RECT parentPos; - GetWindowRect (parent, &parentPos); - - const int dw = (parentPos.right - parentPos.left) - (windowPos.right - windowPos.left); - const int dh = (parentPos.bottom - parentPos.top) - (windowPos.bottom - windowPos.top); - - if (dw > 100 || dh > 100) - break; - - w = parent; - - if (dw == 2 * frameThickness) - break; - } - - return w; -} - -//============================================================================== -#elif JUCE_LINUX - -class SharedMessageThread : public Thread -{ -public: - SharedMessageThread() - : Thread ("VstMessageThread"), - initialised (false) - { - startThread (7); - - while (! initialised) - sleep (1); - } - - ~SharedMessageThread() - { - signalThreadShouldExit(); - JUCEApplication::quit(); - waitForThreadToExit (5000); - clearSingletonInstance(); - } - - void run() - { - initialiseJuce_GUI(); - initialised = true; - - MessageManager::getInstance()->setCurrentThreadAsMessageThread(); - - while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250)) - { - } - } - - juce_DeclareSingleton (SharedMessageThread, false) - -private: - bool initialised; -}; - -juce_ImplementSingleton (SharedMessageThread) - -#endif - -static Array activePlugins; - -//============================================================================== -/** - This wraps an AudioProcessor as an AudioEffectX... -*/ -class JuceVSTWrapper : public AudioEffectX, - private Timer, - public AudioProcessorListener, - public AudioPlayHead -{ -public: - //============================================================================== - JuceVSTWrapper (audioMasterCallback audioMaster, - AudioProcessor* const filter_) - : AudioEffectX (audioMaster, - filter_->getNumPrograms(), - filter_->getNumParameters()), - filter (filter_) - { - chunkMemoryTime = 0; - isProcessing = false; - hasShutdown = false; - firstProcessCallback = true; - shouldDeleteEditor = false; - speakerIn = kSpeakerArrEmpty; - speakerOut = kSpeakerArrEmpty; - numInChans = JucePlugin_MaxNumInputChannels; - numOutChans = JucePlugin_MaxNumOutputChannels; - -#if JUCE_MAC || JUCE_LINUX - hostWindow = 0; -#endif - - filter->setPlayConfigDetails (numInChans, numOutChans, 0, 0); - - filter_->setPlayHead (this); - filter_->addListener (this); - - cEffect.flags |= effFlagsHasEditor; - cEffect.version = (long) (JucePlugin_VersionCode); - - setUniqueID ((int) (JucePlugin_VSTUniqueID)); - -#if JucePlugin_WantsMidiInput && ! JUCE_USE_VSTSDK_2_4 - wantEvents(); -#endif - - setNumInputs (numInChans); - setNumOutputs (numOutChans); - - canProcessReplacing (true); - -#if ! JUCE_USE_VSTSDK_2_4 - hasVu (false); - hasClip (false); -#endif - - isSynth ((JucePlugin_IsSynth) != 0); - noTail (((JucePlugin_SilenceInProducesSilenceOut) != 0) && (JucePlugin_TailLengthSeconds <= 0)); - setInitialDelay (filter->getLatencySamples()); - programsAreChunks (true); - - activePlugins.add (this); - } - - ~JuceVSTWrapper() - { - stopTimer(); - deleteEditor (false); - - hasShutdown = true; - - delete filter; - filter = 0; - - jassert (editorComp == 0); - - channels.free(); - deleteTempChannels(); - - jassert (activePlugins.contains (this)); - activePlugins.removeValue (this); - - if (activePlugins.size() == 0) - { -#if JUCE_LINUX - SharedMessageThread::deleteInstance(); -#endif - shutdownJuce_GUI(); - } - } - - void open() - { - if (editorComp == 0) - { - checkWhetherWavelabHasChangedThread(); - const MessageManagerLock mmLock; - - AudioProcessorEditor* const ed = filter->createEditorIfNeeded(); - - if (ed != 0) - cEffect.flags |= effFlagsHasEditor; - else - cEffect.flags &= ~effFlagsHasEditor; - - filter->editorBeingDeleted (ed); - delete ed; - } - - startTimer (1000 / 4); - } - - void close() - { - const NonWavelabMMLock mmLock; - jassert (! recursionCheck); - - stopTimer(); - deleteEditor (false); - } - - //============================================================================== - bool getEffectName (char* name) - { - String (JucePlugin_Name).copyToCString (name, 64); - return true; - } - - bool getVendorString (char* text) - { - String (JucePlugin_Manufacturer).copyToCString (text, 64); - return true; - } - - bool getProductString (char* text) - { - return getEffectName (text); - } - - VstInt32 getVendorVersion() - { - return JucePlugin_VersionCode; - } - - VstPlugCategory getPlugCategory() - { - return JucePlugin_VSTCategory; - } - - VstInt32 canDo (char* text) - { - VstInt32 result = 0; - - if (strcmp (text, "receiveVstEvents") == 0 - || strcmp (text, "receiveVstMidiEvent") == 0 - || strcmp (text, "receiveVstMidiEvents") == 0) - { -#if JucePlugin_WantsMidiInput - result = 1; -#else - result = -1; -#endif - } - else if (strcmp (text, "sendVstEvents") == 0 - || strcmp (text, "sendVstMidiEvent") == 0 - || strcmp (text, "sendVstMidiEvents") == 0) - { -#if JucePlugin_ProducesMidiOutput - result = 1; -#else - result = -1; -#endif - } - else if (strcmp (text, "receiveVstTimeInfo") == 0 - || strcmp (text, "conformsToWindowRules") == 0) - { - result = 1; - } - - return result; - } - - bool keysRequired() - { - return (JucePlugin_EditorRequiresKeyboardFocus) != 0; - } - - bool getInputProperties (VstInt32 index, VstPinProperties* properties) - { - if (filter == 0 || index >= JucePlugin_MaxNumInputChannels) - return false; - - const String name (filter->getInputChannelName ((int) index)); - - name.copyToCString (properties->label, kVstMaxLabelLen - 1); - name.copyToCString (properties->shortLabel, kVstMaxShortLabelLen - 1); - - if (speakerIn != kSpeakerArrEmpty) - { - properties->flags = kVstPinUseSpeaker; - properties->arrangementType = speakerIn; - } - else - { - properties->flags = kVstPinIsActive; - - if (filter->isInputChannelStereoPair ((int) index)) - properties->flags |= kVstPinIsStereo; - - properties->arrangementType = 0; - } - - return true; - } - - bool getOutputProperties (VstInt32 index, VstPinProperties* properties) - { - if (filter == 0 || index >= JucePlugin_MaxNumOutputChannels) - return false; - - const String name (filter->getOutputChannelName ((int) index)); - - name.copyToCString (properties->label, kVstMaxLabelLen - 1); - name.copyToCString (properties->shortLabel, kVstMaxShortLabelLen - 1); - - if (speakerOut != kSpeakerArrEmpty) - { - properties->flags = kVstPinUseSpeaker; - properties->arrangementType = speakerOut; - } - else - { - properties->flags = kVstPinIsActive; - - if (filter->isOutputChannelStereoPair ((int) index)) - properties->flags |= kVstPinIsStereo; - - properties->arrangementType = 0; - } - - return true; - } - - //============================================================================== - VstInt32 processEvents (VstEvents* events) - { -#if JucePlugin_WantsMidiInput - VSTMidiEventList::addEventsToMidiBuffer (events, midiEvents); - return 1; -#else - return 0; -#endif - } - - void process (float** inputs, float** outputs, VstInt32 numSamples) - { - const int numIn = numInChans; - const int numOut = numOutChans; - - AudioSampleBuffer temp (numIn, numSamples); - int i; - for (i = numIn; --i >= 0;) - memcpy (temp.getSampleData (i), outputs[i], sizeof (float) * numSamples); - - processReplacing (inputs, outputs, numSamples); - - AudioSampleBuffer dest (outputs, numOut, numSamples); - - for (i = jmin (numIn, numOut); --i >= 0;) - dest.addFrom (i, 0, temp, i, 0, numSamples); - } - - void processReplacing (float** inputs, float** outputs, VstInt32 numSamples) - { - if (firstProcessCallback) - { - firstProcessCallback = false; - - // if this fails, the host hasn't called resume() before processing - jassert (isProcessing); - - // (tragically, some hosts actually need this, although it's stupid to have - // to do it here..) - if (! isProcessing) - resume(); - - filter->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */); - -#if JUCE_WINDOWS - if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL - && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST) - filter->setNonRealtime (true); -#endif - } - -#if JUCE_DEBUG && ! JucePlugin_ProducesMidiOutput - const int numMidiEventsComingIn = midiEvents.getNumEvents(); -#endif - - jassert (activePlugins.contains (this)); - - { - const ScopedLock sl (filter->getCallbackLock()); - - const int numIn = numInChans; - const int numOut = numOutChans; - - if (filter->isSuspended()) - { - for (int i = 0; i < numOut; ++i) - zeromem (outputs[i], sizeof (float) * numSamples); - } - else - { - int i; - for (i = 0; i < numOut; ++i) - { - float* chan = tempChannels.getUnchecked(i); - - if (chan == 0) - { - chan = outputs[i]; - - // if some output channels are disabled, some hosts supply the same buffer - // for multiple channels - this buggers up our method of copying the - // inputs over the outputs, so we need to create unique temp buffers in this case.. - for (int j = i; --j >= 0;) - { - if (outputs[j] == chan) - { - chan = (float*) juce_malloc (sizeof (float) * blockSize * 2); - tempChannels.set (i, chan); - break; - } - } - } - - if (i < numIn && chan != inputs[i]) - memcpy (chan, inputs[i], sizeof (float) * numSamples); - - channels[i] = chan; - } - - for (; i < numIn; ++i) - channels[i] = inputs[i]; - - AudioSampleBuffer chans (channels, jmax (numIn, numOut), numSamples); - - filter->processBlock (chans, midiEvents); - } - } - - if (! midiEvents.isEmpty()) - { -#if JucePlugin_ProducesMidiOutput - const int numEvents = midiEvents.getNumEvents(); - - outgoingEvents.ensureSize (numEvents); - outgoingEvents.clear(); - - const JUCE_NAMESPACE::uint8* midiEventData; - int midiEventSize, midiEventPosition; - MidiBuffer::Iterator i (midiEvents); - - while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition)) - { - jassert (midiEventPosition >= 0 && midiEventPosition < numSamples); - - outgoingEvents.addEvent (midiEventData, midiEventSize, midiEventPosition); - } - - sendVstEventsToHost (outgoingEvents.events); -#else - /* This assertion is caused when you've added some events to the - midiMessages array in your processBlock() method, which usually means - that you're trying to send them somewhere. But in this case they're - getting thrown away. - - If your plugin does want to send midi messages, you'll need to set - the JucePlugin_ProducesMidiOutput macro to 1 in your - JucePluginCharacteristics.h file. - - If you don't want to produce any midi output, then you should clear the - midiMessages array at the end of your processBlock() method, to - indicate that you don't want any of the events to be passed through - to the output. - */ - jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn); -#endif - - midiEvents.clear(); - } - } - - //============================================================================== - VstInt32 startProcess () { return 0; } - VstInt32 stopProcess () { return 0;} - - void resume() - { - if (filter == 0) - return; - - isProcessing = true; - channels.calloc (numInChans + numOutChans); - - double rate = getSampleRate(); - jassert (rate > 0); - if (rate <= 0.0) - rate = 44100.0; - - const int blockSize = getBlockSize(); - jassert (blockSize > 0); - - firstProcessCallback = true; - - filter->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */); - - filter->setPlayConfigDetails (numInChans, numOutChans, - rate, blockSize); - - deleteTempChannels(); - - filter->prepareToPlay (rate, blockSize); - - midiEvents.ensureSize (2048); - midiEvents.clear(); - - setInitialDelay (filter->getLatencySamples()); - - AudioEffectX::resume(); - -#if JucePlugin_ProducesMidiOutput - outgoingEvents.ensureSize (512); -#endif - -#if JucePlugin_WantsMidiInput && ! JUCE_USE_VSTSDK_2_4 - wantEvents(); -#endif - } - - void suspend() - { - if (filter == 0) - return; - - AudioEffectX::suspend(); - - filter->releaseResources(); - outgoingEvents.freeEvents(); - - isProcessing = false; - channels.free(); - - deleteTempChannels(); - } - - bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info) - { - const VstTimeInfo* const ti = getTimeInfo (kVstPpqPosValid - | kVstTempoValid - | kVstBarsValid - //| kVstCyclePosValid - | kVstTimeSigValid - | kVstSmpteValid - | kVstClockValid); - - if (ti == 0 || ti->sampleRate <= 0) - return false; - - if ((ti->flags & kVstTempoValid) != 0) - info.bpm = ti->tempo; - else - info.bpm = 0.0; - - if ((ti->flags & kVstTimeSigValid) != 0) - { - info.timeSigNumerator = ti->timeSigNumerator; - info.timeSigDenominator = ti->timeSigDenominator; - } - else - { - info.timeSigNumerator = 4; - info.timeSigDenominator = 4; - } - - info.timeInSeconds = ti->samplePos / ti->sampleRate; - - if ((ti->flags & kVstPpqPosValid) != 0) - info.ppqPosition = ti->ppqPos; - else - info.ppqPosition = 0.0; - - if ((ti->flags & kVstBarsValid) != 0) - info.ppqPositionOfLastBarStart = ti->barStartPos; - else - info.ppqPositionOfLastBarStart = 0.0; - - if ((ti->flags & kVstSmpteValid) != 0) - { - AudioPlayHead::FrameRateType rate = AudioPlayHead::fpsUnknown; - double fps = 1.0; - - switch (ti->smpteFrameRate) - { - case kVstSmpte24fps: - rate = AudioPlayHead::fps24; - fps = 24.0; - break; - - case kVstSmpte25fps: - rate = AudioPlayHead::fps25; - fps = 25.0; - break; - - case kVstSmpte2997fps: - rate = AudioPlayHead::fps2997; - fps = 29.97; - break; - - case kVstSmpte30fps: - rate = AudioPlayHead::fps30; - fps = 30.0; - break; - - case kVstSmpte2997dfps: - rate = AudioPlayHead::fps2997drop; - fps = 29.97; - break; - - case kVstSmpte30dfps: - rate = AudioPlayHead::fps30drop; - fps = 30.0; - break; - - case kVstSmpteFilm16mm: - case kVstSmpteFilm35mm: - fps = 24.0; - break; - - case kVstSmpte239fps: fps = 23.976; break; - case kVstSmpte249fps: fps = 24.976; break; - case kVstSmpte599fps: fps = 59.94; break; - case kVstSmpte60fps: fps = 60; break; - - default: - jassertfalse // unknown frame-rate.. - } - - info.frameRate = rate; - info.editOriginTime = ti->smpteOffset / (80.0 * fps); - } - else - { - info.frameRate = AudioPlayHead::fpsUnknown; - info.editOriginTime = 0; - } - - info.isRecording = (ti->flags & kVstTransportRecording) != 0; - info.isPlaying = (ti->flags & kVstTransportPlaying) != 0 || info.isRecording; - - return true; - } - - //============================================================================== - VstInt32 getProgram() - { - return filter != 0 ? filter->getCurrentProgram() : 0; - } - - void setProgram (VstInt32 program) - { - if (filter != 0) - filter->setCurrentProgram (program); - } - - void setProgramName (char* name) - { - if (filter != 0) - filter->changeProgramName (filter->getCurrentProgram(), name); - } - - void getProgramName (char* name) - { - if (filter != 0) - filter->getProgramName (filter->getCurrentProgram()).copyToCString (name, 24); - } - - bool getProgramNameIndexed (VstInt32 /*category*/, VstInt32 index, char* text) - { - if (filter != 0 && ((unsigned int) index) < (unsigned int) filter->getNumPrograms()) - { - filter->getProgramName (index).copyToCString (text, 24); - return true; - } - - return false; - } - - //============================================================================== - float getParameter (VstInt32 index) - { - if (filter == 0) - return 0.0f; - - jassert (((unsigned int) index) < (unsigned int) filter->getNumParameters()); - return filter->getParameter (index); - } - - void setParameter (VstInt32 index, float value) - { - if (filter != 0) - { - jassert (((unsigned int) index) < (unsigned int) filter->getNumParameters()); - filter->setParameter (index, value); - } - } - - void getParameterDisplay (VstInt32 index, char* text) - { - if (filter != 0) - { - jassert (((unsigned int) index) < (unsigned int) filter->getNumParameters()); - filter->getParameterText (index).copyToCString (text, 24); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. - } - } - - void getParameterName (VstInt32 index, char* text) - { - if (filter != 0) - { - jassert (((unsigned int) index) < (unsigned int) filter->getNumParameters()); - filter->getParameterName (index).copyToCString (text, 16); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. - } - } - - void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue) - { - setParameterAutomated (index, newValue); - } - - void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index) - { - beginEdit (index); - } - - void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index) - { - endEdit (index); - } - - void audioProcessorChanged (AudioProcessor*) - { - updateDisplay(); - } - - bool canParameterBeAutomated (VstInt32 index) - { - return filter != 0 && filter->isParameterAutomatable ((int) index); - } - - class ChannelConfigComparator - { - public: - static int compareElements (const short* const first, const short* const second) - { - if (first[0] < second[0]) - return -1; - else if (first[0] > second[0]) - return 1; - else if (first[1] < second[1]) - return -1; - else if (first[1] > second[1]) - return 1; - - return 0; - } - }; - - bool setSpeakerArrangement (VstSpeakerArrangement* pluginInput, - VstSpeakerArrangement* pluginOutput) - { - short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations }; - - Array channelConfigsSorted; - ChannelConfigComparator comp; - - for (int i = 0; i < numElementsInArray (channelConfigs); ++i) - channelConfigsSorted.addSorted (comp, channelConfigs[i]); - - for (int i = channelConfigsSorted.size(); --i >= 0;) - { - const short* const config = channelConfigsSorted.getUnchecked(i); - bool inCountMatches = (config[0] == pluginInput->numChannels); - bool outCountMatches = (config[1] == pluginOutput->numChannels); - - if (inCountMatches && outCountMatches) - { - speakerIn = (VstSpeakerArrangementType) pluginInput->type; - speakerOut = (VstSpeakerArrangementType) pluginOutput->type; - numInChans = pluginInput->numChannels; - numOutChans = pluginOutput->numChannels; - - filter->setPlayConfigDetails (numInChans, numOutChans, - filter->getSampleRate(), - filter->getBlockSize()); - return true; - } - } - - return false; - } - - //============================================================================== - VstInt32 getChunk (void** data, bool onlyStoreCurrentProgramData) - { - if (filter == 0) - return 0; - - chunkMemory.setSize (0); - if (onlyStoreCurrentProgramData) - filter->getCurrentProgramStateInformation (chunkMemory); - else - filter->getStateInformation (chunkMemory); - - *data = (void*) chunkMemory.getData(); - - // because the chunk is only needed temporarily by the host (or at least you'd - // hope so) we'll give it a while and then free it in the timer callback. - chunkMemoryTime = JUCE_NAMESPACE::Time::getApproximateMillisecondCounter(); - - return (VstInt32) chunkMemory.getSize(); - } - - VstInt32 setChunk (void* data, VstInt32 byteSize, bool onlyRestoreCurrentProgramData) - { - if (filter == 0) - return 0; - - chunkMemory.setSize (0); - chunkMemoryTime = 0; - - if (byteSize > 0 && data != 0) - { - if (onlyRestoreCurrentProgramData) - filter->setCurrentProgramStateInformation (data, byteSize); - else - filter->setStateInformation (data, byteSize); - } - - return 0; - } - - void timerCallback() - { - if (shouldDeleteEditor) - { - shouldDeleteEditor = false; - deleteEditor (true); - } - - if (chunkMemoryTime > 0 - && chunkMemoryTime < JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000 - && ! recursionCheck) - { - chunkMemoryTime = 0; - chunkMemory.setSize (0); - } - -#if JUCE_MAC - if (hostWindow != 0) - checkWindowVisibility (hostWindow, editorComp); -#endif - - tryMasterIdle(); - } - - void tryMasterIdle() - { - if (Component::isMouseButtonDownAnywhere() - && ! recursionCheck) - { - const JUCE_NAMESPACE::uint32 now = JUCE_NAMESPACE::Time::getMillisecondCounter(); - - if (now > lastMasterIdleCall + 20 && editorComp != 0) - { - lastMasterIdleCall = now; - - recursionCheck = true; - masterIdle(); - recursionCheck = false; - } - } - } - - void doIdleCallback() - { - // (wavelab calls this on a separate thread and causes a deadlock).. - if (MessageManager::getInstance()->isThisTheMessageThread() - && ! recursionCheck) - { - recursionCheck = true; - - juce_callAnyTimersSynchronously(); - - for (int i = ComponentPeer::getNumPeers(); --i >= 0;) - ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow(); - - recursionCheck = false; - } - } - - void createEditorComp() - { - if (hasShutdown || filter == 0) - return; - - if (editorComp == 0) - { - AudioProcessorEditor* const ed = filter->createEditorIfNeeded(); - - if (ed != 0) - { - cEffect.flags |= effFlagsHasEditor; - ed->setOpaque (true); - ed->setVisible (true); - - editorComp = new EditorCompWrapper (*this, ed); - } - else - { - cEffect.flags &= ~effFlagsHasEditor; - } - } - - shouldDeleteEditor = false; - } - - void deleteEditor (bool canDeleteLaterIfModal) - { - PopupMenu::dismissAllActiveMenus(); - - jassert (! recursionCheck); - recursionCheck = true; - - if (editorComp != 0) - { - Component* const modalComponent = Component::getCurrentlyModalComponent(); - if (modalComponent != 0) - { - modalComponent->exitModalState (0); - - if (canDeleteLaterIfModal) - { - shouldDeleteEditor = true; - recursionCheck = false; - return; - } - } - -#if JUCE_MAC - if (hostWindow != 0) - { - detachComponentFromWindowRef (editorComp, hostWindow); - hostWindow = 0; - } -#endif - - filter->editorBeingDeleted (editorComp->getEditorComp()); - - editorComp = 0; - - // there's some kind of component currently modal, but the host - // is trying to delete our plugin. You should try to avoid this happening.. - jassert (Component::getCurrentlyModalComponent() == 0); - } - -#if JUCE_LINUX - hostWindow = 0; -#endif - - recursionCheck = false; - } - - VstIntPtr dispatcher (VstInt32 opCode, VstInt32 index, VstIntPtr value, void* ptr, float opt) - { - if (hasShutdown) - return 0; - - if (opCode == effEditIdle) - { - doIdleCallback(); - return 0; - } - else if (opCode == effEditOpen) - { - checkWhetherWavelabHasChangedThread(); - const MessageManagerLock mmLock; - jassert (! recursionCheck); - - deleteEditor (true); - createEditorComp(); - - if (editorComp != 0) - { - editorComp->setOpaque (true); - editorComp->setVisible (false); - - #if JUCE_WINDOWS - editorComp->addToDesktop (0, ptr); - hostWindow = (HWND) ptr; - #elif JUCE_LINUX - editorComp->addToDesktop (0); - hostWindow = (Window) ptr; - Window editorWnd = (Window) editorComp->getWindowHandle(); - XReparentWindow (display, editorWnd, hostWindow, 0, 0); - #else - hostWindow = attachComponentToWindowRef (editorComp, (WindowRef) ptr); - #endif - editorComp->setVisible (true); - - return 1; - } - } - else if (opCode == effEditClose) - { - checkWhetherWavelabHasChangedThread(); - const MessageManagerLock mmLock; - deleteEditor (true); - return 0; - } - else if (opCode == effEditGetRect) - { - checkWhetherWavelabHasChangedThread(); - const MessageManagerLock mmLock; - createEditorComp(); - - if (editorComp != 0) - { - editorSize.left = 0; - editorSize.top = 0; - editorSize.right = (VstInt16) editorComp->getWidth(); - editorSize.bottom = (VstInt16) editorComp->getHeight(); - - *((ERect**) ptr) = &editorSize; - - return (VstIntPtr) (pointer_sized_int) &editorSize; - } - else - { - return 0; - } - } - - return AudioEffectX::dispatcher (opCode, index, value, ptr, opt); - } - - void resizeHostWindow (int newWidth, int newHeight) - { - if (editorComp != 0) - { -#if ! JUCE_LINUX // linux hosts shouldn't be trusted! - if (! (canHostDo (const_cast ("sizeWindow")) && sizeWindow (newWidth, newHeight))) -#endif - { - // some hosts don't support the sizeWindow call, so do it manually.. -#if JUCE_MAC - setNativeHostWindowSize (hostWindow, editorComp, newWidth, newHeight); -#elif JUCE_LINUX - Window root; - int x, y; - unsigned int width, height, border, depth; - - XGetGeometry (display, hostWindow, &root, - &x, &y, &width, &height, &border, &depth); - - newWidth += (width + border) - editorComp->getWidth(); - newHeight += (height + border) - editorComp->getHeight(); - - XResizeWindow (display, hostWindow, newWidth, newHeight); -#else - int dw = 0; - int dh = 0; - const int frameThickness = GetSystemMetrics (SM_CYFIXEDFRAME); - - HWND w = (HWND) editorComp->getWindowHandle(); - - while (w != 0) - { - HWND parent = GetParent (w); - - if (parent == 0) - break; - - TCHAR windowType [32]; - zeromem (windowType, sizeof (windowType)); - GetClassName (parent, windowType, 31); - - if (String (windowType).equalsIgnoreCase ("MDIClient")) - break; - - RECT windowPos; - GetWindowRect (w, &windowPos); - - RECT parentPos; - GetWindowRect (parent, &parentPos); - - SetWindowPos (w, 0, 0, 0, - newWidth + dw, - newHeight + dh, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER); - - dw = (parentPos.right - parentPos.left) - (windowPos.right - windowPos.left); - dh = (parentPos.bottom - parentPos.top) - (windowPos.bottom - windowPos.top); - - w = parent; - - if (dw == 2 * frameThickness) - break; - - if (dw > 100 || dh > 100) - w = 0; - } - - if (w != 0) - SetWindowPos (w, 0, 0, 0, - newWidth + dw, - newHeight + dh, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER); -#endif - } - - if (editorComp->getPeer() != 0) - editorComp->getPeer()->handleMovedOrResized(); - } - } - - static PluginHostType& getHostType() - { - static PluginHostType hostType; - return hostType; - } - - //============================================================================== - // A component to hold the AudioProcessorEditor, and cope with some housekeeping - // chores when it changes or repaints. - class EditorCompWrapper : public Component, - public AsyncUpdater - { - public: - EditorCompWrapper (JuceVSTWrapper& wrapper_, AudioProcessorEditor* editor) - : wrapper (wrapper_) - { - setOpaque (true); - editor->setOpaque (true); - - setBounds (editor->getBounds()); - editor->setTopLeftPosition (0, 0); - addAndMakeVisible (editor); - - #if JUCE_WIN - if (! getHostType().isReceptor()) - addMouseListener (this, true); - #endif - } - - ~EditorCompWrapper() - { - deleteAllChildren(); - } - - void paint (Graphics&) {} - - void paintOverChildren (Graphics&) - { - // this causes an async call to masterIdle() to help - // creaky old DAWs like Nuendo repaint themselves while we're - // repainting. Otherwise they just seem to give up and sit there - // waiting. - triggerAsyncUpdate(); - } - - #if JUCE_MAC - bool keyPressed (const KeyPress& kp) - { - // If we have an unused keypress, move the key-focus to a host window - // and re-inject the event.. - forwardCurrentKeyEventToHost (this); - return true; - } - #endif - - AudioProcessorEditor* getEditorComp() const - { - return dynamic_cast (getChildComponent (0)); - } - - void resized() - { - Component* const c = getChildComponent (0); - - if (c != 0) - c->setBounds (0, 0, getWidth(), getHeight()); - } - - void childBoundsChanged (Component* child) - { - child->setTopLeftPosition (0, 0); - - const int cw = child->getWidth(); - const int ch = child->getHeight(); - - wrapper.resizeHostWindow (cw, ch); - setSize (cw, ch); - - #if JUCE_MAC - wrapper.resizeHostWindow (cw, ch); // (doing this a second time seems to be necessary in tracktion) - #endif - } - - void handleAsyncUpdate() - { - wrapper.tryMasterIdle(); - } - - #if JUCE_WINDOWS - void mouseDown (const MouseEvent&) - { - broughtToFront(); - } - - void broughtToFront() - { - // for hosts like nuendo, need to also pop the MDI container to the - // front when our comp is clicked on. - HWND parent = findMDIParentOf ((HWND) getWindowHandle()); - - if (parent != 0) - SetWindowPos (parent, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - } - #endif - - //============================================================================== - juce_UseDebuggingNewOperator - - private: - JuceVSTWrapper& wrapper; - }; - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - AudioProcessor* filter; - JUCE_NAMESPACE::MemoryBlock chunkMemory; - JUCE_NAMESPACE::uint32 chunkMemoryTime; - ScopedPointer editorComp; - ERect editorSize; - MidiBuffer midiEvents; - VSTMidiEventList outgoingEvents; - bool isProcessing; - bool hasShutdown; - bool firstProcessCallback; - int diffW, diffH; - VstSpeakerArrangementType speakerIn, speakerOut; - int numInChans, numOutChans; - HeapBlock channels; - Array tempChannels; // see note in processReplacing() - bool hasCreatedTempChannels; - bool shouldDeleteEditor; - - //============================================================================== -#if JUCE_WINDOWS // Workarounds for Wavelab's happy-go-lucky use of threads. - class NonWavelabMMLock - { - public: - NonWavelabMMLock() : mm (getHostType().isWavelab() || getHostType().isCubaseBridged() ? 0 : new MessageManagerLock()) {} - ~NonWavelabMMLock() {} - - private: - ScopedPointer mm; - }; - - static void checkWhetherWavelabHasChangedThread() - { - MessageManager::getInstance()->setCurrentThreadAsMessageThread(); - } -#else - typedef MessageManagerLock NonWavelabMMLock; - static void checkWhetherWavelabHasChangedThread() {} -#endif - - //============================================================================== - void deleteTempChannels() - { - for (int i = tempChannels.size(); --i >= 0;) - juce_free (tempChannels.getUnchecked(i)); - - tempChannels.clear(); - - if (filter != 0) - tempChannels.insertMultiple (0, 0, filter->getNumInputChannels() + filter->getNumOutputChannels()); - - hasCreatedTempChannels = false; - } - - const String getHostName() - { - char host[256]; - zeromem (host, sizeof (host)); - getHostProductString (host); - return host; - } - -#if JUCE_MAC - void* hostWindow; -#elif JUCE_LINUX - Window hostWindow; -#else - HWND hostWindow; -#endif -}; - -//============================================================================== -/** Somewhere in the codebase of your plugin, you need to implement this function - and make it create an instance of the filter subclass that you're building. -*/ -extern AudioProcessor* JUCE_CALLTYPE createPluginFilter(); - - -//============================================================================== -static AEffect* pluginEntryPoint (audioMasterCallback audioMaster) -{ - initialiseJuce_GUI(); - - try - { - if (audioMaster (0, audioMasterVersion, 0, 0, 0, 0) != 0) - { - AudioProcessor* const filter = createPluginFilter(); - - if (filter != 0) - { - JuceVSTWrapper* const wrapper = new JuceVSTWrapper (audioMaster, filter); - return wrapper->getAeffect(); - } - } - } - catch (...) - {} - - return 0; -} - - -//============================================================================== -// Mac startup code.. -#if JUCE_MAC - -extern "C" __attribute__ ((visibility("default"))) AEffect* VSTPluginMain (audioMasterCallback audioMaster) -{ - initialiseMac(); - return pluginEntryPoint (audioMaster); -} - -extern "C" __attribute__ ((visibility("default"))) AEffect* main_macho (audioMasterCallback audioMaster) -{ - initialiseMac(); - return pluginEntryPoint (audioMaster); -} - -//============================================================================== -// Linux startup code.. -#elif JUCE_LINUX - -extern "C" AEffect* VSTPluginMain (audioMasterCallback audioMaster) -{ - SharedMessageThread::getInstance(); - - return pluginEntryPoint (audioMaster); -} - -extern "C" __attribute__ ((visibility("default"))) AEffect* main_plugin (audioMasterCallback audioMaster) asm ("main"); - -extern "C" __attribute__ ((visibility("default"))) AEffect* main_plugin (audioMasterCallback audioMaster) -{ - return VSTPluginMain (audioMaster); -} - -__attribute__((constructor)) void myPluginInit() -{ - // don't put initialiseJuce_GUI here... it will crash ! -} - -__attribute__((destructor)) void myPluginFini() -{ - // don't put shutdownJuce_GUI here... it will crash ! -} - -//============================================================================== -// Win32 startup code.. -#else - -extern "C" __declspec (dllexport) AEffect* VSTPluginMain (audioMasterCallback audioMaster) -{ - return pluginEntryPoint (audioMaster); -} - -#ifndef _WIN64 // (can't compile this on win64, but it's not needed anyway with VST2.4) -extern "C" __declspec (dllexport) void* main (audioMasterCallback audioMaster) -{ - return (void*) pluginEntryPoint (audioMaster); -} -#endif - -#if JucePlugin_Build_RTAS -BOOL WINAPI DllMainVST (HINSTANCE instance, DWORD dwReason, LPVOID) -#else -extern "C" BOOL WINAPI DllMain (HINSTANCE instance, DWORD dwReason, LPVOID) -#endif -{ - if (dwReason == DLL_PROCESS_ATTACH) - PlatformUtilities::setCurrentModuleInstanceHandle (instance); - - return TRUE; -} - -#endif - -#endif +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-10 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#ifdef _MSC_VER + #pragma warning (disable : 4996 4100) +#endif + +#ifdef _WIN32 + #include +#elif defined (LINUX) + #include + #include + #include + #undef KeyPress +#else + #include +#endif + +#ifdef PRAGMA_ALIGN_SUPPORTED + #undef PRAGMA_ALIGN_SUPPORTED + #define PRAGMA_ALIGN_SUPPORTED 1 +#endif + +#include "../juce_IncludeCharacteristics.h" + +#if JucePlugin_Build_VST + +//============================================================================== +/* These files come with the Steinberg VST SDK - to get them, you'll need to + visit the Steinberg website and jump through some hoops to sign up as a + VST developer. + + Then, you'll need to make sure your include path contains your "vstsdk2.3" or + "vstsdk2.4" directory. + + Note that the JUCE_USE_VSTSDK_2_4 macro should be defined in JucePluginCharacteristics.h +*/ +#if JUCE_USE_VSTSDK_2_4 + #ifdef __GNUC__ + #define __cdecl + #endif + + // VSTSDK V2.4 includes.. + #include "public.sdk/source/vst2.x/audioeffectx.h" + #include "public.sdk/source/vst2.x/aeffeditor.h" + #include "public.sdk/source/vst2.x/audioeffectx.cpp" + #include "public.sdk/source/vst2.x/audioeffect.cpp" + + #if ! VST_2_4_EXTENSIONS + #error // You're probably trying to include the wrong VSTSDK version - make sure your include path matches the JUCE_USE_VSTSDK_2_4 flag + #endif + +#else + // VSTSDK V2.3 includes.. + #include "source/common/audioeffectx.h" + #include "source/common/AEffEditor.hpp" + #include "source/common/audioeffectx.cpp" + #include "source/common/AudioEffect.cpp" + + #if (! VST_2_3_EXTENSIONS) || VST_2_4_EXTENSIONS + #error // You're probably trying to include the wrong VSTSDK version - make sure your include path matches the JUCE_USE_VSTSDK_2_4 flag + #endif + + #define __aeffect__ // (needed for juce_VSTMidiEventList.h to work) + + typedef long VstInt32; + typedef long VstIntPtr; + enum Vst2StringConstants + { + kVstMaxNameLen = 64, + kVstMaxLabelLen = 64, + kVstMaxShortLabelLen = 8, + kVstMaxCategLabelLen = 24, + kVstMaxFileNameLen = 100 + }; + + enum VstSmpteFrameRate + { + kVstSmpte24fps = 0, ///< 24 fps + kVstSmpte25fps = 1, ///< 25 fps + kVstSmpte2997fps = 2, ///< 29.97 fps + kVstSmpte30fps = 3, ///< 30 fps + kVstSmpte2997dfps = 4, ///< 29.97 drop + kVstSmpte30dfps = 5, ///< 30 drop + kVstSmpteFilm16mm = 6, ///< Film 16mm + kVstSmpteFilm35mm = 7, ///< Film 35mm + kVstSmpte239fps = 10, ///< HDTV: 23.976 fps + kVstSmpte249fps = 11, ///< HDTV: 24.976 fps + kVstSmpte599fps = 12, ///< HDTV: 59.94 fps + kVstSmpte60fps = 13 ///< HDTV: 60 fps + }; + + struct VstMidiSysexEvent + { + VstInt32 type; ///< #kVstSysexType + VstInt32 byteSize; ///< sizeof (VstMidiSysexEvent) + VstInt32 deltaFrames; ///< sample frames related to the current block start sample position + VstInt32 flags; ///< none defined yet (should be zero) + VstInt32 dumpBytes; ///< byte size of sysexDump + VstIntPtr resvd1; ///< zero (Reserved for future use) + char* sysexDump; ///< sysex dump + VstIntPtr resvd2; ///< zero (Reserved for future use) + }; + + typedef int VstSpeakerArrangementType; +#endif + +//============================================================================== +#ifdef _MSC_VER + #pragma pack (push, 8) +#endif + +#include "../juce_PluginHeaders.h" +#include "../juce_PluginHostType.h" + + +#ifdef _MSC_VER + #pragma pack (pop) +#endif + +#undef MemoryBlock + +class JuceVSTWrapper; +static bool recursionCheck = false; +static JUCE_NAMESPACE::uint32 lastMasterIdleCall = 0; + +BEGIN_JUCE_NAMESPACE + extern void juce_callAnyTimersSynchronously(); + + #if JUCE_MAC + extern void initialiseMac(); + extern void* attachComponentToWindowRef (Component* component, void* windowRef); + extern void detachComponentFromWindowRef (Component* component, void* nsWindow); + extern void setNativeHostWindowSize (void* nsWindow, Component* editorComp, int newWidth, int newHeight); + extern void checkWindowVisibility (void* nsWindow, Component* component); + extern void forwardCurrentKeyEventToHost (Component* component); + #endif + + #if JUCE_LINUX + extern Display* display; + #endif +END_JUCE_NAMESPACE + + +//============================================================================== +#if JUCE_WINDOWS + +static HWND findMDIParentOf (HWND w) +{ + const int frameThickness = GetSystemMetrics (SM_CYFIXEDFRAME); + + while (w != 0) + { + HWND parent = GetParent (w); + + if (parent == 0) + break; + + TCHAR windowType [32]; + zeromem (windowType, sizeof (windowType)); + GetClassName (parent, windowType, 31); + + if (String (windowType).equalsIgnoreCase ("MDIClient")) + { + w = parent; + break; + } + + RECT windowPos; + GetWindowRect (w, &windowPos); + + RECT parentPos; + GetWindowRect (parent, &parentPos); + + const int dw = (parentPos.right - parentPos.left) - (windowPos.right - windowPos.left); + const int dh = (parentPos.bottom - parentPos.top) - (windowPos.bottom - windowPos.top); + + if (dw > 100 || dh > 100) + break; + + w = parent; + + if (dw == 2 * frameThickness) + break; + } + + return w; +} + +//============================================================================== +#elif JUCE_LINUX + +class SharedMessageThread : public Thread +{ +public: + SharedMessageThread() + : Thread ("VstMessageThread"), + initialised (false) + { + startThread (7); + + while (! initialised) + sleep (1); + } + + ~SharedMessageThread() + { + signalThreadShouldExit(); + JUCEApplication::quit(); + waitForThreadToExit (5000); + clearSingletonInstance(); + } + + void run() + { + initialiseJuce_GUI(); + initialised = true; + + MessageManager::getInstance()->setCurrentThreadAsMessageThread(); + + while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250)) + { + } + } + + juce_DeclareSingleton (SharedMessageThread, false) + +private: + bool initialised; +}; + +juce_ImplementSingleton (SharedMessageThread) + +#endif + +static Array activePlugins; + +//============================================================================== +/** + This wraps an AudioProcessor as an AudioEffectX... +*/ +class JuceVSTWrapper : public AudioEffectX, + private Timer, + public AudioProcessorListener, + public AudioPlayHead +{ +public: + //============================================================================== + JuceVSTWrapper (audioMasterCallback audioMaster, + AudioProcessor* const filter_) + : AudioEffectX (audioMaster, + filter_->getNumPrograms(), + filter_->getNumParameters()), + filter (filter_) + { + chunkMemoryTime = 0; + isProcessing = false; + hasShutdown = false; + firstProcessCallback = true; + shouldDeleteEditor = false; + speakerIn = kSpeakerArrEmpty; + speakerOut = kSpeakerArrEmpty; + numInChans = JucePlugin_MaxNumInputChannels; + numOutChans = JucePlugin_MaxNumOutputChannels; + +#if JUCE_MAC || JUCE_LINUX + hostWindow = 0; +#endif + + filter->setPlayConfigDetails (numInChans, numOutChans, 0, 0); + + filter_->setPlayHead (this); + filter_->addListener (this); + + cEffect.flags |= effFlagsHasEditor; + cEffect.version = (long) (JucePlugin_VersionCode); + + setUniqueID ((int) (JucePlugin_VSTUniqueID)); + +#if JucePlugin_WantsMidiInput && ! JUCE_USE_VSTSDK_2_4 + wantEvents(); +#endif + + setNumInputs (numInChans); + setNumOutputs (numOutChans); + + canProcessReplacing (true); + +#if ! JUCE_USE_VSTSDK_2_4 + hasVu (false); + hasClip (false); +#endif + + isSynth ((JucePlugin_IsSynth) != 0); + noTail (((JucePlugin_SilenceInProducesSilenceOut) != 0) && (JucePlugin_TailLengthSeconds <= 0)); + setInitialDelay (filter->getLatencySamples()); + programsAreChunks (true); + + activePlugins.add (this); + } + + ~JuceVSTWrapper() + { + stopTimer(); + deleteEditor (false); + + hasShutdown = true; + + delete filter; + filter = 0; + + jassert (editorComp == 0); + + channels.free(); + deleteTempChannels(); + + jassert (activePlugins.contains (this)); + activePlugins.removeValue (this); + + if (activePlugins.size() == 0) + { +#if JUCE_LINUX + SharedMessageThread::deleteInstance(); +#endif + shutdownJuce_GUI(); + } + } + + void open() + { + if (editorComp == 0) + { + checkWhetherWavelabHasChangedThread(); + const MessageManagerLock mmLock; + + AudioProcessorEditor* const ed = filter->createEditorIfNeeded(); + + if (ed != 0) + cEffect.flags |= effFlagsHasEditor; + else + cEffect.flags &= ~effFlagsHasEditor; + + filter->editorBeingDeleted (ed); + delete ed; + } + + startTimer (1000 / 4); + } + + void close() + { + const NonWavelabMMLock mmLock; + jassert (! recursionCheck); + + stopTimer(); + deleteEditor (false); + } + + //============================================================================== + bool getEffectName (char* name) + { + String (JucePlugin_Name).copyToCString (name, 64); + return true; + } + + bool getVendorString (char* text) + { + String (JucePlugin_Manufacturer).copyToCString (text, 64); + return true; + } + + bool getProductString (char* text) + { + return getEffectName (text); + } + + VstInt32 getVendorVersion() + { + return JucePlugin_VersionCode; + } + + VstPlugCategory getPlugCategory() + { + return JucePlugin_VSTCategory; + } + + VstInt32 canDo (char* text) + { + VstInt32 result = 0; + + if (strcmp (text, "receiveVstEvents") == 0 + || strcmp (text, "receiveVstMidiEvent") == 0 + || strcmp (text, "receiveVstMidiEvents") == 0) + { +#if JucePlugin_WantsMidiInput + result = 1; +#else + result = -1; +#endif + } + else if (strcmp (text, "sendVstEvents") == 0 + || strcmp (text, "sendVstMidiEvent") == 0 + || strcmp (text, "sendVstMidiEvents") == 0) + { +#if JucePlugin_ProducesMidiOutput + result = 1; +#else + result = -1; +#endif + } + else if (strcmp (text, "receiveVstTimeInfo") == 0 + || strcmp (text, "conformsToWindowRules") == 0) + { + result = 1; + } + + return result; + } + + bool keysRequired() + { + return (JucePlugin_EditorRequiresKeyboardFocus) != 0; + } + + bool getInputProperties (VstInt32 index, VstPinProperties* properties) + { + if (filter == 0 || index >= JucePlugin_MaxNumInputChannels) + return false; + + const String name (filter->getInputChannelName ((int) index)); + + name.copyToCString (properties->label, kVstMaxLabelLen - 1); + name.copyToCString (properties->shortLabel, kVstMaxShortLabelLen - 1); + + if (speakerIn != kSpeakerArrEmpty) + { + properties->flags = kVstPinUseSpeaker; + properties->arrangementType = speakerIn; + } + else + { + properties->flags = kVstPinIsActive; + + if (filter->isInputChannelStereoPair ((int) index)) + properties->flags |= kVstPinIsStereo; + + properties->arrangementType = 0; + } + + return true; + } + + bool getOutputProperties (VstInt32 index, VstPinProperties* properties) + { + if (filter == 0 || index >= JucePlugin_MaxNumOutputChannels) + return false; + + const String name (filter->getOutputChannelName ((int) index)); + + name.copyToCString (properties->label, kVstMaxLabelLen - 1); + name.copyToCString (properties->shortLabel, kVstMaxShortLabelLen - 1); + + if (speakerOut != kSpeakerArrEmpty) + { + properties->flags = kVstPinUseSpeaker; + properties->arrangementType = speakerOut; + } + else + { + properties->flags = kVstPinIsActive; + + if (filter->isOutputChannelStereoPair ((int) index)) + properties->flags |= kVstPinIsStereo; + + properties->arrangementType = 0; + } + + return true; + } + + //============================================================================== + VstInt32 processEvents (VstEvents* events) + { +#if JucePlugin_WantsMidiInput + VSTMidiEventList::addEventsToMidiBuffer (events, midiEvents); + return 1; +#else + return 0; +#endif + } + + void process (float** inputs, float** outputs, VstInt32 numSamples) + { + const int numIn = numInChans; + const int numOut = numOutChans; + + AudioSampleBuffer temp (numIn, numSamples); + int i; + for (i = numIn; --i >= 0;) + memcpy (temp.getSampleData (i), outputs[i], sizeof (float) * numSamples); + + processReplacing (inputs, outputs, numSamples); + + AudioSampleBuffer dest (outputs, numOut, numSamples); + + for (i = jmin (numIn, numOut); --i >= 0;) + dest.addFrom (i, 0, temp, i, 0, numSamples); + } + + void processReplacing (float** inputs, float** outputs, VstInt32 numSamples) + { + if (firstProcessCallback) + { + firstProcessCallback = false; + + // if this fails, the host hasn't called resume() before processing + jassert (isProcessing); + + // (tragically, some hosts actually need this, although it's stupid to have + // to do it here..) + if (! isProcessing) + resume(); + + filter->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */); + +#if JUCE_WINDOWS + if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL + && GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST) + filter->setNonRealtime (true); +#endif + } + +#if JUCE_DEBUG && ! JucePlugin_ProducesMidiOutput + const int numMidiEventsComingIn = midiEvents.getNumEvents(); +#endif + + jassert (activePlugins.contains (this)); + + { + const ScopedLock sl (filter->getCallbackLock()); + + const int numIn = numInChans; + const int numOut = numOutChans; + + if (filter->isSuspended()) + { + for (int i = 0; i < numOut; ++i) + zeromem (outputs[i], sizeof (float) * numSamples); + } + else + { + int i; + for (i = 0; i < numOut; ++i) + { + float* chan = tempChannels.getUnchecked(i); + + if (chan == 0) + { + chan = outputs[i]; + + // if some output channels are disabled, some hosts supply the same buffer + // for multiple channels - this buggers up our method of copying the + // inputs over the outputs, so we need to create unique temp buffers in this case.. + for (int j = i; --j >= 0;) + { + if (outputs[j] == chan) + { + chan = (float*) juce_malloc (sizeof (float) * blockSize * 2); + tempChannels.set (i, chan); + break; + } + } + } + + if (i < numIn && chan != inputs[i]) + memcpy (chan, inputs[i], sizeof (float) * numSamples); + + channels[i] = chan; + } + + for (; i < numIn; ++i) + channels[i] = inputs[i]; + + AudioSampleBuffer chans (channels, jmax (numIn, numOut), numSamples); + + filter->processBlock (chans, midiEvents); + } + } + + if (! midiEvents.isEmpty()) + { +#if JucePlugin_ProducesMidiOutput + const int numEvents = midiEvents.getNumEvents(); + + outgoingEvents.ensureSize (numEvents); + outgoingEvents.clear(); + + const JUCE_NAMESPACE::uint8* midiEventData; + int midiEventSize, midiEventPosition; + MidiBuffer::Iterator i (midiEvents); + + while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition)) + { + jassert (midiEventPosition >= 0 && midiEventPosition < numSamples); + + outgoingEvents.addEvent (midiEventData, midiEventSize, midiEventPosition); + } + + sendVstEventsToHost (outgoingEvents.events); +#else + /* This assertion is caused when you've added some events to the + midiMessages array in your processBlock() method, which usually means + that you're trying to send them somewhere. But in this case they're + getting thrown away. + + If your plugin does want to send midi messages, you'll need to set + the JucePlugin_ProducesMidiOutput macro to 1 in your + JucePluginCharacteristics.h file. + + If you don't want to produce any midi output, then you should clear the + midiMessages array at the end of your processBlock() method, to + indicate that you don't want any of the events to be passed through + to the output. + */ + jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn); +#endif + + midiEvents.clear(); + } + } + + //============================================================================== + VstInt32 startProcess () { return 0; } + VstInt32 stopProcess () { return 0;} + + void resume() + { + if (filter == 0) + return; + + isProcessing = true; + channels.calloc (numInChans + numOutChans); + + double rate = getSampleRate(); + jassert (rate > 0); + if (rate <= 0.0) + rate = 44100.0; + + const int blockSize = getBlockSize(); + jassert (blockSize > 0); + + firstProcessCallback = true; + + filter->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */); + + filter->setPlayConfigDetails (numInChans, numOutChans, + rate, blockSize); + + deleteTempChannels(); + + filter->prepareToPlay (rate, blockSize); + + midiEvents.ensureSize (2048); + midiEvents.clear(); + + setInitialDelay (filter->getLatencySamples()); + + AudioEffectX::resume(); + +#if JucePlugin_ProducesMidiOutput + outgoingEvents.ensureSize (512); +#endif + +#if JucePlugin_WantsMidiInput && ! JUCE_USE_VSTSDK_2_4 + wantEvents(); +#endif + } + + void suspend() + { + if (filter == 0) + return; + + AudioEffectX::suspend(); + + filter->releaseResources(); + outgoingEvents.freeEvents(); + + isProcessing = false; + channels.free(); + + deleteTempChannels(); + } + + bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info) + { + const VstTimeInfo* const ti = getTimeInfo (kVstPpqPosValid + | kVstTempoValid + | kVstBarsValid + //| kVstCyclePosValid + | kVstTimeSigValid + | kVstSmpteValid + | kVstClockValid); + + if (ti == 0 || ti->sampleRate <= 0) + return false; + + if ((ti->flags & kVstTempoValid) != 0) + info.bpm = ti->tempo; + else + info.bpm = 0.0; + + if ((ti->flags & kVstTimeSigValid) != 0) + { + info.timeSigNumerator = ti->timeSigNumerator; + info.timeSigDenominator = ti->timeSigDenominator; + } + else + { + info.timeSigNumerator = 4; + info.timeSigDenominator = 4; + } + + info.timeInSeconds = ti->samplePos / ti->sampleRate; + + if ((ti->flags & kVstPpqPosValid) != 0) + info.ppqPosition = ti->ppqPos; + else + info.ppqPosition = 0.0; + + if ((ti->flags & kVstBarsValid) != 0) + info.ppqPositionOfLastBarStart = ti->barStartPos; + else + info.ppqPositionOfLastBarStart = 0.0; + + if ((ti->flags & kVstSmpteValid) != 0) + { + AudioPlayHead::FrameRateType rate = AudioPlayHead::fpsUnknown; + double fps = 1.0; + + switch (ti->smpteFrameRate) + { + case kVstSmpte24fps: + rate = AudioPlayHead::fps24; + fps = 24.0; + break; + + case kVstSmpte25fps: + rate = AudioPlayHead::fps25; + fps = 25.0; + break; + + case kVstSmpte2997fps: + rate = AudioPlayHead::fps2997; + fps = 29.97; + break; + + case kVstSmpte30fps: + rate = AudioPlayHead::fps30; + fps = 30.0; + break; + + case kVstSmpte2997dfps: + rate = AudioPlayHead::fps2997drop; + fps = 29.97; + break; + + case kVstSmpte30dfps: + rate = AudioPlayHead::fps30drop; + fps = 30.0; + break; + + case kVstSmpteFilm16mm: + case kVstSmpteFilm35mm: + fps = 24.0; + break; + + case kVstSmpte239fps: fps = 23.976; break; + case kVstSmpte249fps: fps = 24.976; break; + case kVstSmpte599fps: fps = 59.94; break; + case kVstSmpte60fps: fps = 60; break; + + default: + jassertfalse // unknown frame-rate.. + } + + info.frameRate = rate; + info.editOriginTime = ti->smpteOffset / (80.0 * fps); + } + else + { + info.frameRate = AudioPlayHead::fpsUnknown; + info.editOriginTime = 0; + } + + info.isRecording = (ti->flags & kVstTransportRecording) != 0; + info.isPlaying = (ti->flags & kVstTransportPlaying) != 0 || info.isRecording; + + return true; + } + + //============================================================================== + VstInt32 getProgram() + { + return filter != 0 ? filter->getCurrentProgram() : 0; + } + + void setProgram (VstInt32 program) + { + if (filter != 0) + filter->setCurrentProgram (program); + } + + void setProgramName (char* name) + { + if (filter != 0) + filter->changeProgramName (filter->getCurrentProgram(), name); + } + + void getProgramName (char* name) + { + if (filter != 0) + filter->getProgramName (filter->getCurrentProgram()).copyToCString (name, 24); + } + + bool getProgramNameIndexed (VstInt32 /*category*/, VstInt32 index, char* text) + { + if (filter != 0 && ((unsigned int) index) < (unsigned int) filter->getNumPrograms()) + { + filter->getProgramName (index).copyToCString (text, 24); + return true; + } + + return false; + } + + //============================================================================== + float getParameter (VstInt32 index) + { + if (filter == 0) + return 0.0f; + + jassert (((unsigned int) index) < (unsigned int) filter->getNumParameters()); + return filter->getParameter (index); + } + + void setParameter (VstInt32 index, float value) + { + if (filter != 0) + { + jassert (((unsigned int) index) < (unsigned int) filter->getNumParameters()); + filter->setParameter (index, value); + } + } + + void getParameterDisplay (VstInt32 index, char* text) + { + if (filter != 0) + { + jassert (((unsigned int) index) < (unsigned int) filter->getNumParameters()); + filter->getParameterText (index).copyToCString (text, 24); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. + } + } + + void getParameterName (VstInt32 index, char* text) + { + if (filter != 0) + { + jassert (((unsigned int) index) < (unsigned int) filter->getNumParameters()); + filter->getParameterName (index).copyToCString (text, 16); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. + } + } + + void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue) + { + setParameterAutomated (index, newValue); + } + + void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index) + { + beginEdit (index); + } + + void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index) + { + endEdit (index); + } + + void audioProcessorChanged (AudioProcessor*) + { + updateDisplay(); + } + + bool canParameterBeAutomated (VstInt32 index) + { + return filter != 0 && filter->isParameterAutomatable ((int) index); + } + + class ChannelConfigComparator + { + public: + static int compareElements (const short* const first, const short* const second) + { + if (first[0] < second[0]) + return -1; + else if (first[0] > second[0]) + return 1; + else if (first[1] < second[1]) + return -1; + else if (first[1] > second[1]) + return 1; + + return 0; + } + }; + + bool setSpeakerArrangement (VstSpeakerArrangement* pluginInput, + VstSpeakerArrangement* pluginOutput) + { + short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations }; + + Array channelConfigsSorted; + ChannelConfigComparator comp; + + for (int i = 0; i < numElementsInArray (channelConfigs); ++i) + channelConfigsSorted.addSorted (comp, channelConfigs[i]); + + for (int i = channelConfigsSorted.size(); --i >= 0;) + { + const short* const config = channelConfigsSorted.getUnchecked(i); + bool inCountMatches = (config[0] == pluginInput->numChannels); + bool outCountMatches = (config[1] == pluginOutput->numChannels); + + if (inCountMatches && outCountMatches) + { + speakerIn = (VstSpeakerArrangementType) pluginInput->type; + speakerOut = (VstSpeakerArrangementType) pluginOutput->type; + numInChans = pluginInput->numChannels; + numOutChans = pluginOutput->numChannels; + + filter->setPlayConfigDetails (numInChans, numOutChans, + filter->getSampleRate(), + filter->getBlockSize()); + return true; + } + } + + return false; + } + + //============================================================================== + VstInt32 getChunk (void** data, bool onlyStoreCurrentProgramData) + { + if (filter == 0) + return 0; + + chunkMemory.setSize (0); + if (onlyStoreCurrentProgramData) + filter->getCurrentProgramStateInformation (chunkMemory); + else + filter->getStateInformation (chunkMemory); + + *data = (void*) chunkMemory.getData(); + + // because the chunk is only needed temporarily by the host (or at least you'd + // hope so) we'll give it a while and then free it in the timer callback. + chunkMemoryTime = JUCE_NAMESPACE::Time::getApproximateMillisecondCounter(); + + return (VstInt32) chunkMemory.getSize(); + } + + VstInt32 setChunk (void* data, VstInt32 byteSize, bool onlyRestoreCurrentProgramData) + { + if (filter == 0) + return 0; + + chunkMemory.setSize (0); + chunkMemoryTime = 0; + + if (byteSize > 0 && data != 0) + { + if (onlyRestoreCurrentProgramData) + filter->setCurrentProgramStateInformation (data, byteSize); + else + filter->setStateInformation (data, byteSize); + } + + return 0; + } + + void timerCallback() + { + if (shouldDeleteEditor) + { + shouldDeleteEditor = false; + deleteEditor (true); + } + + if (chunkMemoryTime > 0 + && chunkMemoryTime < JUCE_NAMESPACE::Time::getApproximateMillisecondCounter() - 2000 + && ! recursionCheck) + { + chunkMemoryTime = 0; + chunkMemory.setSize (0); + } + +#if JUCE_MAC + if (hostWindow != 0) + checkWindowVisibility (hostWindow, editorComp); +#endif + + tryMasterIdle(); + } + + void tryMasterIdle() + { + if (Component::isMouseButtonDownAnywhere() + && ! recursionCheck) + { + const JUCE_NAMESPACE::uint32 now = JUCE_NAMESPACE::Time::getMillisecondCounter(); + + if (now > lastMasterIdleCall + 20 && editorComp != 0) + { + lastMasterIdleCall = now; + + recursionCheck = true; + masterIdle(); + recursionCheck = false; + } + } + } + + void doIdleCallback() + { + // (wavelab calls this on a separate thread and causes a deadlock).. + if (MessageManager::getInstance()->isThisTheMessageThread() + && ! recursionCheck) + { + recursionCheck = true; + + juce_callAnyTimersSynchronously(); + + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow(); + + recursionCheck = false; + } + } + + void createEditorComp() + { + if (hasShutdown || filter == 0) + return; + + if (editorComp == 0) + { + AudioProcessorEditor* const ed = filter->createEditorIfNeeded(); + + if (ed != 0) + { + cEffect.flags |= effFlagsHasEditor; + ed->setOpaque (true); + ed->setVisible (true); + + editorComp = new EditorCompWrapper (*this, ed); + } + else + { + cEffect.flags &= ~effFlagsHasEditor; + } + } + + shouldDeleteEditor = false; + } + + void deleteEditor (bool canDeleteLaterIfModal) + { + PopupMenu::dismissAllActiveMenus(); + + jassert (! recursionCheck); + recursionCheck = true; + + if (editorComp != 0) + { + Component* const modalComponent = Component::getCurrentlyModalComponent(); + if (modalComponent != 0) + { + modalComponent->exitModalState (0); + + if (canDeleteLaterIfModal) + { + shouldDeleteEditor = true; + recursionCheck = false; + return; + } + } + +#if JUCE_MAC + if (hostWindow != 0) + { + detachComponentFromWindowRef (editorComp, hostWindow); + hostWindow = 0; + } +#endif + + filter->editorBeingDeleted (editorComp->getEditorComp()); + + editorComp = 0; + + // there's some kind of component currently modal, but the host + // is trying to delete our plugin. You should try to avoid this happening.. + jassert (Component::getCurrentlyModalComponent() == 0); + } + +#if JUCE_LINUX + hostWindow = 0; +#endif + + recursionCheck = false; + } + + VstIntPtr dispatcher (VstInt32 opCode, VstInt32 index, VstIntPtr value, void* ptr, float opt) + { + if (hasShutdown) + return 0; + + if (opCode == effEditIdle) + { + doIdleCallback(); + return 0; + } + else if (opCode == effEditOpen) + { + checkWhetherWavelabHasChangedThread(); + const MessageManagerLock mmLock; + jassert (! recursionCheck); + + deleteEditor (true); + createEditorComp(); + + if (editorComp != 0) + { + editorComp->setOpaque (true); + editorComp->setVisible (false); + + #if JUCE_WINDOWS + editorComp->addToDesktop (0, ptr); + hostWindow = (HWND) ptr; + #elif JUCE_LINUX + editorComp->addToDesktop (0); + hostWindow = (Window) ptr; + Window editorWnd = (Window) editorComp->getWindowHandle(); + XReparentWindow (display, editorWnd, hostWindow, 0, 0); + #else + hostWindow = attachComponentToWindowRef (editorComp, (WindowRef) ptr); + #endif + editorComp->setVisible (true); + + return 1; + } + } + else if (opCode == effEditClose) + { + checkWhetherWavelabHasChangedThread(); + const MessageManagerLock mmLock; + deleteEditor (true); + return 0; + } + else if (opCode == effEditGetRect) + { + checkWhetherWavelabHasChangedThread(); + const MessageManagerLock mmLock; + createEditorComp(); + + if (editorComp != 0) + { + editorSize.left = 0; + editorSize.top = 0; + editorSize.right = (VstInt16) editorComp->getWidth(); + editorSize.bottom = (VstInt16) editorComp->getHeight(); + + *((ERect**) ptr) = &editorSize; + + return (VstIntPtr) (pointer_sized_int) &editorSize; + } + else + { + return 0; + } + } + + return AudioEffectX::dispatcher (opCode, index, value, ptr, opt); + } + + void resizeHostWindow (int newWidth, int newHeight) + { + if (editorComp != 0) + { +#if ! JUCE_LINUX // linux hosts shouldn't be trusted! + if (! (canHostDo (const_cast ("sizeWindow")) && sizeWindow (newWidth, newHeight))) +#endif + { + // some hosts don't support the sizeWindow call, so do it manually.. +#if JUCE_MAC + setNativeHostWindowSize (hostWindow, editorComp, newWidth, newHeight); +#elif JUCE_LINUX + Window root; + int x, y; + unsigned int width, height, border, depth; + + XGetGeometry (display, hostWindow, &root, + &x, &y, &width, &height, &border, &depth); + + newWidth += (width + border) - editorComp->getWidth(); + newHeight += (height + border) - editorComp->getHeight(); + + XResizeWindow (display, hostWindow, newWidth, newHeight); +#else + int dw = 0; + int dh = 0; + const int frameThickness = GetSystemMetrics (SM_CYFIXEDFRAME); + + HWND w = (HWND) editorComp->getWindowHandle(); + + while (w != 0) + { + HWND parent = GetParent (w); + + if (parent == 0) + break; + + TCHAR windowType [32]; + zeromem (windowType, sizeof (windowType)); + GetClassName (parent, windowType, 31); + + if (String (windowType).equalsIgnoreCase ("MDIClient")) + break; + + RECT windowPos; + GetWindowRect (w, &windowPos); + + RECT parentPos; + GetWindowRect (parent, &parentPos); + + SetWindowPos (w, 0, 0, 0, + newWidth + dw, + newHeight + dh, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER); + + dw = (parentPos.right - parentPos.left) - (windowPos.right - windowPos.left); + dh = (parentPos.bottom - parentPos.top) - (windowPos.bottom - windowPos.top); + + w = parent; + + if (dw == 2 * frameThickness) + break; + + if (dw > 100 || dh > 100) + w = 0; + } + + if (w != 0) + SetWindowPos (w, 0, 0, 0, + newWidth + dw, + newHeight + dh, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER); +#endif + } + + if (editorComp->getPeer() != 0) + editorComp->getPeer()->handleMovedOrResized(); + } + } + + static PluginHostType& getHostType() + { + static PluginHostType hostType; + return hostType; + } + + //============================================================================== + // A component to hold the AudioProcessorEditor, and cope with some housekeeping + // chores when it changes or repaints. + class EditorCompWrapper : public Component, + public AsyncUpdater + { + public: + EditorCompWrapper (JuceVSTWrapper& wrapper_, AudioProcessorEditor* editor) + : wrapper (wrapper_) + { + setOpaque (true); + editor->setOpaque (true); + + setBounds (editor->getBounds()); + editor->setTopLeftPosition (0, 0); + addAndMakeVisible (editor); + + #if JUCE_WIN + if (! getHostType().isReceptor()) + addMouseListener (this, true); + #endif + } + + ~EditorCompWrapper() + { + deleteAllChildren(); + } + + void paint (Graphics&) {} + + void paintOverChildren (Graphics&) + { + // this causes an async call to masterIdle() to help + // creaky old DAWs like Nuendo repaint themselves while we're + // repainting. Otherwise they just seem to give up and sit there + // waiting. + triggerAsyncUpdate(); + } + + #if JUCE_MAC + bool keyPressed (const KeyPress& kp) + { + // If we have an unused keypress, move the key-focus to a host window + // and re-inject the event.. + forwardCurrentKeyEventToHost (this); + return true; + } + #endif + + AudioProcessorEditor* getEditorComp() const + { + return dynamic_cast (getChildComponent (0)); + } + + void resized() + { + Component* const c = getChildComponent (0); + + if (c != 0) + c->setBounds (0, 0, getWidth(), getHeight()); + } + + void childBoundsChanged (Component* child) + { + child->setTopLeftPosition (0, 0); + + const int cw = child->getWidth(); + const int ch = child->getHeight(); + + wrapper.resizeHostWindow (cw, ch); + setSize (cw, ch); + + #if JUCE_MAC + wrapper.resizeHostWindow (cw, ch); // (doing this a second time seems to be necessary in tracktion) + #endif + } + + void handleAsyncUpdate() + { + wrapper.tryMasterIdle(); + } + + #if JUCE_WINDOWS + void mouseDown (const MouseEvent&) + { + broughtToFront(); + } + + void broughtToFront() + { + // for hosts like nuendo, need to also pop the MDI container to the + // front when our comp is clicked on. + HWND parent = findMDIParentOf ((HWND) getWindowHandle()); + + if (parent != 0) + SetWindowPos (parent, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } + #endif + + //============================================================================== + juce_UseDebuggingNewOperator + + private: + JuceVSTWrapper& wrapper; + }; + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + AudioProcessor* filter; + JUCE_NAMESPACE::MemoryBlock chunkMemory; + JUCE_NAMESPACE::uint32 chunkMemoryTime; + ScopedPointer editorComp; + ERect editorSize; + MidiBuffer midiEvents; + VSTMidiEventList outgoingEvents; + bool isProcessing; + bool hasShutdown; + bool firstProcessCallback; + int diffW, diffH; + VstSpeakerArrangementType speakerIn, speakerOut; + int numInChans, numOutChans; + HeapBlock channels; + Array tempChannels; // see note in processReplacing() + bool hasCreatedTempChannels; + bool shouldDeleteEditor; + + //============================================================================== +#if JUCE_WINDOWS // Workarounds for Wavelab's happy-go-lucky use of threads. + class NonWavelabMMLock + { + public: + NonWavelabMMLock() : mm (getHostType().isWavelab() || getHostType().isCubaseBridged() ? 0 : new MessageManagerLock()) {} + ~NonWavelabMMLock() {} + + private: + ScopedPointer mm; + }; + + static void checkWhetherWavelabHasChangedThread() + { + MessageManager::getInstance()->setCurrentThreadAsMessageThread(); + } +#else + typedef MessageManagerLock NonWavelabMMLock; + static void checkWhetherWavelabHasChangedThread() {} +#endif + + //============================================================================== + void deleteTempChannels() + { + for (int i = tempChannels.size(); --i >= 0;) + juce_free (tempChannels.getUnchecked(i)); + + tempChannels.clear(); + + if (filter != 0) + tempChannels.insertMultiple (0, 0, filter->getNumInputChannels() + filter->getNumOutputChannels()); + + hasCreatedTempChannels = false; + } + + const String getHostName() + { + char host[256]; + zeromem (host, sizeof (host)); + getHostProductString (host); + return host; + } + +#if JUCE_MAC + void* hostWindow; +#elif JUCE_LINUX + Window hostWindow; +#else + HWND hostWindow; +#endif +}; + +//============================================================================== +/** Somewhere in the codebase of your plugin, you need to implement this function + and make it create an instance of the filter subclass that you're building. +*/ +extern AudioProcessor* JUCE_CALLTYPE createPluginFilter(); + + +//============================================================================== +static AEffect* pluginEntryPoint (audioMasterCallback audioMaster) +{ + initialiseJuce_GUI(); + + try + { + if (audioMaster (0, audioMasterVersion, 0, 0, 0, 0) != 0) + { + AudioProcessor* const filter = createPluginFilter(); + + if (filter != 0) + { + JuceVSTWrapper* const wrapper = new JuceVSTWrapper (audioMaster, filter); + return wrapper->getAeffect(); + } + } + } + catch (...) + {} + + return 0; +} + + +//============================================================================== +// Mac startup code.. +#if JUCE_MAC + +extern "C" __attribute__ ((visibility("default"))) AEffect* VSTPluginMain (audioMasterCallback audioMaster) +{ + initialiseMac(); + return pluginEntryPoint (audioMaster); +} + +extern "C" __attribute__ ((visibility("default"))) AEffect* main_macho (audioMasterCallback audioMaster) +{ + initialiseMac(); + return pluginEntryPoint (audioMaster); +} + +//============================================================================== +// Linux startup code.. +#elif JUCE_LINUX + +extern "C" AEffect* VSTPluginMain (audioMasterCallback audioMaster) +{ + SharedMessageThread::getInstance(); + + return pluginEntryPoint (audioMaster); +} + +extern "C" __attribute__ ((visibility("default"))) AEffect* main_plugin (audioMasterCallback audioMaster) asm ("main"); + +extern "C" __attribute__ ((visibility("default"))) AEffect* main_plugin (audioMasterCallback audioMaster) +{ + return VSTPluginMain (audioMaster); +} + +__attribute__((constructor)) void myPluginInit() +{ + // don't put initialiseJuce_GUI here... it will crash ! +} + +__attribute__((destructor)) void myPluginFini() +{ + // don't put shutdownJuce_GUI here... it will crash ! +} + +//============================================================================== +// Win32 startup code.. +#else + +extern "C" __declspec (dllexport) AEffect* VSTPluginMain (audioMasterCallback audioMaster) +{ + return pluginEntryPoint (audioMaster); +} + +#ifndef _WIN64 // (can't compile this on win64, but it's not needed anyway with VST2.4) +extern "C" __declspec (dllexport) void* main (audioMasterCallback audioMaster) +{ + return (void*) pluginEntryPoint (audioMaster); +} +#endif + +#if JucePlugin_Build_RTAS +BOOL WINAPI DllMainVST (HINSTANCE instance, DWORD dwReason, LPVOID) +#else +extern "C" BOOL WINAPI DllMain (HINSTANCE instance, DWORD dwReason, LPVOID) +#endif +{ + if (dwReason == DLL_PROCESS_ATTACH) + PlatformUtilities::setCurrentModuleInstanceHandle (instance); + + return TRUE; +} + +#endif + +#endif diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 0125d1cc3a..52f96a6b5b 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -25683,6 +25683,8 @@ private: void endModal (Component* component, int returnValue); void endModal (Component* component); + ModalComponentManager (const ModalComponentManager&); + ModalComponentManager& operator= (const ModalComponentManager&); }; #endif // __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__ @@ -26210,8 +26212,8 @@ public: /** Adds a child component to this one, and also makes the child visible if it isn't. - Quite a useful function, this is just the same as calling addChildComponent() - followed by setVisible (true) on the child. See addChildComponent() for more details. + Quite a useful function, this is just the same as calling setVisible (true) on the child + and then addChildComponent(). See addChildComponent() for more details. */ void addAndMakeVisible (Component* child, int zOrder = -1); diff --git a/src/gui/components/juce_Component.h b/src/gui/components/juce_Component.h index 185ca66cd0..fc73b50720 100644 --- a/src/gui/components/juce_Component.h +++ b/src/gui/components/juce_Component.h @@ -577,8 +577,8 @@ public: /** Adds a child component to this one, and also makes the child visible if it isn't. - Quite a useful function, this is just the same as calling addChildComponent() - followed by setVisible (true) on the child. See addChildComponent() for more details. + Quite a useful function, this is just the same as calling setVisible (true) on the child + and then addChildComponent(). See addChildComponent() for more details. */ void addAndMakeVisible (Component* child, int zOrder = -1); diff --git a/src/gui/components/juce_ModalComponentManager.h b/src/gui/components/juce_ModalComponentManager.h index 8baf256d9a..44754bebcb 100644 --- a/src/gui/components/juce_ModalComponentManager.h +++ b/src/gui/components/juce_ModalComponentManager.h @@ -138,6 +138,8 @@ private: void endModal (Component* component, int returnValue); void endModal (Component* component); + ModalComponentManager (const ModalComponentManager&); + ModalComponentManager& operator= (const ModalComponentManager&); };