diff --git a/JuceLibraryCode/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/JuceLibraryCode/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp new file mode 100644 index 0000000..ba36794 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -0,0 +1,1617 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found 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.juce.com for more information. + + ============================================================================== +*/ + +// Your project must contain an AppConfig.h file with your project-specific settings in it, +// and your header search path must make it accessible to the module's files. +#include "AppConfig.h" + +//============================================================================== +#if JucePlugin_Build_VST3 && (__APPLE_CPP__ || __APPLE_CC__ || _WIN32 || _WIN64) + +#include "../../juce_audio_processors/format_types/juce_VST3Headers.h" +#include "../utility/juce_CheckSettingMacros.h" +#include "../utility/juce_IncludeModuleHeaders.h" +#include "../../juce_audio_processors/format_types/juce_VST3Common.h" + +#undef Point +#undef Component + +using namespace Steinberg; + +//============================================================================== +class JuceLibraryRefCount +{ +public: + JuceLibraryRefCount() { if ((getCount()++) == 0) initialiseJuce_GUI(); } + ~JuceLibraryRefCount() { if ((--getCount()) == 0) shutdownJuce_GUI(); } + +private: + int& getCount() noexcept + { + static int count = 0; + return count; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLibraryRefCount) +}; + +//============================================================================== +namespace juce +{ + #if JUCE_MAC + extern void initialiseMac(); + extern void* attachComponentToWindowRef (Component*, void* parent, bool isNSView); + extern void detachComponentFromWindowRef (Component*, void* window, bool isNSView); + extern void setNativeHostWindowSize (void* window, Component*, int newWidth, int newHeight, bool isNSView); + #endif +} + +//============================================================================== +class JuceAudioProcessor : public FUnknown +{ +public: + JuceAudioProcessor (AudioProcessor* source) noexcept + : refCount (0), audioProcessor (source) {} + + virtual ~JuceAudioProcessor() {} + + AudioProcessor* get() const noexcept { return audioProcessor; } + + JUCE_DECLARE_VST3_COM_QUERY_METHODS + JUCE_DECLARE_VST3_COM_REF_METHODS + + static const FUID iid; + +private: + Atomic refCount; + ScopedPointer audioProcessor; + + JuceAudioProcessor() JUCE_DELETED_FUNCTION; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceAudioProcessor) +}; + +#define TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID(CommonClassType, SourceClassType) \ + if (doUIDsMatch (iid, CommonClassType::iid)) \ + { \ + addRef(); \ + *obj = (CommonClassType*) static_cast (this); \ + return Steinberg::kResultOk; \ + } + +//============================================================================== +class JuceVST3EditController : public Vst::EditController, + public Vst::IMidiMapping, + public AudioProcessorListener +{ +public: + JuceVST3EditController (Vst::IHostApplication* host) + { + if (host != nullptr) + host->queryInterface (FUnknown::iid, (void**) &hostContext); + } + + //============================================================================== + static const FUID iid; + + //============================================================================== + REFCOUNT_METHODS (ComponentBase) + + tresult PLUGIN_API queryInterface (const TUID iid, void** obj) override + { + TEST_FOR_AND_RETURN_IF_VALID (FObject) + TEST_FOR_AND_RETURN_IF_VALID (JuceVST3EditController) + TEST_FOR_AND_RETURN_IF_VALID (Vst::IEditController) + TEST_FOR_AND_RETURN_IF_VALID (Vst::IEditController2) + TEST_FOR_AND_RETURN_IF_VALID (Vst::IConnectionPoint) + TEST_FOR_AND_RETURN_IF_VALID (Vst::IMidiMapping) + TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (IPluginBase, Vst::IEditController) + TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (IDependent, Vst::IEditController) + TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (FUnknown, Vst::IEditController) + + if (doUIDsMatch (iid, JuceAudioProcessor::iid)) + { + audioProcessor->addRef(); + *obj = audioProcessor; + return kResultOk; + } + + *obj = nullptr; + return kNoInterface; + } + + //============================================================================== + tresult PLUGIN_API initialize (FUnknown* context) override + { + if (hostContext != context) + { + if (hostContext != nullptr) + hostContext->release(); + + hostContext = context; + + if (hostContext != nullptr) + hostContext->addRef(); + } + + return kResultTrue; + } + + tresult PLUGIN_API terminate() override + { + if (AudioProcessor* const pluginInstance = getPluginInstance()) + pluginInstance->removeListener (this); + + audioProcessor = nullptr; + + return EditController::terminate(); + } + + //============================================================================== + struct Param : public Vst::Parameter + { + Param (AudioProcessor& p, int index) : owner (p), paramIndex (index) + { + info.id = (Vst::ParamID) index; + toString128 (info.title, p.getParameterName (index)); + toString128 (info.shortTitle, p.getParameterName (index, 8)); + toString128 (info.units, p.getParameterLabel (index)); + + const int numSteps = p.getParameterNumSteps (index); + info.stepCount = (Steinberg::int32) (numSteps > 0 && numSteps < 0x7fffffff ? numSteps - 1 : 0); + info.defaultNormalizedValue = p.getParameterDefaultValue (index); + info.unitId = Vst::kRootUnitId; + info.flags = p.isParameterAutomatable (index) ? Vst::ParameterInfo::kCanAutomate : 0; + } + + virtual ~Param() {} + + bool setNormalized (Vst::ParamValue v) override + { + v = jlimit (0.0, 1.0, v); + + if (v != valueNormalized) + { + valueNormalized = v; + changed(); + owner.setParameter (paramIndex, (float) v); + return true; + } + + return false; + } + + void toString (Vst::ParamValue, Vst::String128 result) const override + { + toString128 (result, owner.getParameterText (paramIndex, 128)); + } + + Vst::ParamValue toPlain (Vst::ParamValue v) const override { return v; } + Vst::ParamValue toNormalized (Vst::ParamValue v) const override { return v; } + + private: + AudioProcessor& owner; + int paramIndex; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Param) + }; + + //============================================================================== + void setAudioProcessor (JuceAudioProcessor* audioProc) + { + if (audioProcessor != audioProc) + { + audioProcessor = audioProc; + setupParameters(); + } + } + + tresult PLUGIN_API connect (IConnectionPoint* other) override + { + if (other != nullptr && audioProcessor == nullptr) + { + const tresult result = ComponentBase::connect (other); + + if (! audioProcessor.loadFrom (other)) + sendIntMessage ("JuceVST3EditController", (Steinberg::int64) (pointer_sized_int) this); + else + setupParameters(); + + return result; + } + + jassertfalse; + return kResultFalse; + } + + //============================================================================== + tresult PLUGIN_API getMidiControllerAssignment (Steinberg::int32, Steinberg::int16, + Vst::CtrlNumber, + Vst::ParamID& id) override + { + //TODO + id = 0; + return kNotImplemented; + } + + //============================================================================== + IPlugView* PLUGIN_API createView (const char* name) override + { + if (AudioProcessor* const pluginInstance = getPluginInstance()) + { + if (pluginInstance->hasEditor() && name != nullptr + && strcmp (name, Vst::ViewType::kEditor) == 0) + { + return new JuceVST3Editor (*this, *pluginInstance); + } + } + + return nullptr; + } + + //============================================================================== + void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index) override { beginEdit ((Steinberg::uint32) index); } + void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue) override { performEdit ((Steinberg::uint32) index, (double) newValue); } + void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index) override { endEdit ((Steinberg::uint32) index); } + + void audioProcessorChanged (AudioProcessor*) override + { + if (componentHandler != nullptr) + componentHandler->restartComponent (Vst::kLatencyChanged & Vst::kParamValuesChanged); + } + + //============================================================================== + AudioProcessor* getPluginInstance() const noexcept + { + if (audioProcessor != nullptr) + return audioProcessor->get(); + + return nullptr; + } + +private: + //============================================================================== + ComSmartPtr audioProcessor; + const JuceLibraryRefCount juceCount; + + //============================================================================== + void setupParameters() + { + if (AudioProcessor* const pluginInstance = getPluginInstance()) + { + pluginInstance->addListener (this); + + if (parameters.getParameterCount() <= 0) + for (int i = 0; i < pluginInstance->getNumParameters(); ++i) + parameters.addParameter (new Param (*pluginInstance, i)); + + audioProcessorChanged (pluginInstance); + } + } + + void sendIntMessage (const char* idTag, const Steinberg::int64 value) + { + jassert (hostContext != nullptr); + + if (Vst::IMessage* message = allocateMessage()) + { + const FReleaser releaser (message); + message->setMessageID (idTag); + message->getAttributes()->setInt (idTag, value); + sendMessage (message); + } + } + + //============================================================================== + class JuceVST3Editor : public Vst::EditorView + { + public: + JuceVST3Editor (JuceVST3EditController& ec, AudioProcessor& p) + : Vst::EditorView (&ec, nullptr), + owner (&ec), pluginInstance (p) + { + #if JUCE_MAC + macHostWindow = nullptr; + isNSView = false; + #endif + + component = new ContentWrapperComponent (*this, p); + } + + //============================================================================== + tresult PLUGIN_API isPlatformTypeSupported (FIDString type) override + { + if (type != nullptr && pluginInstance.hasEditor()) + { + #if JUCE_WINDOWS + if (strcmp (type, kPlatformTypeHWND) == 0) + #else + if (strcmp (type, kPlatformTypeNSView) == 0 || strcmp (type, kPlatformTypeHIView) == 0) + #endif + return kResultTrue; + } + + return kResultFalse; + } + + tresult PLUGIN_API attached (void* parent, FIDString type) override + { + if (parent == nullptr || isPlatformTypeSupported (type) == kResultFalse) + return kResultFalse; + + #if JUCE_WINDOWS + component->addToDesktop (0, parent); + component->setOpaque (true); + component->setVisible (true); + #else + isNSView = (strcmp (type, kPlatformTypeNSView) == 0); + macHostWindow = juce::attachComponentToWindowRef (component, parent, isNSView); + #endif + + component->resizeHostWindow(); + systemWindow = parent; + attachedToParent(); + + return kResultTrue; + } + + tresult PLUGIN_API removed() override + { + if (component != nullptr) + { + #if JUCE_WINDOWS + component->removeFromDesktop(); + #else + if (macHostWindow != nullptr) + { + juce::detachComponentFromWindowRef (component, macHostWindow, isNSView); + macHostWindow = nullptr; + } + #endif + + component = nullptr; + } + + return CPluginView::removed(); + } + + tresult PLUGIN_API onSize (ViewRect* newSize) override + { + if (newSize != nullptr) + { + rect = *newSize; + + if (component != nullptr) + component->setSize (rect.getWidth(), rect.getHeight()); + + return kResultTrue; + } + + jassertfalse; + return kResultFalse; + } + + tresult PLUGIN_API getSize (ViewRect* size) override + { + if (size != nullptr && component != nullptr) + { + *size = ViewRect (0, 0, component->getWidth(), component->getHeight()); + return kResultTrue; + } + + return kResultFalse; + } + + tresult PLUGIN_API canResize() override { return kResultTrue; } + + tresult PLUGIN_API checkSizeConstraint (ViewRect* rect) override + { + if (rect != nullptr && component != nullptr) + { + rect->right = rect->left + component->getWidth(); + rect->bottom = rect->top + component->getHeight(); + return kResultTrue; + } + + jassertfalse; + return kResultFalse; + } + + private: + //============================================================================== + class ContentWrapperComponent : public juce::Component + { + public: + ContentWrapperComponent (JuceVST3Editor& editor, AudioProcessor& plugin) + : owner (editor), + pluginEditor (plugin.createEditorIfNeeded()) + { + setOpaque (true); + setBroughtToFrontOnMouseClick (true); + + // if hasEditor() returns true then createEditorIfNeeded has to return a valid editor + jassert (pluginEditor != nullptr); + + if (pluginEditor != nullptr) + { + addAndMakeVisible (pluginEditor); + setBounds (pluginEditor->getLocalBounds()); + resizeHostWindow(); + } + } + + ~ContentWrapperComponent() + { + if (pluginEditor != nullptr) + { + PopupMenu::dismissAllActiveMenus(); + pluginEditor->getAudioProcessor()->editorBeingDeleted (pluginEditor); + } + } + + void paint (Graphics& g) override + { + g.fillAll (Colours::black); + } + + void childBoundsChanged (Component*) override + { + resizeHostWindow(); + } + + void resized() override + { + if (pluginEditor != nullptr) + pluginEditor->setBounds (getLocalBounds()); + } + + void resizeHostWindow() + { + if (pluginEditor != nullptr) + { + const int w = pluginEditor->getWidth(); + const int h = pluginEditor->getHeight(); + + #if JUCE_WINDOWS + setSize (w, h); + #else + if (owner.macHostWindow != nullptr) + juce::setNativeHostWindowSize (owner.macHostWindow, this, w, h, owner.isNSView); + #endif + + if (owner.plugFrame != nullptr) + { + ViewRect newSize (0, 0, w, h); + owner.plugFrame->resizeView (&owner, &newSize); + } + } + } + + private: + JuceVST3Editor& owner; + ScopedPointer pluginEditor; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent) + }; + + //============================================================================== + ComSmartPtr owner; + AudioProcessor& pluginInstance; + + ScopedPointer component; + friend class ContentWrapperComponent; + + #if JUCE_MAC + void* macHostWindow; + bool isNSView; + #endif + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVST3Editor) + }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVST3EditController) +}; + +//============================================================================== +class JuceVST3Component : public Vst::IComponent, + public Vst::IAudioProcessor, + public Vst::IUnitInfo, + public Vst::IConnectionPoint, + public AudioPlayHead +{ +public: + JuceVST3Component (Vst::IHostApplication* h) + : refCount (1), + host (h), + audioInputs (Vst::kAudio, Vst::kInput), + audioOutputs (Vst::kAudio, Vst::kOutput), + eventInputs (Vst::kEvent, Vst::kInput), + eventOutputs (Vst::kEvent, Vst::kOutput) + { + pluginInstance = createPluginFilterOfType (AudioProcessor::wrapperType_VST3); + comPluginInstance = new JuceAudioProcessor (pluginInstance); + + zerostruct (processContext); + + processSetup.maxSamplesPerBlock = 1024; + processSetup.processMode = Vst::kRealtime; + processSetup.sampleRate = 44100.0; + processSetup.symbolicSampleSize = Vst::kSample32; + } + + ~JuceVST3Component() + { + if (pluginInstance != nullptr) + if (pluginInstance->getPlayHead() == this) + pluginInstance->setPlayHead (nullptr); + + audioInputs.removeAll(); + audioOutputs.removeAll(); + eventInputs.removeAll(); + eventOutputs.removeAll(); + } + + //============================================================================== + AudioProcessor& getPluginInstance() const noexcept { return *pluginInstance; } + + //============================================================================== + static const FUID iid; + + JUCE_DECLARE_VST3_COM_REF_METHODS + + tresult PLUGIN_API queryInterface (const TUID iid, void** obj) override + { + TEST_FOR_AND_RETURN_IF_VALID (IPluginBase) + TEST_FOR_AND_RETURN_IF_VALID (JuceVST3Component) + TEST_FOR_AND_RETURN_IF_VALID (Vst::IComponent) + TEST_FOR_AND_RETURN_IF_VALID (Vst::IAudioProcessor) + TEST_FOR_AND_RETURN_IF_VALID (Vst::IUnitInfo) + TEST_FOR_AND_RETURN_IF_VALID (Vst::IConnectionPoint) + TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (FUnknown, Vst::IComponent) + + if (doUIDsMatch (iid, JuceAudioProcessor::iid)) + { + comPluginInstance->addRef(); + *obj = comPluginInstance; + return kResultOk; + } + + *obj = nullptr; + return kNoInterface; + } + + //============================================================================== + tresult PLUGIN_API initialize (FUnknown* hostContext) override + { + if (host != hostContext) + host.loadFrom (hostContext); + + #if JucePlugin_MaxNumInputChannels > 0 + addAudioBusTo (audioInputs, TRANS("Audio Input"), + getArrangementForNumChannels (JucePlugin_MaxNumInputChannels)); + #endif + + #if JucePlugin_MaxNumOutputChannels > 0 + addAudioBusTo (audioOutputs, TRANS("Audio Output"), + getArrangementForNumChannels (JucePlugin_MaxNumOutputChannels)); + #endif + + #if JucePlugin_WantsMidiInput + addEventBusTo (eventInputs, TRANS("MIDI Input")); + #endif + + #if JucePlugin_ProducesMidiOutput + addEventBusTo (eventOutputs, TRANS("MIDI Output")); + #endif + + processContext.sampleRate = processSetup.sampleRate; + + preparePlugin (processSetup.sampleRate, (int) processSetup.maxSamplesPerBlock); + + return kResultTrue; + } + + tresult PLUGIN_API terminate() override + { + getPluginInstance().releaseResources(); + return kResultTrue; + } + + //============================================================================== + tresult PLUGIN_API connect (IConnectionPoint* other) override + { + if (other != nullptr && juceVST3EditController == nullptr) + juceVST3EditController.loadFrom (other); + + return kResultTrue; + } + + tresult PLUGIN_API disconnect (IConnectionPoint*) override + { + juceVST3EditController = nullptr; + return kResultTrue; + } + + tresult PLUGIN_API notify (Vst::IMessage* message) override + { + if (message != nullptr && juceVST3EditController == nullptr) + { + Steinberg::int64 value = 0; + + if (message->getAttributes()->getInt ("JuceVST3EditController", value) == kResultTrue) + { + juceVST3EditController = (JuceVST3EditController*) (pointer_sized_int) value; + + if (juceVST3EditController != nullptr) + juceVST3EditController->setAudioProcessor (comPluginInstance); + else + jassertfalse; + } + } + + return kResultTrue; + } + + //============================================================================== + tresult PLUGIN_API getControllerClassId (TUID classID) override + { + memcpy (classID, JuceVST3EditController::iid, sizeof (TUID)); + return kResultTrue; + } + + Steinberg::int32 PLUGIN_API getBusCount (Vst::MediaType type, Vst::BusDirection dir) override + { + if (Vst::BusList* const busList = getBusListFor (type, dir)) + return busList->total(); + + return 0; + } + + tresult PLUGIN_API getBusInfo (Vst::MediaType type, Vst::BusDirection dir, + Steinberg::int32 index, Vst::BusInfo& info) override + { + if (Vst::BusList* const busList = getBusListFor (type, dir)) + { + if (Vst::Bus* const bus = (Vst::Bus*) busList->at (index)) + { + info.mediaType = type; + info.direction = dir; + + if (bus->getInfo (info)) + return kResultTrue; + } + } + + zerostruct (info); + return kResultFalse; + } + + tresult PLUGIN_API activateBus (Vst::MediaType type, Vst::BusDirection dir, + Steinberg::int32 index, TBool state) override + { + if (Vst::BusList* const busList = getBusListFor (type, dir)) + { + if (Vst::Bus* const bus = (Vst::Bus*) busList->at (index)) + { + bus->setActive (state); + return kResultTrue; + } + } + + jassertfalse; + return kResultFalse; + } + + tresult PLUGIN_API setActive (TBool state) override + { + if (state == kResultFalse) + { + getPluginInstance().releaseResources(); + } + else + { + double sampleRate = getPluginInstance().getSampleRate(); + int bufferSize = getPluginInstance().getBlockSize(); + + sampleRate = processSetup.sampleRate > 0.0 + ? processSetup.sampleRate + : sampleRate; + + bufferSize = processSetup.maxSamplesPerBlock > 0 + ? (int) processSetup.maxSamplesPerBlock + : bufferSize; + + channelList.clear(); + channelList.insertMultiple (0, nullptr, jmax (JucePlugin_MaxNumInputChannels, JucePlugin_MaxNumOutputChannels) + 1); + + preparePlugin (sampleRate, bufferSize); + } + + return kResultOk; + } + + tresult PLUGIN_API setIoMode (Vst::IoMode) override { return kNotImplemented; } + tresult PLUGIN_API getRoutingInfo (Vst::RoutingInfo&, Vst::RoutingInfo&) override { return kNotImplemented; } + + tresult PLUGIN_API setState (IBStream* state) override + { + if (state != nullptr) + { + // Reset to the beginning of the stream: + if (state->seek (0, IBStream::kIBSeekSet, nullptr) != kResultTrue) + return kResultFalse; + + Steinberg::int64 end = -1; + + if (end < 0) + { + FUnknownPtr s (state); + + if (s != nullptr) + s->getStreamSize (end); + } + + if (end < 0) + { + FUnknownPtr s (state); + + if (s != nullptr) + { + if (getHostType().isAdobeAudition()) + { + // Adobe Audition CS6 hack to avoid trying to use corrupted streams: + bool failed = true; + + if (const char* const data = s->getData()) + { + if (s->getSize() >= 5 && data[0] != 'V' && data[1] != 'C' + && data[2] != '2' && data[3] != '!' && data[4] != 'E') + { + failed = false; + } + } + else + { + jassertfalse; + } + + if (failed) + return kResultFalse; + } + + end = (Steinberg::int64) s->getSize(); + } + } + + if (end <= 0) + return kResultFalse; + + // Try reading the data, and setting the plugin state: + Steinberg::int32 numBytes = (Steinberg::int32) jmin ((Steinberg::int64) std::numeric_limits::max(), end); + + Array buff; + buff.ensureStorageAllocated ((int) numBytes); + void* buffer = buff.getRawDataPointer(); + + if (state->read (buffer, numBytes, &numBytes) == kResultTrue + && buffer != nullptr + && numBytes > 0) + { + pluginInstance->setStateInformation (buffer, (int) numBytes); + return kResultTrue; + } + + return kResultFalse; + } + + return kInvalidArgument; + } + + tresult PLUGIN_API getState (IBStream* state) override + { + if (state != nullptr) + { + juce::MemoryBlock mem; + pluginInstance->getStateInformation (mem); + return state->write (mem.getData(), (Steinberg::int32) mem.getSize()); + } + + return kInvalidArgument; + } + + //============================================================================== + Steinberg::int32 PLUGIN_API getUnitCount() override + { + return 1; + } + + tresult PLUGIN_API getUnitInfo (Steinberg::int32 unitIndex, Vst::UnitInfo& info) override + { + if (unitIndex == 0) + { + info.id = Vst::kRootUnitId; + info.parentUnitId = Vst::kNoParentUnitId; + info.programListId = Vst::kNoProgramListId; + + toString128 (info.name, TRANS("Root Unit")); + + return kResultTrue; + } + + zerostruct (info); + return kResultFalse; + } + + Steinberg::int32 PLUGIN_API getProgramListCount() override + { + if (getPluginInstance().getNumPrograms() > 0) + return 1; + + return 0; + } + + tresult PLUGIN_API getProgramListInfo (Steinberg::int32 listIndex, Vst::ProgramListInfo& info) override + { + if (listIndex == 0) + { + info.id = paramPreset; + info.programCount = (Steinberg::int32) getPluginInstance().getNumPrograms(); + + toString128 (info.name, TRANS("Factory Presets")); + + return kResultTrue; + } + + jassertfalse; + zerostruct (info); + return kResultFalse; + } + + tresult PLUGIN_API getProgramName (Vst::ProgramListID listId, Steinberg::int32 programIndex, Vst::String128 name) override + { + if (listId == paramPreset + && isPositiveAndBelow ((int) programIndex, getPluginInstance().getNumPrograms())) + { + toString128 (name, getPluginInstance().getProgramName ((int) programIndex)); + return kResultTrue; + } + + jassertfalse; + toString128 (name, juce::String()); + return kResultFalse; + } + + tresult PLUGIN_API getProgramInfo (Vst::ProgramListID, Steinberg::int32, Vst::CString, Vst::String128) override { return kNotImplemented; } + tresult PLUGIN_API hasProgramPitchNames (Vst::ProgramListID, Steinberg::int32) override { return kNotImplemented; } + tresult PLUGIN_API getProgramPitchName (Vst::ProgramListID, Steinberg::int32, Steinberg::int16, Vst::String128) override { return kNotImplemented; } + tresult PLUGIN_API selectUnit (Vst::UnitID) override { return kNotImplemented; } + tresult PLUGIN_API setUnitProgramData (Steinberg::int32, Steinberg::int32, IBStream*) override { return kNotImplemented; } + Vst::UnitID PLUGIN_API getSelectedUnit() override { return Vst::kRootUnitId; } + + tresult PLUGIN_API getUnitByBus (Vst::MediaType, Vst::BusDirection, + Steinberg::int32, Steinberg::int32, + Vst::UnitID& unitId) override + { + zerostruct (unitId); + return kNotImplemented; + } + + //============================================================================== + bool getCurrentPosition (CurrentPositionInfo& info) override + { + info.timeInSamples = jmax ((juce::int64) 0, processContext.projectTimeSamples); + info.timeInSeconds = processContext.projectTimeMusic; + info.bpm = jmax (1.0, processContext.tempo); + info.timeSigNumerator = jmax (1, (int) processContext.timeSigNumerator); + info.timeSigDenominator = jmax (1, (int) processContext.timeSigDenominator); + info.ppqPositionOfLastBarStart = processContext.barPositionMusic; + info.ppqPosition = processContext.projectTimeMusic; + info.ppqLoopStart = processContext.cycleStartMusic; + info.ppqLoopEnd = processContext.cycleEndMusic; + info.isRecording = (processContext.state & Vst::ProcessContext::kRecording) != 0; + info.isPlaying = (processContext.state & Vst::ProcessContext::kPlaying) != 0; + info.isLooping = (processContext.state & Vst::ProcessContext::kCycleActive) != 0; + info.editOriginTime = 0.0; + info.frameRate = AudioPlayHead::fpsUnknown; + + if ((processContext.state & Vst::ProcessContext::kSmpteValid) != 0) + { + switch (processContext.frameRate.framesPerSecond) + { + case 24: info.frameRate = AudioPlayHead::fps24; break; + case 25: info.frameRate = AudioPlayHead::fps25; break; + case 29: info.frameRate = AudioPlayHead::fps30drop; break; + + case 30: + { + if ((processContext.frameRate.flags & Vst::FrameRate::kDropRate) != 0) + info.frameRate = AudioPlayHead::fps30drop; + else + info.frameRate = AudioPlayHead::fps30; + } + break; + + default: break; + } + } + + return true; + } + + //============================================================================== + static tresult setBusArrangementFor (Vst::BusList& list, + Vst::SpeakerArrangement* arrangement, + Steinberg::int32 numBusses) + { + if (arrangement != nullptr && numBusses == 1) //Should only be 1 bus per BusList + { + Steinberg::int32 counter = 0; + + FOREACH_CAST (IPtr, Vst::AudioBus, bus, list) + if (counter < numBusses) + bus->setArrangement (arrangement[counter]); + + counter++; + ENDFOR + + return kResultTrue; + } + + return kResultFalse; + } + + tresult PLUGIN_API setBusArrangements (Vst::SpeakerArrangement* inputs, Steinberg::int32 numIns, + Vst::SpeakerArrangement* outputs, Steinberg::int32 numOuts) override + { + #if JucePlugin_MaxNumInputChannels > 0 + if (setBusArrangementFor (audioInputs, inputs, numIns) != kResultTrue) + return kResultFalse; + #else + if (numIns != 0) + return kResultFalse; + #endif + + #if JucePlugin_MaxNumOutputChannels > 0 + if (setBusArrangementFor (audioOutputs, outputs, numOuts) != kResultTrue) + return kResultFalse; + #else + if (numOuts != 0) + return kResultFalse; + #endif + + return kResultTrue; + } + + tresult PLUGIN_API getBusArrangement (Vst::BusDirection dir, Steinberg::int32 index, Vst::SpeakerArrangement& arr) override + { + if (Vst::BusList* const busList = getBusListFor (Vst::kAudio, dir)) + { + if (Vst::AudioBus* const audioBus = FCast (busList->at (index))) + { + arr = audioBus->getArrangement(); + return kResultTrue; + } + } + + return kResultFalse; + } + + tresult PLUGIN_API canProcessSampleSize (Steinberg::int32 symbolicSampleSize) override + { + return symbolicSampleSize == Vst::kSample32 ? kResultTrue : kResultFalse; + } + + Steinberg::uint32 PLUGIN_API getLatencySamples() override + { + return (Steinberg::uint32) jmax (0, getPluginInstance().getLatencySamples()); + } + + tresult PLUGIN_API setupProcessing (Vst::ProcessSetup& newSetup) override + { + if (canProcessSampleSize (newSetup.symbolicSampleSize) != kResultTrue) + return kResultFalse; + + processSetup = newSetup; + processContext.sampleRate = processSetup.sampleRate; + + preparePlugin (processSetup.sampleRate, processSetup.maxSamplesPerBlock); + + return kResultTrue; + } + + tresult PLUGIN_API setProcessing (TBool state) override + { + if (state == kResultFalse) + getPluginInstance().reset(); + + return kResultTrue; + } + + Steinberg::uint32 PLUGIN_API getTailSamples() override + { + const double tailLengthSeconds = getPluginInstance().getTailLengthSeconds(); + + if (tailLengthSeconds <= 0.0 || processSetup.sampleRate > 0.0) + return Vst::kNoTail; + + return (Steinberg::uint32) roundToIntAccurate (tailLengthSeconds * processSetup.sampleRate); + } + + //============================================================================== + void processParameterChanges (Vst::IParameterChanges& paramChanges) + { + jassert (pluginInstance != nullptr); + + const Steinberg::int32 numParamsChanged = paramChanges.getParameterCount(); + + for (Steinberg::int32 i = 0; i < numParamsChanged; ++i) + { + if (Vst::IParamValueQueue* paramQueue = paramChanges.getParameterData (i)) + { + const Steinberg::int32 numPoints = paramQueue->getPointCount(); + + Steinberg::int32 offsetSamples; + double value = 0.0; + + if (paramQueue->getPoint (numPoints - 1, offsetSamples, value) == kResultTrue) + { + const int id = (int) paramQueue->getParameterId(); + jassert (isPositiveAndBelow (id, pluginInstance->getNumParameters())); + pluginInstance->setParameter (id, (float) value); + } + } + } + } + + tresult PLUGIN_API process (Vst::ProcessData& data) override + { + if (pluginInstance == nullptr) + return kResultFalse; + + if (data.processContext != nullptr) + processContext = *data.processContext; + else + zerostruct (processContext); + + midiBuffer.clear(); + + #if JucePlugin_WantsMidiInput + if (data.inputEvents != nullptr) + MidiEventList::toMidiBuffer (midiBuffer, *data.inputEvents); + #endif + + #if JUCE_DEBUG && ! JucePlugin_ProducesMidiOutput + const int numMidiEventsComingIn = midiBuffer.getNumEvents(); + #endif + + const int numInputChans = data.inputs != nullptr ? (int) data.inputs[0].numChannels : 0; + const int numOutputChans = data.outputs != nullptr ? (int) data.outputs[0].numChannels : 0; + + int totalChans = 0; + + while (totalChans < numInputChans) + { + channelList.set (totalChans, data.inputs[0].channelBuffers32[totalChans]); + ++totalChans; + } + + while (totalChans < numOutputChans) + { + channelList.set (totalChans, data.outputs[0].channelBuffers32[totalChans]); + ++totalChans; + } + + AudioSampleBuffer buffer (channelList.getRawDataPointer(), totalChans, (int) data.numSamples); + + { + const ScopedLock sl (pluginInstance->getCallbackLock()); + + pluginInstance->setNonRealtime (data.processMode == Vst::kOffline); + + if (data.inputParameterChanges != nullptr) + processParameterChanges (*data.inputParameterChanges); + + if (pluginInstance->isSuspended()) + buffer.clear(); + else + pluginInstance->processBlock (buffer, midiBuffer); + } + + for (int i = 0; i < numOutputChans; ++i) + FloatVectorOperations::copy (data.outputs[0].channelBuffers32[i], buffer.getSampleData (i), (int) data.numSamples); + + // clear extra busses.. + if (data.outputs != nullptr) + for (int i = 1; i < data.numOutputs; ++i) + for (int f = 0; f < data.outputs[i].numChannels; ++f) + FloatVectorOperations::clear (data.outputs[i].channelBuffers32[f], (int) data.numSamples); + + #if JucePlugin_ProducesMidiOutput + if (data.outputEvents != nullptr) + MidiEventList::toEventList (*data.outputEvents, midiBuffer); + #elif JUCE_DEBUG + /* 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 (midiBuffer.getNumEvents() <= numMidiEventsComingIn); + #endif + + return kResultTrue; + } + +private: + //============================================================================== + Atomic refCount; + + AudioProcessor* pluginInstance; + ComSmartPtr host; + ComSmartPtr comPluginInstance; + ComSmartPtr juceVST3EditController; + + /** + Since VST3 does not provide a way of knowing the buffer size and sample rate at any point, + this object needs to be copied on every call to process() to be up-to-date... + */ + Vst::ProcessContext processContext; + Vst::ProcessSetup processSetup; + + Vst::BusList audioInputs, audioOutputs, eventInputs, eventOutputs; + MidiBuffer midiBuffer; + Array channelList; + + const JuceLibraryRefCount juceCount; + + //============================================================================== + void addBusTo (Vst::BusList& busList, Vst::Bus* newBus) + { + busList.append (IPtr (newBus, false)); + } + + void addAudioBusTo (Vst::BusList& busList, const juce::String& name, Vst::SpeakerArrangement arr) + { + addBusTo (busList, new Vst::AudioBus (toString (name), Vst::kMain, Vst::BusInfo::kDefaultActive, arr)); + } + + void addEventBusTo (Vst::BusList& busList, const juce::String& name) + { + addBusTo (busList, new Vst::EventBus (toString (name), 16, Vst::kMain, Vst::BusInfo::kDefaultActive)); + } + + Vst::BusList* getBusListFor (Vst::MediaType type, Vst::BusDirection dir) + { + if (type == Vst::kAudio) return dir == Vst::kInput ? &audioInputs : &audioOutputs; + if (type == Vst::kEvent) return dir == Vst::kInput ? &eventInputs : &eventOutputs; + + return nullptr; + } + + //============================================================================== + enum InternalParameters + { + paramPreset = 'prst' + }; + + void preparePlugin (double sampleRate, int bufferSize) + { + getPluginInstance().setPlayConfigDetails (JucePlugin_MaxNumInputChannels, + JucePlugin_MaxNumOutputChannels, + sampleRate, bufferSize); + + getPluginInstance().prepareToPlay (sampleRate, bufferSize); + } + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVST3Component) +}; + +//============================================================================== +#if JUCE_MSVC + #pragma warning (push, 0) + #pragma warning (disable: 4310) +#elif JUCE_CLANG + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-w" +#endif + +DECLARE_CLASS_IID (JuceAudioProcessor, 0x0101ABAB, 0xABCDEF01, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) +DEF_CLASS_IID (JuceAudioProcessor) + +DECLARE_CLASS_IID (JuceVST3Component, 0xABCDEF01, 0x9182FAEB, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) +DEF_CLASS_IID (JuceVST3Component) + +DECLARE_CLASS_IID (JuceVST3EditController, 0xABCDEF01, 0x1234ABCD, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) +DEF_CLASS_IID (JuceVST3EditController) + +#if JUCE_MSVC + #pragma warning (pop) +#elif JUCE_CLANG + #pragma clang diagnostic pop +#endif + +//============================================================================== +bool initModule() +{ + #if JUCE_MAC + initialiseMac(); + #endif + + return true; +} + +bool shutdownModule() +{ + return true; +} + +#undef JUCE_EXPORTED_FUNCTION + +#if JUCE_WINDOWS + extern "C" __declspec (dllexport) bool InitDll() { return initModule(); } + extern "C" __declspec (dllexport) bool ExitDll() { return shutdownModule(); } + #define JUCE_EXPORTED_FUNCTION + +#else + #define JUCE_EXPORTED_FUNCTION extern "C" __attribute__ ((visibility ("default"))) + + CFBundleRef globalBundleInstance = nullptr; + juce::uint32 numBundleRefs = 0; + juce::Array bundleRefs; + + enum { MaxPathLength = 2048 }; + char modulePath[MaxPathLength] = { 0 }; + void* moduleHandle = nullptr; + + JUCE_EXPORTED_FUNCTION bool bundleEntry (CFBundleRef ref) + { + if (ref != nullptr) + { + ++numBundleRefs; + CFRetain (ref); + + bundleRefs.add (ref); + + if (moduleHandle == nullptr) + { + globalBundleInstance = ref; + moduleHandle = ref; + + CFURLRef tempURL = CFBundleCopyBundleURL (ref); + CFURLGetFileSystemRepresentation (tempURL, true, (UInt8*) modulePath, MaxPathLength); + CFRelease (tempURL); + } + } + + return initModule(); + } + + JUCE_EXPORTED_FUNCTION bool bundleExit() + { + if (shutdownModule()) + { + if (--numBundleRefs == 0) + { + for (size_t i = 0; i < bundleRefs.size(); ++i) + CFRelease (bundleRefs.getUnchecked (i)); + + bundleRefs.clear(); + } + + return true; + } + + return false; + } +#endif + +//============================================================================== +/** This typedef represents VST3's createInstance() function signature */ +typedef FUnknown* (*CreateFunction) (Vst::IHostApplication*); + +static FUnknown* createComponentInstance (Vst::IHostApplication* host) +{ + return (Vst::IAudioProcessor*) new JuceVST3Component (host); +} + +static FUnknown* createControllerInstance (Vst::IHostApplication* host) +{ + return (Vst::IEditController*) new JuceVST3EditController (host); +} + +//============================================================================== +class JucePluginFactory; +JucePluginFactory* globalFactory = nullptr; + +//============================================================================== +class JucePluginFactory : public IPluginFactory3 +{ +public: + JucePluginFactory() + : refCount (1), + factoryInfo (JucePlugin_Manufacturer, JucePlugin_ManufacturerWebsite, + JucePlugin_ManufacturerEmail, Vst::kDefaultFactoryFlags) + { + } + + virtual ~JucePluginFactory() + { + if (globalFactory == this) + globalFactory = nullptr; + } + + //============================================================================== + bool registerClass (const PClassInfo2& info, CreateFunction createFunction) + { + if (createFunction == nullptr) + { + jassertfalse; + return false; + } + + ClassEntry* entry = classes.add (new ClassEntry (info, createFunction)); + entry->infoW.fromAscii (info); + + return true; + } + + bool isClassRegistered (const FUID& cid) const + { + for (int i = 0; i < classes.size(); ++i) + if (classes.getUnchecked (i)->infoW.cid == cid) + return true; + + return false; + } + + //============================================================================== + JUCE_DECLARE_VST3_COM_REF_METHODS + + tresult PLUGIN_API queryInterface (const TUID iid, void** obj) override + { + TEST_FOR_AND_RETURN_IF_VALID (IPluginFactory3) + TEST_FOR_AND_RETURN_IF_VALID (IPluginFactory2) + TEST_FOR_AND_RETURN_IF_VALID (IPluginFactory) + TEST_FOR_AND_RETURN_IF_VALID (FUnknown) + + jassertfalse; // Something new? + *obj = nullptr; + return kNotImplemented; + } + + //============================================================================== + Steinberg::int32 PLUGIN_API countClasses() override + { + return (Steinberg::int32) classes.size(); + } + + tresult PLUGIN_API getFactoryInfo (PFactoryInfo* info) override + { + if (info == nullptr) + return kInvalidArgument; + + memcpy (info, &factoryInfo, sizeof (PFactoryInfo)); + return kResultOk; + } + + tresult PLUGIN_API getClassInfo (Steinberg::int32 index, PClassInfo* info) override + { + return getPClassInfo (index, info); + } + + tresult PLUGIN_API getClassInfo2 (Steinberg::int32 index, PClassInfo2* info) override + { + return getPClassInfo (index, info); + } + + tresult PLUGIN_API getClassInfoUnicode (Steinberg::int32 index, PClassInfoW* info) override + { + if (info != nullptr) + { + if (ClassEntry* entry = classes[(int) index]) + { + memcpy (info, &entry->infoW, sizeof (PClassInfoW)); + return kResultOk; + } + } + + return kInvalidArgument; + } + + tresult PLUGIN_API createInstance (FIDString cid, FIDString sourceIid, void** obj) override + { + *obj = nullptr; + + FUID sourceFuid = sourceIid; + + if (cid == nullptr || sourceIid == nullptr || ! sourceFuid.isValid()) + { + jassertfalse; // The host you're running in has severe implementation issues! + return kInvalidArgument; + } + + TUID iidToQuery; + sourceFuid.toTUID (iidToQuery); + + for (int i = 0; i < classes.size(); ++i) + { + const ClassEntry& entry = *classes.getUnchecked (i); + + if (doUIDsMatch (entry.infoW.cid, cid)) + { + if (FUnknown* const instance = entry.createFunction (host)) + { + const FReleaser releaser (instance); + + if (instance->queryInterface (iidToQuery, obj) == kResultOk) + return kResultOk; + } + + break; + } + } + + return kNoInterface; + } + + tresult PLUGIN_API setHostContext (FUnknown* context) override + { + host.loadFrom (context); + + if (host != nullptr) + { + Vst::String128 name; + host->getName (name); + + return kResultTrue; + } + + return kNotImplemented; + } + +private: + //============================================================================== + const JuceLibraryRefCount juceCount; + Atomic refCount; + const PFactoryInfo factoryInfo; + ComSmartPtr host; + + //============================================================================== + struct ClassEntry + { + ClassEntry() noexcept : createFunction (nullptr), isUnicode (false) {} + + ClassEntry (const PClassInfo2& info, CreateFunction fn) noexcept + : info2 (info), createFunction (fn), isUnicode (false) {} + + PClassInfo2 info2; + PClassInfoW infoW; + CreateFunction createFunction; + bool isUnicode; + + private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ClassEntry) + }; + + OwnedArray classes; + + //============================================================================== + template + tresult PLUGIN_API getPClassInfo (Steinberg::int32 index, PClassInfoType* info) + { + if (info != nullptr) + { + zerostruct (*info); + + if (ClassEntry* entry = classes[(int) index]) + { + if (entry->isUnicode) + return kResultFalse; + + memcpy (info, &entry->info2, sizeof (PClassInfoType)); + return kResultOk; + } + } + + jassertfalse; + return kInvalidArgument; + } + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JucePluginFactory) +}; + +//============================================================================== +#ifndef JucePlugin_Vst3ComponentFlags + #if JucePlugin_IsSynth + #define JucePlugin_Vst3ComponentFlags Vst::kSimpleModeSupported + #else + #define JucePlugin_Vst3ComponentFlags 0 + #endif +#endif + +#ifndef JucePlugin_Vst3Category + #if JucePlugin_IsSynth + #define JucePlugin_Vst3Category Vst::PlugType::kInstrumentSynth + #else + #define JucePlugin_Vst3Category Vst::PlugType::kFx + #endif +#endif + +//============================================================================== +// The VST3 plugin entry point. +JUCE_EXPORTED_FUNCTION IPluginFactory* PLUGIN_API GetPluginFactory() +{ + #if JUCE_WINDOWS + // Cunning trick to force this function to be exported. Life's too short to + // faff around creating .def files for this kind of thing. + #pragma comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__) + #endif + + if (globalFactory == nullptr) + { + globalFactory = new JucePluginFactory(); + + static const PClassInfo2 componentClass (JuceVST3Component::iid, + PClassInfo::kManyInstances, + kVstAudioEffectClass, + JucePlugin_Name, + JucePlugin_Vst3ComponentFlags, + JucePlugin_Vst3Category, + JucePlugin_Manufacturer, + JucePlugin_VersionString, + kVstVersionString); + + globalFactory->registerClass (componentClass, createComponentInstance); + + static const PClassInfo2 controllerClass (JuceVST3EditController::iid, + PClassInfo::kManyInstances, + kVstComponentControllerClass, + JucePlugin_Name, + JucePlugin_Vst3ComponentFlags, + JucePlugin_Vst3Category, + JucePlugin_Manufacturer, + JucePlugin_VersionString, + kVstVersionString); + + globalFactory->registerClass (controllerClass, createControllerInstance); + } + else + { + globalFactory->addRef(); + } + + return dynamic_cast (globalFactory); +} + +#endif //JucePlugin_Build_VST3 diff --git a/JuceLibraryCode/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.mm b/JuceLibraryCode/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.mm new file mode 100644 index 0000000..9fdf505 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.mm @@ -0,0 +1,269 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found 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.juce.com for more information. + + ============================================================================== +*/ + +// Your project must contain an AppConfig.h file with your project-specific settings in it, +// and your header search path must make it accessible to the module's files. +#include "AppConfig.h" +#include "../utility/juce_CheckSettingMacros.h" + +#if JucePlugin_Build_VST3 + +#define JUCE_MAC_WINDOW_VISIBITY_BODGE 1 + +#include "../utility/juce_IncludeSystemHeaders.h" +#include "../utility/juce_IncludeModuleHeaders.h" +#include "../utility/juce_FakeMouseMoveGenerator.h" +#include "../utility/juce_CarbonVisibility.h" + +#undef Component +#undef Point + +//============================================================================== +namespace juce +{ + static void initialiseMac() + { + #if ! JUCE_64BIT + NSApplicationLoad(); + #endif + } + + #if ! JUCE_64BIT + static void updateComponentPos (Component* const comp) + { + DBG ("updateComponentPos()"); + + HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int) + comp->getProperties() ["dummyViewRef"].toString().getHexValue64(); + + HIRect r; + HIViewGetFrame (dummyView, &r); + HIViewRef root; + HIViewFindByID (HIViewGetRoot (HIViewGetWindow (dummyView)), kHIViewWindowContentID, &root); + HIViewConvertRect (&r, HIViewGetSuperview (dummyView), root); + + Rect windowPos; + GetWindowBounds (HIViewGetWindow (dummyView), kWindowContentRgn, &windowPos); + + comp->setTopLeftPosition ((int) (windowPos.left + r.origin.x), + (int) (windowPos.top + r.origin.y)); + } + + static pascal OSStatus viewBoundsChangedEvent (EventHandlerCallRef, EventRef, void* user) + { + updateComponentPos ((Component*) user); + return noErr; + } + #endif + + static void* attachComponentToWindowRef (Component* comp, void* windowRef, bool isHIView) + { + DBG ("attachComponentToWindowRef()"); + + JUCE_AUTORELEASEPOOL + { + #if JUCE_64BIT + NSView* parentView = (NSView*) windowRef; + + #if JucePlugin_EditorRequiresKeyboardFocus + comp->addToDesktop (0, parentView); + #else + comp->addToDesktop (ComponentPeer::windowIgnoresKeyPresses, parentView); + #endif + + // (this workaround is because Wavelab provides a zero-size parent view..) + if ([parentView frame].size.height == 0) + [((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint]; + + comp->setVisible (true); + comp->toFront (false); + + [[parentView window] setAcceptsMouseMovedEvents: YES]; + return parentView; + + #else + //treat NSView like 64bit + if (! isHIView) + { + NSView* parentView = (NSView*) windowRef; + + #if JucePlugin_EditorRequiresKeyboardFocus + comp->addToDesktop (0, parentView); + #else + comp->addToDesktop (ComponentPeer::windowIgnoresKeyPresses, parentView); + #endif + + // (this workaround is because Wavelab provides a zero-size parent view..) + if ([parentView frame].size.height == 0) + [((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint]; + + comp->setVisible (true); + comp->toFront (false); + + [[parentView window] setAcceptsMouseMovedEvents: YES]; + return parentView; + } + + NSWindow* hostWindow = [[NSWindow alloc] initWithWindowRef: windowRef]; + [hostWindow retain]; + [hostWindow setCanHide: YES]; + [hostWindow setReleasedWhenClosed: YES]; + + HIViewRef parentView = nullptr; + + WindowAttributes attributes; + GetWindowAttributes ((WindowRef) windowRef, &attributes); + + if ((attributes & kWindowCompositingAttribute) != 0) + { + HIViewRef root = HIViewGetRoot ((WindowRef) windowRef); + HIViewFindByID (root, kHIViewWindowContentID, &parentView); + + if (parentView == nullptr) + parentView = root; + } + else + { + GetRootControl ((WindowRef) windowRef, (ControlRef*) &parentView); + + if (parentView == nullptr) + CreateRootControl ((WindowRef) windowRef, (ControlRef*) &parentView); + } + + // It seems that the only way to successfully position our overlaid window is by putting a dummy + // HIView into the host's carbon window, and then catching events to see when it gets repositioned + HIViewRef dummyView = 0; + HIImageViewCreate (0, &dummyView); + HIRect r = { {0, 0}, { (float) comp->getWidth(), (float) comp->getHeight()} }; + HIViewSetFrame (dummyView, &r); + HIViewAddSubview (parentView, dummyView); + comp->getProperties().set ("dummyViewRef", String::toHexString ((pointer_sized_int) (void*) dummyView)); + + EventHandlerRef ref; + const EventTypeSpec kControlBoundsChangedEvent = { kEventClassControl, kEventControlBoundsChanged }; + InstallEventHandler (GetControlEventTarget (dummyView), NewEventHandlerUPP (viewBoundsChangedEvent), 1, &kControlBoundsChangedEvent, (void*) comp, &ref); + comp->getProperties().set ("boundsEventRef", String::toHexString ((pointer_sized_int) (void*) ref)); + + updateComponentPos (comp); + + #if ! JucePlugin_EditorRequiresKeyboardFocus + comp->addToDesktop (ComponentPeer::windowIsTemporary | ComponentPeer::windowIgnoresKeyPresses); + #else + comp->addToDesktop (ComponentPeer::windowIsTemporary); + #endif + + comp->setVisible (true); + comp->toFront (false); + + NSView* pluginView = (NSView*) comp->getWindowHandle(); + NSWindow* pluginWindow = [pluginView window]; + [pluginWindow setExcludedFromWindowsMenu: YES]; + [pluginWindow setCanHide: YES]; + + [hostWindow addChildWindow: pluginWindow + ordered: NSWindowAbove]; + [hostWindow orderFront: nil]; + [pluginWindow orderFront: nil]; + + attachWindowHidingHooks (comp, (WindowRef) windowRef, hostWindow); + + return hostWindow; + #endif + } + } + + static void detachComponentFromWindowRef (Component* comp, void* nsWindow, bool isHIView) + { + #if JUCE_64BIT + comp->removeFromDesktop(); + #else + //treat NSView like 64bit + if (! isHIView) + { + comp->removeFromDesktop(); + } + else + { + JUCE_AUTORELEASEPOOL + { + EventHandlerRef ref = (EventHandlerRef) (void*) (pointer_sized_int) + comp->getProperties() ["boundsEventRef"].toString().getHexValue64(); + RemoveEventHandler (ref); + + removeWindowHidingHooks (comp); + + HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int) + comp->getProperties() ["dummyViewRef"].toString().getHexValue64(); + + if (HIViewIsValid (dummyView)) + CFRelease (dummyView); + + NSWindow* hostWindow = (NSWindow*) nsWindow; + NSView* pluginView = (NSView*) comp->getWindowHandle(); + NSWindow* pluginWindow = [pluginView window]; + + [hostWindow removeChildWindow: pluginWindow]; + comp->removeFromDesktop(); + + [hostWindow release]; + } + + // The event loop needs to be run between closing the window and deleting the plugin, + // presumably to let the cocoa objects get tidied up. Leaving out this line causes crashes + // in Live and Reaper when you delete the plugin with its window open. + // (Doing it this way rather than using a single longer timout means that we can guarantee + // how many messages will be dispatched, which seems to be vital in Reaper) + for (int i = 20; --i >= 0;) + MessageManager::getInstance()->runDispatchLoopUntil (1); + } + #endif + } + + static void setNativeHostWindowSize (void* nsWindow, Component* component, int newWidth, int newHeight, bool isHIView) + { + JUCE_AUTORELEASEPOOL + { + #if JUCE_64BIT + component->setSize (newWidth, newHeight); + #else + if (! isHIView) + { //Treat NSView like 64bit: + component->setSize (newWidth, newHeight); + } + else if (HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int) + component->getProperties() ["dummyViewRef"].toString().getHexValue64()) + { + HIRect frameRect; + HIViewGetFrame (dummyView, &frameRect); + frameRect.size.width = newWidth; + frameRect.size.height = newHeight; + HIViewSetFrame (dummyView, &frameRect); + } + #endif + } + } +} // (juce namespace) + + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Common.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Common.h new file mode 100644 index 0000000..2c575f3 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Common.h @@ -0,0 +1,393 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found 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.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_VST3COMMON_H_INCLUDED +#define JUCE_VST3COMMON_H_INCLUDED + +//============================================================================== +#define JUCE_DECLARE_VST3_COM_REF_METHODS \ + Steinberg::uint32 PLUGIN_API addRef() override { return (Steinberg::uint32) ++refCount; } \ + Steinberg::uint32 PLUGIN_API release() override { const int r = --refCount; if (r == 0) delete this; return (Steinberg::uint32) r; } + +#define JUCE_DECLARE_VST3_COM_QUERY_METHODS \ + Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID, void** obj) override \ + { \ + jassertfalse; \ + *obj = nullptr; \ + return Steinberg::kNotImplemented; \ + } + +static bool doUIDsMatch (const Steinberg::TUID a, const Steinberg::TUID b) noexcept +{ + return std::memcmp (a, b, sizeof (Steinberg::TUID)) == 0; +} + +#define TEST_FOR_AND_RETURN_IF_VALID(ClassType) \ + if (doUIDsMatch (iid, ClassType::iid)) \ + { \ + addRef(); \ + *obj = dynamic_cast (this); \ + return Steinberg::kResultOk; \ + } + +#define TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID(CommonClassType, SourceClassType) \ + if (doUIDsMatch (iid, CommonClassType::iid)) \ + { \ + addRef(); \ + *obj = (CommonClassType*) static_cast (this); \ + return Steinberg::kResultOk; \ + } + +//============================================================================== +static juce::String toString (const Steinberg::char8* string) noexcept { return juce::String (string); } +static juce::String toString (const Steinberg::char16* string) noexcept { return juce::String (juce::CharPointer_UTF16 ((juce::CharPointer_UTF16::CharType*) string)); } + +// NB: The casts are handled by a Steinberg::UString operator +static juce::String toString (const Steinberg::UString128& string) noexcept { return toString (static_cast (string)); } +static juce::String toString (const Steinberg::UString256& string) noexcept { return toString (static_cast (string)); } + +static void toString128 (Steinberg::Vst::String128 result, const juce::String& source) +{ + Steinberg::UString (result, 128).fromAscii (source.toUTF8()); +} + +static Steinberg::Vst::TChar* toString (const juce::String& source) noexcept +{ + return reinterpret_cast (source.toUTF16().getAddress()); +} + +#if JUCE_WINDOWS + static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeHWND; +#else + static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeNSView; +#endif + + +//============================================================================== +/** For the sake of simplicity, there can only be 1 arrangement type per channel count. + i.e.: 4 channels == k31Cine OR k40Cine +*/ +static Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (int numChannels) noexcept +{ + using namespace Steinberg::Vst::SpeakerArr; + + switch (numChannels) + { + case 0: return kEmpty; + case 1: return kMono; + case 2: return kStereo; + case 3: return k30Cine; + case 4: return k31Cine; + case 5: return k50; + case 6: return k51; + case 7: return k61Cine; + case 8: return k71CineFullFront; + case 9: return k90; + case 10: return k91; + case 11: return k101; + case 12: return k111; + case 13: return k130; + case 14: return k131; + case 24: return (Steinberg::Vst::SpeakerArrangement) 1929904127; // k222 + default: break; + } + + jassert (numChannels >= 0); + + juce::BigInteger bi; + bi.setRange (0, jmin (numChannels, (int) (sizeof (Steinberg::Vst::SpeakerArrangement) * 8)), true); + return (Steinberg::Vst::SpeakerArrangement) bi.toInt64(); +} + +//============================================================================== +template +class ComSmartPtr +{ +public: + ComSmartPtr() noexcept : source (nullptr) {} + ComSmartPtr (ObjectType* object) noexcept : source (object) { if (source != nullptr) source->addRef(); } + ComSmartPtr (const ComSmartPtr& other) noexcept : source (other.source) { if (source != nullptr) source->addRef(); } + ~ComSmartPtr() { if (source != nullptr) source->release(); } + + operator ObjectType*() const noexcept { return source; } + ObjectType* get() const noexcept { return source; } + ObjectType& operator*() const noexcept { return *source; } + ObjectType* operator->() const noexcept { return source; } + + ComSmartPtr& operator= (const ComSmartPtr& other) { return operator= (other.source); } + + ComSmartPtr& operator= (ObjectType* const newObjectToTakePossessionOf) + { + ComSmartPtr p (newObjectToTakePossessionOf); + std::swap (p.source, source); + return *this; + } + + bool operator== (ObjectType* const other) noexcept { return source == other; } + bool operator!= (ObjectType* const other) noexcept { return source != other; } + + bool loadFrom (Steinberg::FUnknown* o) + { + *this = nullptr; + return o != nullptr && o->queryInterface (ObjectType::iid, (void**) &source) == Steinberg::kResultOk; + } + + bool loadFrom (Steinberg::IPluginFactory* factory, const Steinberg::TUID& uuid) + { + jassert (factory != nullptr); + *this = nullptr; + return factory->createInstance (uuid, ObjectType::iid, (void**) &source) == Steinberg::kResultOk; + } + +private: + ObjectType* source; +}; + +//============================================================================== +class MidiEventList : public Steinberg::Vst::IEventList +{ +public: + MidiEventList() {} + virtual ~MidiEventList() {} + + JUCE_DECLARE_VST3_COM_REF_METHODS + JUCE_DECLARE_VST3_COM_QUERY_METHODS + + //============================================================================== + void clear() + { + events.clearQuick(); + } + + Steinberg::int32 PLUGIN_API getEventCount() override + { + return (Steinberg::int32) events.size(); + } + + // NB: This has to cope with out-of-range indexes from some plugins. + Steinberg::tresult PLUGIN_API getEvent (Steinberg::int32 index, Steinberg::Vst::Event& e) override + { + if (isPositiveAndBelow ((int) index, events.size())) + { + e = events.getReference ((int) index); + return Steinberg::kResultTrue; + } + + return Steinberg::kResultFalse; + } + + Steinberg::tresult PLUGIN_API addEvent (Steinberg::Vst::Event& e) override + { + events.add (e); + return Steinberg::kResultTrue; + } + + //============================================================================== + static void toMidiBuffer (MidiBuffer& result, Steinberg::Vst::IEventList& eventList) + { + const int32 numEvents = eventList.getEventCount(); + + for (Steinberg::int32 i = 0; i < numEvents; ++i) + { + Steinberg::Vst::Event e; + + if (eventList.getEvent (i, e) == Steinberg::kResultOk) + { + switch (e.type) + { + case Steinberg::Vst::Event::kNoteOnEvent: + result.addEvent (MidiMessage::noteOn (createSafeChannel (e.noteOn.channel), + createSafeNote (e.noteOn.pitch), + (Steinberg::uint8) denormaliseToMidiValue (e.noteOn.velocity)), + e.sampleOffset); + break; + + case Steinberg::Vst::Event::kNoteOffEvent: + result.addEvent (MidiMessage::noteOff (createSafeChannel (e.noteOff.channel), + createSafeNote (e.noteOff.pitch), + (Steinberg::uint8) denormaliseToMidiValue (e.noteOff.velocity)), + e.sampleOffset); + break; + + case Steinberg::Vst::Event::kPolyPressureEvent: + result.addEvent (MidiMessage::aftertouchChange (createSafeChannel (e.polyPressure.channel), + createSafeNote (e.polyPressure.pitch), + denormaliseToMidiValue (e.polyPressure.pressure)), + e.sampleOffset); + break; + + case Steinberg::Vst::Event::kDataEvent: + result.addEvent (MidiMessage::createSysExMessage (e.data.bytes, e.data.size), + e.sampleOffset); + break; + + default: + break; + } + } + } + } + + static void toEventList (Steinberg::Vst::IEventList& result, MidiBuffer& midiBuffer) + { + MidiBuffer::Iterator iterator (midiBuffer); + MidiMessage msg; + int midiEventPosition = 0; + + enum { maxNumEvents = 2048 }; // Steinberg's Host Checker states that no more than 2048 events are allowed at once + int numEvents = 0; + + while (iterator.getNextEvent (msg, midiEventPosition)) + { + if (++numEvents > maxNumEvents) + break; + + Steinberg::Vst::Event e = { 0 }; + + if (msg.isNoteOn()) + { + e.type = Steinberg::Vst::Event::kNoteOnEvent; + e.noteOn.channel = createSafeChannel (msg.getChannel()); + e.noteOn.pitch = createSafeNote (msg.getNoteNumber()); + e.noteOn.velocity = normaliseMidiValue (msg.getVelocity()); + e.noteOn.length = 0; + e.noteOn.tuning = 0.0f; + e.noteOn.noteId = -1; + } + else if (msg.isNoteOff()) + { + e.type = Steinberg::Vst::Event::kNoteOffEvent; + e.noteOff.channel = createSafeChannel (msg.getChannel()); + e.noteOff.pitch = createSafeNote (msg.getNoteNumber()); + e.noteOff.velocity = normaliseMidiValue (msg.getVelocity()); + e.noteOff.tuning = 0.0f; + e.noteOff.noteId = -1; + } + else if (msg.isSysEx()) + { + e.type = Steinberg::Vst::Event::kDataEvent; + e.data.bytes = msg.getSysExData(); + e.data.size = msg.getSysExDataSize(); + e.data.type = Steinberg::Vst::DataEvent::kMidiSysEx; + } + else if (msg.isAftertouch()) + { + e.type = Steinberg::Vst::Event::kPolyPressureEvent; + e.polyPressure.channel = createSafeChannel (msg.getChannel()); + e.polyPressure.pitch = createSafeNote (msg.getNoteNumber()); + e.polyPressure.pressure = normaliseMidiValue (msg.getAfterTouchValue()); + } + else + { + continue; + } + + e.busIndex = 0; + e.sampleOffset = midiEventPosition; + + result.addEvent (e); + } + } + +private: + Array events; + Atomic refCount; + + static Steinberg::int16 createSafeChannel (int channel) noexcept { return (Steinberg::int16) jlimit (0, 15, channel - 1); } + static int createSafeChannel (Steinberg::int16 channel) noexcept { return (int) jlimit (1, 16, channel + 1); } + + static Steinberg::int16 createSafeNote (int note) noexcept { return (Steinberg::int16) jlimit (0, 127, note); } + static int createSafeNote (Steinberg::int16 note) noexcept { return jlimit (0, 127, (int) note); } + + static float normaliseMidiValue (int value) noexcept { return jlimit (0.0f, 1.0f, (float) value / 127.0f); } + static int denormaliseToMidiValue (float value) noexcept { return roundToInt (jlimit (0.0f, 127.0f, value * 127.0f)); } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiEventList) +}; + +//============================================================================== +namespace VST3BufferExchange +{ + typedef Array Bus; + typedef Array BusMap; + + /** Assigns a series of AudioSampleBuffer's channels to an AudioBusBuffers' + + @warning For speed, does not check the channel count and offsets + according to the AudioSampleBuffer + */ + void associateBufferTo (Steinberg::Vst::AudioBusBuffers& vstBuffers, + Bus& bus, + const AudioSampleBuffer& buffer, + int numChannels, int channelStartOffset, + int sampleOffset = 0) noexcept + { + const int channelEnd = numChannels + channelStartOffset; + jassert (channelEnd >= 0 && channelEnd <= buffer.getNumChannels()); + + bus.clearQuick(); + + for (int i = channelStartOffset; i < channelEnd; ++i) + bus.add (buffer.getSampleData (i, sampleOffset)); + + vstBuffers.channelBuffers32 = bus.getRawDataPointer(); + vstBuffers.numChannels = numChannels; + vstBuffers.silenceFlags = 0; + } + + static void mapBufferToBusses (Array& result, + Steinberg::Vst::IAudioProcessor& processor, + BusMap& busMapToUse, + bool isInput, int numBusses, + AudioSampleBuffer& source) + { + int channelIndexOffset = 0; + + for (int i = 0; i < numBusses; ++i) + { + Steinberg::Vst::SpeakerArrangement arrangement = 0; + processor.getBusArrangement (isInput ? Steinberg::Vst::kInput : Steinberg::Vst::kOutput, + (Steinberg::int32) i, arrangement); + + const int numChansForBus = BigInteger ((juce::int64) arrangement).countNumberOfSetBits(); + + if (i >= result.size()) + result.add (Steinberg::Vst::AudioBusBuffers()); + + if (i >= busMapToUse.size()) + busMapToUse.add (Bus()); + + if (numChansForBus > 0) + { + associateBufferTo (result.getReference (i), + busMapToUse.getReference (i), + source, numChansForBus, channelIndexOffset); + } + + channelIndexOffset += numChansForBus; + } + } +} + +#endif // JUCE_VST3COMMON_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Headers.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Headers.h new file mode 100644 index 0000000..612e850 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Headers.h @@ -0,0 +1,171 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found 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.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_VST3HEADERS_H_INCLUDED +#define JUCE_VST3HEADERS_H_INCLUDED + +#undef Point +#undef Component + +// Wow, those Steinberg guys really don't worry too much about compiler warnings. +#if _MSC_VER + #pragma warning (disable: 4505) + #pragma warning (push, 0) + #pragma warning (disable: 4702) +#elif __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wnon-virtual-dtor" + #pragma clang diagnostic ignored "-Wreorder" + #pragma clang diagnostic ignored "-Wunsequenced" + #pragma clang diagnostic ignored "-Wint-to-pointer-cast" + #pragma clang diagnostic ignored "-Wunused-parameter" + #pragma clang diagnostic ignored "-Wconversion" + #pragma clang diagnostic ignored "-Woverloaded-virtual" + #pragma clang diagnostic ignored "-Wshadow" +#endif + +/* These files come with the Steinberg VST3 SDK - to get them, you'll need to + visit the Steinberg website and agree to whatever is currently required to + get them. + + Then, you'll need to make sure your include path contains your "VST3 SDK" + directory (or whatever you've named it on your machine). The Introjucer has + a special box for setting this path. +*/ +#if JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + +//============================================================================== +namespace Steinberg +{ + /** Missing IIDs */ + DEF_CLASS_IID (IPluginBase) + DEF_CLASS_IID (IPlugView) + DEF_CLASS_IID (IPlugFrame) + DEF_CLASS_IID (IBStream) + DEF_CLASS_IID (ISizeableStream) + DEF_CLASS_IID (IPluginFactory) + DEF_CLASS_IID (IPluginFactory2) + DEF_CLASS_IID (IPluginFactory3) +} +#endif //JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY + +#if _MSC_VER + #pragma warning (pop) +#elif __clang__ + #pragma clang diagnostic pop +#endif + +//============================================================================== +#undef ASSERT +#undef WARNING +#undef PRINTSYSERROR +#undef DEBUGSTR +#undef DBPRT0 +#undef DBPRT1 +#undef DBPRT2 +#undef DBPRT3 +#undef DBPRT4 +#undef DBPRT5 +#undef min +#undef max +#undef MIN +#undef MAX +#undef calloc +#undef free +#undef malloc +#undef realloc +#undef NEW +#undef NEWVEC +#undef VERIFY +#undef VERIFY_IS +#undef VERIFY_NOT +#undef META_CREATE_FUNC +#undef CLASS_CREATE_FUNC +#undef SINGLE_CREATE_FUNC +#undef _META_CLASS +#undef _META_CLASS_IFACE +#undef _META_CLASS_SINGLE +#undef META_CLASS +#undef META_CLASS_IFACE +#undef META_CLASS_SINGLE +#undef SINGLETON +#undef OBJ_METHODS +#undef QUERY_INTERFACE +#undef LICENCE_UID +#undef BEGIN_FACTORY +#undef DEF_CLASS +#undef DEF_CLASS1 +#undef DEF_CLASS2 +#undef DEF_CLASS_W +#undef END_FACTORY +#undef Point +#undef Component + +#endif // JUCE_VST3HEADERS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.cpp b/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.cpp new file mode 100644 index 0000000..bd0bdf8 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.cpp @@ -0,0 +1,41 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +FileFilter::FileFilter (const String& filterDescription) + : description (filterDescription) +{ +} + +FileFilter::~FileFilter() +{ +} + +const String& FileFilter::getDescription() const noexcept +{ + return description; +} diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.h b/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.h new file mode 100644 index 0000000..4c02415 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.h @@ -0,0 +1,77 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +#ifndef JUCE_FILEFILTER_H_INCLUDED +#define JUCE_FILEFILTER_H_INCLUDED + + +//============================================================================== +/** + Interface for deciding which files are suitable for something. + + For example, this is used by DirectoryContentsList to select which files + go into the list. + + @see WildcardFileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent +*/ +class JUCE_API FileFilter +{ +public: + //============================================================================== + /** Creates a filter with the given description. + + The description can be returned later with the getDescription() method. + */ + FileFilter (const String& filterDescription); + + /** Destructor. */ + virtual ~FileFilter(); + + //============================================================================== + /** Returns the description that the filter was created with. */ + const String& getDescription() const noexcept; + + //============================================================================== + /** Should return true if this file is suitable for inclusion in whatever context + the object is being used. + */ + virtual bool isFileSuitable (const File& file) const = 0; + + /** Should return true if this directory is suitable for inclusion in whatever context + the object is being used. + */ + virtual bool isDirectorySuitable (const File& file) const = 0; + + +protected: + //============================================================================== + String description; +}; + + +#endif // JUCE_FILEFILTER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.cpp b/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.cpp new file mode 100644 index 0000000..1fef455 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.cpp @@ -0,0 +1,77 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns, + const String& directoryWildcardPatterns, + const String& desc) + : FileFilter (desc.isEmpty() ? fileWildcardPatterns + : (desc + " (" + fileWildcardPatterns + ")")) +{ + parse (fileWildcardPatterns, fileWildcards); + parse (directoryWildcardPatterns, directoryWildcards); +} + +WildcardFileFilter::~WildcardFileFilter() +{ +} + +bool WildcardFileFilter::isFileSuitable (const File& file) const +{ + return match (file, fileWildcards); +} + +bool WildcardFileFilter::isDirectorySuitable (const File& file) const +{ + return match (file, directoryWildcards); +} + +//============================================================================== +void WildcardFileFilter::parse (const String& pattern, StringArray& result) +{ + result.addTokens (pattern.toLowerCase(), ";,", "\"'"); + + result.trim(); + result.removeEmptyStrings(); + + // special case for *.*, because people use it to mean "any file", but it + // would actually ignore files with no extension. + for (int i = result.size(); --i >= 0;) + if (result[i] == "*.*") + result.set (i, "*"); +} + +bool WildcardFileFilter::match (const File& file, const StringArray& wildcards) +{ + const String filename (file.getFileName()); + + for (int i = wildcards.size(); --i >= 0;) + if (filename.matchesWildcard (wildcards[i], true)) + return true; + + return false; +} diff --git a/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h b/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h new file mode 100644 index 0000000..166ae4a --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h @@ -0,0 +1,86 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +#ifndef JUCE_WILDCARDFILEFILTER_H_INCLUDED +#define JUCE_WILDCARDFILEFILTER_H_INCLUDED + + +//============================================================================== +/** + A type of FileFilter that works by wildcard pattern matching. + + This filter only allows files that match one of the specified patterns, but + allows all directories through. + + @see FileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent +*/ +class JUCE_API WildcardFileFilter : public FileFilter +{ +public: + //============================================================================== + /** + Creates a wildcard filter for one or more patterns. + + The wildcardPatterns parameter is a comma or semicolon-delimited set of + patterns, e.g. "*.wav;*.aiff" would look for files ending in either .wav + or .aiff. + + Passing an empty string as a pattern will fail to match anything, so by leaving + either the file or directory pattern parameter empty means you can control + whether files or directories are found. + + The description is a name to show the user in a list of possible patterns, so + for the wav/aiff example, your description might be "audio files". + */ + WildcardFileFilter (const String& fileWildcardPatterns, + const String& directoryWildcardPatterns, + const String& description); + + /** Destructor. */ + ~WildcardFileFilter(); + + //============================================================================== + /** Returns true if the filename matches one of the patterns specified. */ + bool isFileSuitable (const File& file) const; + + /** This always returns true. */ + bool isDirectorySuitable (const File& file) const; + +private: + //============================================================================== + StringArray fileWildcards, directoryWildcards; + + static void parse (const String& pattern, StringArray& result); + static bool match (const File& file, const StringArray& wildcards); + + JUCE_LEAK_DETECTOR (WildcardFileFilter) +}; + + + +#endif // JUCE_WILDCARDFILEFILTER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp b/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp new file mode 100644 index 0000000..79356f7 --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp @@ -0,0 +1,259 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found 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.juce.com for more information. + + ============================================================================== +*/ + +enum { magicMastSlaveConnectionHeader = 0x712baf04 }; + +static const char* startMessage = "__ipc_st"; +static const char* killMessage = "__ipc_k_"; +static const char* pingMessage = "__ipc_p_"; +enum { specialMessageSize = 8 }; + +static String getCommandLinePrefix (const String& commandLineUniqueID) +{ + return "--" + commandLineUniqueID + ":"; +} + +//============================================================================== +// This thread sends and receives ping messages every second, so that it +// can find out if the other process has stopped running. +struct ChildProcessPingThread : public Thread, + private AsyncUpdater +{ + ChildProcessPingThread() : Thread ("IPC ping"), timeoutMs (8000) + { + pingReceived(); + } + + static bool isPingMessage (const MemoryBlock& m) noexcept + { + return memcmp (m.getData(), pingMessage, specialMessageSize) == 0; + } + + void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; } + void triggerConnectionLostMessage() { triggerAsyncUpdate(); } + + virtual bool sendPingMessage (const MemoryBlock&) = 0; + virtual void pingFailed() = 0; + + int timeoutMs; + +private: + Atomic countdown; + + void handleAsyncUpdate() override { pingFailed(); } + + void run() override + { + while (! threadShouldExit()) + { + if (--countdown <= 0 || ! sendPingMessage (MemoryBlock (pingMessage, specialMessageSize))) + { + triggerConnectionLostMessage(); + break; + } + + wait (1000); + } + } + + JUCE_DECLARE_NON_COPYABLE (ChildProcessPingThread) +}; + +//============================================================================== +struct ChildProcessMaster::Connection : public InterprocessConnection, + private ChildProcessPingThread +{ + Connection (ChildProcessMaster& m, const String& pipeName) + : InterprocessConnection (false, magicMastSlaveConnectionHeader), owner (m) + { + if (createPipe (pipeName, timeoutMs)) + startThread (4); + } + + ~Connection() + { + stopThread (10000); + } + +private: + void connectionMade() override {} + void connectionLost() override { owner.handleConnectionLost(); } + + bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToSlave (m); } + void pingFailed() override { connectionLost(); } + + void messageReceived (const MemoryBlock& m) override + { + pingReceived(); + + if (m.getSize() != specialMessageSize || ! isPingMessage (m)) + owner.handleMessageFromSlave (m); + } + + ChildProcessMaster& owner; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection) +}; + +//============================================================================== +ChildProcessMaster::ChildProcessMaster() {} + +ChildProcessMaster::~ChildProcessMaster() +{ + if (connection != nullptr) + { + sendMessageToSlave (MemoryBlock (killMessage, specialMessageSize)); + connection->disconnect(); + connection = nullptr; + } +} + +void ChildProcessMaster::handleConnectionLost() {} + +bool ChildProcessMaster::sendMessageToSlave (const MemoryBlock& mb) +{ + if (connection != nullptr) + return connection->sendMessage (mb); + + jassertfalse; // this can only be used when the connection is active! + return false; +} + +bool ChildProcessMaster::launchSlaveProcess (const File& executable, const String& commandLineUniqueID) +{ + connection = nullptr; + jassert (childProcess.kill()); + + const String pipeName ("p" + String::toHexString (Random().nextInt64())); + + StringArray args; + args.add (executable.getFullPathName()); + args.add (getCommandLinePrefix (commandLineUniqueID) + pipeName); + + if (childProcess.start (args)) + { + connection = new Connection (*this, pipeName); + + if (connection->isConnected()) + { + sendMessageToSlave (MemoryBlock (startMessage, specialMessageSize)); + return true; + } + + connection = nullptr; + } + + return false; +} + +//============================================================================== +struct ChildProcessSlave::Connection : public InterprocessConnection, + private ChildProcessPingThread +{ + Connection (ChildProcessSlave& p, const String& pipeName) + : InterprocessConnection (false, magicMastSlaveConnectionHeader), owner (p) + { + connectToPipe (pipeName, timeoutMs); + startThread (4); + } + + ~Connection() + { + stopThread (10000); + } + +private: + ChildProcessSlave& owner; + + void connectionMade() override {} + void connectionLost() override { owner.handleConnectionLost(); } + + bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToMaster (m); } + void pingFailed() override { connectionLost(); } + + void messageReceived (const MemoryBlock& m) override + { + pingReceived(); + + if (m.getSize() == specialMessageSize) + { + if (isPingMessage (m)) + return; + + if (memcmp (m.getData(), killMessage, specialMessageSize) == 0) + { + triggerConnectionLostMessage(); + return; + } + + if (memcmp (m.getData(), startMessage, specialMessageSize) == 0) + { + owner.handleConnectionMade(); + return; + } + } + + owner.handleMessageFromMaster (m); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection) +}; + +//============================================================================== +ChildProcessSlave::ChildProcessSlave() {} +ChildProcessSlave::~ChildProcessSlave() {} + +void ChildProcessSlave::handleConnectionMade() {} +void ChildProcessSlave::handleConnectionLost() {} + +bool ChildProcessSlave::sendMessageToMaster (const MemoryBlock& mb) +{ + if (connection != nullptr) + return connection->sendMessage (mb); + + jassertfalse; // this can only be used when the connection is active! + return false; +} + +bool ChildProcessSlave::initialiseFromCommandLine (const String& commandLine, + const String& commandLineUniqueID) +{ + String prefix (getCommandLinePrefix (commandLineUniqueID)); + + if (commandLine.trim().startsWith (prefix)) + { + String pipeName (commandLine.fromFirstOccurrenceOf (prefix, false, false) + .upToFirstOccurrenceOf (" ", false, false).trim()); + + if (pipeName.isNotEmpty()) + { + connection = new Connection (*this, pipeName); + + if (! connection->isConnected()) + connection = nullptr; + } + } + + return connection != nullptr; +} diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h b/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h new file mode 100644 index 0000000..0b3ec6b --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h @@ -0,0 +1,179 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found 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.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED +#define JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED + +//============================================================================== +/** + Acts as the slave end of a master/slave pair of connected processes. + + The ChildProcessSlave and ChildProcessMaster classes make it easy for an app + to spawn a child process, and to manage a 2-way messaging connection to control it. + + To use the system, you need to create subclasses of both ChildProcessSlave and + ChildProcessMaster. To instantiate the ChildProcessSlave object, you must + add some code to your main() or JUCEApplication::initialise() function that + calls the initialiseFromCommandLine() method to check the app's command-line + parameters to see whether it's being launched as a child process. If this returns + true then the slave process can be allowed to run, and its handleMessageFromMaster() + method will be called whenever a message arrives. + + The juce demo app has a good example of this class in action. + + @see ChildProcessMaster, InterprocessConnection, ChildProcess +*/ +class JUCE_API ChildProcessSlave +{ +public: + /** Creates a non-connected slave process. + Use initialiseFromCommandLine to connect to a master process. + */ + ChildProcessSlave(); + + /** Destructor. */ + virtual ~ChildProcessSlave(); + + /** This checks some command-line parameters to see whether they were generated by + ChildProcessMaster::launchSlaveProcess(), and if so, connects to that master process. + + In an exe that can be used as a child process, you should add some code to your + main() or JUCEApplication::initialise() that calls this method. + + The commandLineUniqueID should be a short alphanumeric identifier (no spaces!) + that matches the string passed to ChildProcessMaster::launchSlaveProcess(). + + Returns true if the command-line matches and the connection is made successfully. + */ + bool initialiseFromCommandLine (const String& commandLine, + const String& commandLineUniqueID); + + //============================================================================== + /** This will be called to deliver messages from the master process. + The call will probably be made on a background thread, so be careful with your + thread-safety! You may want to respond by sending back a message with + sendMessageToMaster() + */ + virtual void handleMessageFromMaster (const MemoryBlock&) = 0; + + /** This will be called when the master process finishes connecting to this slave. + The call will probably be made on a background thread, so be careful with your thread-safety! + */ + virtual void handleConnectionMade(); + + /** This will be called when the connection to the master process is lost. + The call may be made from any thread (including the message thread). + Typically, if your process only exists to act as a slave, you should probably exit + when this happens. + */ + virtual void handleConnectionLost(); + + /** Tries to send a message to the master process. + This returns true if the message was sent, but doesn't check that it actually gets + delivered at the other end. If successful, the data will emerge in a call to your + ChildProcessMaster::handleMessageFromSlave(). + */ + bool sendMessageToMaster (const MemoryBlock&); + +private: + struct Connection; + friend struct Connection; + friend struct ContainerDeletePolicy; + ScopedPointer connection; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessSlave) +}; + +//============================================================================== +/** + Acts as the master in a master/slave pair of connected processes. + + The ChildProcessSlave and ChildProcessMaster classes make it easy for an app + to spawn a child process, and to manage a 2-way messaging connection to control it. + + To use the system, you need to create subclasses of both ChildProcessSlave and + ChildProcessMaster. When you want your master process to launch the slave, you + just call launchSlaveProcess(), and it'll attempt to launch the executable that + you specify (which may be the same exe), and assuming it has been set-up to + correctly parse the command-line parameters (see ChildProcessSlave) then a + two-way connection will be created. + + The juce demo app has a good example of this class in action. + + @see ChildProcessSlave, InterprocessConnection, ChildProcess +*/ +class JUCE_API ChildProcessMaster +{ +public: + /** Creates an uninitialised master process object. + Use launchSlaveProcess to launch and connect to a child process. + */ + ChildProcessMaster(); + + /** Destructor. */ + virtual ~ChildProcessMaster(); + + /** Attempts to launch and connect to a slave process. + This will start the given executable, passing it a special command-line + parameter based around the commandLineUniqueID string, which must be a + short alphanumeric string (no spaces!) that identifies your app. The exe + that gets launched must respond by calling ChildProcessSlave::initialiseFromCommandLine() + in its startup code, and must use a matching ID to commandLineUniqueID. + + If this all works, the method returns true, and you can begin sending and + receiving messages with the slave process. + */ + bool launchSlaveProcess (const File& executableToLaunch, + const String& commandLineUniqueID); + + /** This will be called to deliver a message from the slave process. + The call will probably be made on a background thread, so be careful with your thread-safety! + */ + virtual void handleMessageFromSlave (const MemoryBlock&) = 0; + + /** This will be called when the slave process dies or is somehow disconnected. + The call will probably be made on a background thread, so be careful with your thread-safety! + */ + virtual void handleConnectionLost(); + + /** Attempts to send a message to the slave process. + This returns true if the message was dispatched, but doesn't check that it actually + gets delivered at the other end. If successful, the data will emerge in a call to + your ChildProcessSlave::handleMessageFromMaster(). + */ + bool sendMessageToSlave (const MemoryBlock&); + +private: + ChildProcess childProcess; + + struct Connection; + friend struct Connection; + friend struct ContainerDeletePolicy; + ScopedPointer connection; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessMaster) +}; + + +#endif // JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED