From 5c74ca9896924ab4e248bcc41406e2a255fcdd53 Mon Sep 17 00:00:00 2001 From: jules Date: Tue, 11 Feb 2014 10:22:12 +0000 Subject: [PATCH] Fixes to VST3 wrapper to handle different numbers of input/output channels. --- .../VST3/juce_VST3_Wrapper.cpp | 104 +++++----- .../VST3/juce_VST3_Wrapper.mm | 181 +++++++++--------- .../format_types/juce_VST3Common.h | 4 +- 3 files changed, 143 insertions(+), 146 deletions(-) diff --git a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp index edcb308dd5..82c998e8d3 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -623,10 +623,7 @@ public: addEventBusTo (eventOutputs, TRANS("MIDI Output")); #endif - { - const ScopedLock sl (contextLock); - processContext.sampleRate = processSetup.sampleRate; - } + processContext.sampleRate = processSetup.sampleRate; preparePlugin (processSetup.sampleRate, (int) processSetup.maxSamplesPerBlock); @@ -735,16 +732,16 @@ public: double sampleRate = getPluginInstance().getSampleRate(); int bufferSize = getPluginInstance().getBlockSize(); - { - const ScopedLock sl (contextLock); - sampleRate = processSetup.sampleRate > 0.0 - ? processSetup.sampleRate - : sampleRate; + sampleRate = processSetup.sampleRate > 0.0 + ? processSetup.sampleRate + : sampleRate; - bufferSize = processSetup.maxSamplesPerBlock > 0 - ? (int) processSetup.maxSamplesPerBlock - : bufferSize; - } + bufferSize = processSetup.maxSamplesPerBlock > 0 + ? (int) processSetup.maxSamplesPerBlock + : bufferSize; + + channelList.clear(); + channelList.insertMultiple (0, nullptr, jmax (JucePlugin_MaxNumInputChannels, JucePlugin_MaxNumOutputChannels) + 1); preparePlugin (sampleRate, bufferSize); } @@ -921,8 +918,6 @@ public: //============================================================================== bool getCurrentPosition (CurrentPositionInfo& info) override { - const ScopedLock sl (contextLock); - info.timeInSamples = jmax ((juce::int64) 0, processContext.projectTimeSamples); info.timeInSeconds = processContext.projectTimeMusic; info.bpm = jmax (1.0, processContext.tempo); @@ -1089,17 +1084,16 @@ public: tresult PLUGIN_API process (Vst::ProcessData& data) override { + if (pluginInstance == nullptr || processContext.sampleRate <= 0.0) + return kResultFalse; + + if (data.processContext != nullptr) + processContext = *data.processContext; + else + zerostruct (processContext); + midiBuffer.clear(); - { - const ScopedLock sl (contextLock); - - if (data.processContext != nullptr) - processContext = *data.processContext; - else - zerostruct (processContext); - } - #if JucePlugin_WantsMidiInput if (data.inputEvents != nullptr) MidiEventList::toMidiBuffer (midiBuffer, *data.inputEvents); @@ -1109,42 +1103,47 @@ public: const int numMidiEventsComingIn = midiBuffer.getNumEvents(); #endif - if (pluginInstance != nullptr - && data.inputs != nullptr - && data.outputs != nullptr - && processContext.sampleRate > 0.0) + 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 (Vst::IParameterChanges* const paramChanges = data.inputParameterChanges) - processParameterChanges (*paramChanges); - - const int numSamples = (int) data.numSamples; - const int numChannels = (int) data.inputs[0].numChannels; - - AudioSampleBuffer buffer (data.inputs[0].channelBuffers32, numChannels, numSamples); - int startBusOffset = 1; + if (data.inputParameterChanges != nullptr) + processParameterChanges (*data.inputParameterChanges); if (pluginInstance->isSuspended()) - startBusOffset = 0; + buffer.clear(); else pluginInstance->processBlock (buffer, midiBuffer); + } - // Copy the audio: - for (int i = 0; i < numChannels; ++i) - FloatVectorOperations::copy (data.outputs[0].channelBuffers32[i], buffer.getSampleData (i), numSamples); + for (int i = 0; i < numOutputChans; ++i) + FloatVectorOperations::copy (data.outputs[0].channelBuffers32[i], buffer.getSampleData (i), (int) data.numSamples); - // Clear the other busses: - for (int i = startBusOffset; i < data.numOutputs; ++i) + // 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], numSamples); - } - else - { - return kResultFalse; - } + FloatVectorOperations::clear (data.outputs[i].channelBuffers32[f], (int) data.numSamples); #if JucePlugin_ProducesMidiOutput if (data.outputEvents != nullptr) @@ -1183,12 +1182,12 @@ private: 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... */ - CriticalSection contextLock; //Not sure how necessary this is... Vst::ProcessContext processContext; Vst::ProcessSetup processSetup; Vst::BusList audioInputs, audioOutputs, eventInputs, eventOutputs; MidiBuffer midiBuffer; + Array channelList; const JuceLibraryRefCount juceCount; @@ -1210,11 +1209,8 @@ private: 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; + if (type == Vst::kAudio) return dir == Vst::kInput ? &audioInputs : &audioOutputs; + if (type == Vst::kEvent) return dir == Vst::kInput ? &eventInputs : &eventOutputs; return nullptr; } diff --git a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.mm b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.mm index 333c26e441..9fdf505493 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.mm +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.mm @@ -83,29 +83,7 @@ namespace juce 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) - { + #if JUCE_64BIT NSView* parentView = (NSView*) windowRef; #if JucePlugin_EditorRequiresKeyboardFocus @@ -123,75 +101,96 @@ namespace juce [[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 } - - 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) diff --git a/modules/juce_audio_processors/format_types/juce_VST3Common.h b/modules/juce_audio_processors/format_types/juce_VST3Common.h index 70d797c552..1a1e4932b6 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3Common.h +++ b/modules/juce_audio_processors/format_types/juce_VST3Common.h @@ -225,7 +225,9 @@ public: //============================================================================== static void toMidiBuffer (MidiBuffer& result, Steinberg::Vst::IEventList& eventList) { - for (Steinberg::int32 i = 0; i < eventList.getEventCount(); ++i) + const int32 numEvents = eventList.getEventCount(); + + for (Steinberg::int32 i = 0; i < numEvents; ++i) { Steinberg::Vst::Event e;