| @@ -0,0 +1,61 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE 7 technical preview. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| You may use this code under the terms of the GPL v3 | |||
| (see www.gnu.org/licenses). | |||
| For the technical preview this file cannot be licensed commercially. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| #include <juce_core/system/juce_TargetPlatform.h> | |||
| #include "../utility/juce_CheckSettingMacros.h" | |||
| #if JucePlugin_Enable_ARA | |||
| #include "../utility/juce_IncludeSystemHeaders.h" | |||
| #include "../utility/juce_IncludeModuleHeaders.h" | |||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunused-parameter", "-Wgnu-zero-variadic-macro-arguments", "-Wmissing-prototypes") | |||
| JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4100) | |||
| #include <ARA_Library/PlugIn/ARAPlug.cpp> | |||
| #include <ARA_Library/Dispatch/ARAPlugInDispatch.cpp> | |||
| #include <ARA_Library/Utilities/ARAPitchInterpretation.cpp> | |||
| JUCE_END_IGNORE_WARNINGS_MSVC | |||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
| namespace juce | |||
| { | |||
| #if (JUCE_DEBUG && ! JUCE_DISABLE_ASSERTIONS) || JUCE_LOG_ASSERTIONS | |||
| JUCE_API void JUCE_CALLTYPE handleARAAssertion (const char* file, const int line, const char* diagnosis) noexcept | |||
| { | |||
| #if (JUCE_DEBUG && ! JUCE_DISABLE_ASSERTIONS) | |||
| DBG (diagnosis); | |||
| #endif | |||
| logAssertion (file, line); | |||
| #if (JUCE_DEBUG && ! JUCE_DISABLE_ASSERTIONS) | |||
| if (juce_isRunningUnderDebugger()) | |||
| JUCE_BREAK_IN_DEBUGGER; | |||
| JUCE_ANALYZER_NORETURN | |||
| #endif | |||
| } | |||
| #endif | |||
| ARA_SETUP_DEBUG_MESSAGE_PREFIX(JucePlugin_Name); | |||
| } // namespace juce | |||
| #endif | |||
| @@ -0,0 +1,48 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE 7 technical preview. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| You may use this code under the terms of the GPL v3 | |||
| (see www.gnu.org/licenses). | |||
| For the technical preview this file cannot be licensed commercially. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| #pragma once | |||
| #if JucePlugin_Enable_ARA | |||
| // Configure ARA debug support prior to including ARA SDK headers | |||
| namespace juce | |||
| { | |||
| #if (JUCE_DEBUG && ! JUCE_DISABLE_ASSERTIONS) || JUCE_LOG_ASSERTIONS | |||
| #define ARA_ENABLE_INTERNAL_ASSERTS 1 | |||
| extern JUCE_API void JUCE_CALLTYPE handleARAAssertion (const char* file, const int line, const char* diagnosis) noexcept; | |||
| #if !defined(ARA_HANDLE_ASSERT) | |||
| #define ARA_HANDLE_ASSERT(file, line, diagnosis) juce::handleARAAssertion (file, line, diagnosis) | |||
| #endif | |||
| #if JUCE_LOG_ASSERTIONS | |||
| #define ARA_ENABLE_DEBUG_OUTPUT 1 | |||
| #endif | |||
| #else | |||
| #define ARA_ENABLE_INTERNAL_ASSERTS 0 | |||
| #endif // (JUCE_DEBUG && ! JUCE_DISABLE_ASSERTIONS) || JUCE_LOG_ASSERTIONS | |||
| } // namespace juce | |||
| #endif | |||
| @@ -58,6 +58,14 @@ JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
| #include <juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp> | |||
| #include <juce_audio_processors/format_types/juce_AU_Shared.h> | |||
| #if JucePlugin_Enable_ARA | |||
| #include <juce_audio_processors/utilities/ARA/juce_AudioProcessor_ARAExtensions.h> | |||
| #include <ARA_API/ARAAudioUnit.h> | |||
| #if ARA_SUPPORT_VERSION_1 | |||
| #error "Unsupported ARA version - only ARA version 2 and onward are supported by the current JUCE ARA implementation" | |||
| #endif | |||
| #endif | |||
| //============================================================================== | |||
| using namespace juce; | |||
| @@ -425,6 +433,17 @@ public: | |||
| outWritable = false; | |||
| return noErr; | |||
| #if JucePlugin_Enable_ARA | |||
| case ARA::kAudioUnitProperty_ARAFactory: | |||
| outWritable = false; | |||
| outDataSize = sizeof (ARA::ARAAudioUnitFactory); | |||
| return noErr; | |||
| case ARA::kAudioUnitProperty_ARAPlugInExtensionBindingWithRoles: | |||
| outWritable = false; | |||
| outDataSize = sizeof (ARA::ARAAudioUnitPlugInExtensionBinding); | |||
| return noErr; | |||
| #endif | |||
| default: break; | |||
| } | |||
| } | |||
| @@ -466,6 +485,33 @@ public: | |||
| jassertfalse; | |||
| break; | |||
| //============================================================================== | |||
| #if JucePlugin_Enable_ARA | |||
| case ARA::kAudioUnitProperty_ARAFactory: | |||
| { | |||
| auto auFactory = static_cast<ARA::ARAAudioUnitFactory*> (outData); | |||
| if (auFactory->inOutMagicNumber != ARA::kARAAudioUnitMagic) | |||
| return kAudioUnitErr_InvalidProperty; // if the magic value isn't found, the property ID is re-used outside the ARA context with different, unsupported sematics | |||
| auFactory->outFactory = createARAFactory(); | |||
| return noErr; | |||
| } | |||
| case ARA::kAudioUnitProperty_ARAPlugInExtensionBindingWithRoles: | |||
| { | |||
| auto binding = static_cast<ARA::ARAAudioUnitPlugInExtensionBinding*> (outData); | |||
| if (binding->inOutMagicNumber != ARA::kARAAudioUnitMagic) | |||
| return kAudioUnitErr_InvalidProperty; // if the magic value isn't found, the property ID is re-used outside the ARA context with different, unsupported sematics | |||
| AudioProcessorARAExtension* araAudioProcessorExtension = dynamic_cast<AudioProcessorARAExtension*> (juceFilter.get()); | |||
| binding->outPlugInExtension = araAudioProcessorExtension->bindToARA (binding->inDocumentControllerRef, binding->knownRoles, binding->assignedRoles); | |||
| if (binding->outPlugInExtension == nullptr) | |||
| return kAudioUnitErr_CannotDoInCurrentContext; // bindToARA() returns null if binding is already established | |||
| return noErr; | |||
| } | |||
| #endif | |||
| case juceFilterObjectPropertyID: | |||
| ((void**) outData)[0] = (void*) static_cast<AudioProcessor*> (juceFilter.get()); | |||
| ((void**) outData)[1] = (void*) this; | |||
| @@ -1030,6 +1076,9 @@ public: | |||
| { | |||
| const double rate = getSampleRate(); | |||
| jassert (rate > 0); | |||
| #if JucePlugin_Enable_ARA | |||
| jassert (juceFilter->getLatencySamples() == 0 || ! dynamic_cast<AudioProcessorARAExtension*> (juceFilter.get())->isBoundToARA()); | |||
| #endif | |||
| return rate > 0 ? juceFilter->getLatencySamples() / rate : 0; | |||
| } | |||
| @@ -1698,7 +1747,14 @@ public: | |||
| { | |||
| if (AudioProcessor* filter = static_cast<AudioProcessor*> (pointers[0])) | |||
| if (AudioProcessorEditor* editorComp = filter->createEditorIfNeeded()) | |||
| { | |||
| #if JucePlugin_Enable_ARA | |||
| jassert (dynamic_cast<AudioProcessorEditorARAExtension*> (editorComp) != nullptr); | |||
| // for proper view embedding, ARA plug-ins must be resizable | |||
| jassert (editorComp->isResizable()); | |||
| #endif | |||
| return EditorCompHolder::createViewFor (filter, static_cast<JuceAU*> (pointers[1]), editorComp); | |||
| } | |||
| } | |||
| return nil; | |||
| @@ -79,6 +79,21 @@ JUCE_BEGIN_NO_SANITIZE ("vptr") | |||
| #include <juce_core/native/juce_mac_CFHelpers.h> | |||
| #endif | |||
| //============================================================================== | |||
| #if JucePlugin_Enable_ARA | |||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE("-Wpragma-pack") | |||
| #include <ARA_API/ARAVST3.h> | |||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
| #if ARA_SUPPORT_VERSION_1 | |||
| #error "Unsupported ARA version - only ARA version 2 and onward are supported by the current implementation" | |||
| #endif | |||
| DEF_CLASS_IID(ARA::IPlugInEntryPoint) | |||
| DEF_CLASS_IID(ARA::IPlugInEntryPoint2) | |||
| DEF_CLASS_IID(ARA::IMainFactory) | |||
| #endif | |||
| namespace juce | |||
| { | |||
| @@ -644,6 +659,9 @@ class JuceVST3EditController : public Vst::EditController, | |||
| public Vst::IMidiMapping, | |||
| public Vst::IUnitInfo, | |||
| public Vst::ChannelContext::IInfoListener, | |||
| #if JucePlugin_Enable_ARA | |||
| public Presonus::IPlugInViewEmbedding, | |||
| #endif | |||
| public AudioProcessorListener, | |||
| private ComponentRestarter::Listener | |||
| { | |||
| @@ -925,6 +943,21 @@ public: | |||
| return kResultOk; | |||
| } | |||
| //============================================================================== | |||
| #if JucePlugin_Enable_ARA | |||
| Steinberg::TBool PLUGIN_API isViewEmbeddingSupported() override | |||
| { | |||
| if (auto* pluginInstance = getPluginInstance()) | |||
| return (Steinberg::TBool) dynamic_cast<AudioProcessorARAExtension*> (pluginInstance)->isEditorView(); | |||
| return (Steinberg::TBool) false; | |||
| } | |||
| Steinberg::tresult PLUGIN_API setViewIsEmbedded (Steinberg::IPlugView* /*view*/, Steinberg::TBool /*embedded*/) override | |||
| { | |||
| return kResultOk; | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| tresult PLUGIN_API setComponentState (IBStream* stream) override | |||
| { | |||
| @@ -1266,6 +1299,10 @@ public: | |||
| auto latencySamples = pluginInstance->getLatencySamples(); | |||
| #if JucePlugin_Enable_ARA | |||
| jassert (latencySamples == 0 || ! dynamic_cast<AudioProcessorARAExtension*> (pluginInstance)->isBoundToARA()); | |||
| #endif | |||
| if (details.latencyChanged && latencySamples != lastLatencySamples) | |||
| { | |||
| flags |= Vst::kLatencyChanged; | |||
| @@ -1410,6 +1447,9 @@ private: | |||
| UniqueBase<Vst::ChannelContext::IInfoListener>{}, | |||
| SharedBase<IPluginBase, Vst::IEditController>{}, | |||
| UniqueBase<IDependent>{}, | |||
| #if JucePlugin_Enable_ARA | |||
| UniqueBase<Presonus::IPlugInViewEmbedding>{}, | |||
| #endif | |||
| SharedBase<FUnknown, Vst::IEditController>{}); | |||
| if (result.isOk()) | |||
| @@ -1989,6 +2029,12 @@ private: | |||
| { | |||
| pluginEditor.reset (plugin.createEditorIfNeeded()); | |||
| #if JucePlugin_Enable_ARA | |||
| jassert (dynamic_cast<AudioProcessorEditorARAExtension*> (pluginEditor.get()) != nullptr); | |||
| // for proper view embedding, ARA plug-ins must be resizable | |||
| jassert (pluginEditor->isResizable()); | |||
| #endif | |||
| if (pluginEditor != nullptr) | |||
| { | |||
| editorHostContext = std::make_unique<EditorHostContext> (*owner.owner->audioProcessor, | |||
| @@ -2231,12 +2277,61 @@ private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVST3EditController) | |||
| }; | |||
| //============================================================================== | |||
| #if JucePlugin_Enable_ARA | |||
| class JuceARAFactory : public ARA::IMainFactory | |||
| { | |||
| public: | |||
| JuceARAFactory() = default; | |||
| virtual ~JuceARAFactory() = default; | |||
| JUCE_DECLARE_VST3_COM_REF_METHODS | |||
| tresult PLUGIN_API queryInterface (const ::Steinberg::TUID targetIID, void** obj) override | |||
| { | |||
| const auto result = testForMultiple (*this, | |||
| targetIID, | |||
| UniqueBase<ARA::IMainFactory>{}, | |||
| UniqueBase<FUnknown>{}); | |||
| if (result.isOk()) | |||
| return result.extract (obj); | |||
| if (doUIDsMatch (targetIID, JuceARAFactory::iid)) | |||
| { | |||
| addRef(); | |||
| *obj = this; | |||
| return kResultOk; | |||
| } | |||
| *obj = nullptr; | |||
| return kNoInterface; | |||
| } | |||
| //---from ARA::IMainFactory------- | |||
| const ARA::ARAFactory* PLUGIN_API getFactory() SMTG_OVERRIDE | |||
| { | |||
| return createARAFactory(); | |||
| } | |||
| static const FUID iid; | |||
| private: | |||
| //============================================================================== | |||
| std::atomic<int> refCount { 1 }; | |||
| }; | |||
| #endif | |||
| //============================================================================== | |||
| class JuceVST3Component : public Vst::IComponent, | |||
| public Vst::IAudioProcessor, | |||
| public Vst::IUnitInfo, | |||
| public Vst::IConnectionPoint, | |||
| public Vst::IProcessContextRequirements, | |||
| #if JucePlugin_Enable_ARA | |||
| public ARA::IPlugInEntryPoint, | |||
| public ARA::IPlugInEntryPoint2, | |||
| #endif | |||
| public AudioPlayHead | |||
| { | |||
| public: | |||
| @@ -3272,6 +3367,10 @@ private: | |||
| UniqueBase<Vst::IUnitInfo>{}, | |||
| UniqueBase<Vst::IConnectionPoint>{}, | |||
| UniqueBase<Vst::IProcessContextRequirements>{}, | |||
| #if JucePlugin_Enable_ARA | |||
| UniqueBase<ARA::IPlugInEntryPoint>{}, | |||
| UniqueBase<ARA::IPlugInEntryPoint2>{}, | |||
| #endif | |||
| SharedBase<FUnknown, Vst::IComponent>{}); | |||
| if (result.isOk()) | |||
| @@ -3385,6 +3484,27 @@ private: | |||
| bufferMapper.prepare (p, bufferSize); | |||
| } | |||
| //============================================================================== | |||
| #if JucePlugin_Enable_ARA | |||
| const ARA::ARAFactory* PLUGIN_API getFactory() SMTG_OVERRIDE | |||
| { | |||
| return createARAFactory(); | |||
| } | |||
| const ARA::ARAPlugInExtensionInstance* PLUGIN_API bindToDocumentController (ARA::ARADocumentControllerRef /*controllerRef*/) SMTG_OVERRIDE | |||
| { | |||
| ARA_VALIDATE_API_STATE (false && "call is deprecated in ARA 2, host must not call this"); | |||
| return nullptr; | |||
| } | |||
| const ARA::ARAPlugInExtensionInstance* PLUGIN_API bindToDocumentControllerWithRoles (ARA::ARADocumentControllerRef documentControllerRef, | |||
| ARA::ARAPlugInInstanceRoleFlags knownRoles, ARA::ARAPlugInInstanceRoleFlags assignedRoles) SMTG_OVERRIDE | |||
| { | |||
| AudioProcessorARAExtension* araAudioProcessorExtension = dynamic_cast<AudioProcessorARAExtension*> (pluginInstance); | |||
| return araAudioProcessorExtension->bindToARA (documentControllerRef, knownRoles, assignedRoles); | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| ScopedJuceInitialiser_GUI libraryInitialiser; | |||
| @@ -3483,6 +3603,11 @@ DEF_CLASS_IID (JuceAudioProcessor) | |||
| DEF_CLASS_IID (JuceVST3Component) | |||
| #endif | |||
| #if JucePlugin_Enable_ARA | |||
| DECLARE_CLASS_IID (JuceARAFactory, 0xABCDEF01, 0xA1B2C3D4, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) | |||
| DEF_CLASS_IID (JuceARAFactory) | |||
| #endif | |||
| JUCE_END_IGNORE_WARNINGS_MSVC | |||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
| @@ -3611,6 +3736,13 @@ static FUnknown* createControllerInstance (Vst::IHostApplication* host) | |||
| return static_cast<Vst::IEditController*> (new JuceVST3EditController (host)); | |||
| } | |||
| #if JucePlugin_Enable_ARA | |||
| static FUnknown* createARAFactoryInstance (Vst::IHostApplication* /*host*/) | |||
| { | |||
| return static_cast<ARA::IMainFactory*> (new JuceARAFactory()); | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| struct JucePluginFactory; | |||
| static JucePluginFactory* globalFactory = nullptr; | |||
| @@ -3883,6 +4015,20 @@ extern "C" SMTG_EXPORT_SYMBOL IPluginFactory* PLUGIN_API GetPluginFactory() | |||
| kVstVersionString); | |||
| globalFactory->registerClass (controllerClass, createControllerInstance); | |||
| #if JucePlugin_Enable_ARA | |||
| static const PClassInfo2 araFactoryClass (JuceARAFactory::iid, | |||
| PClassInfo::kManyInstances, | |||
| kARAMainFactoryClass, | |||
| JucePlugin_Name, | |||
| JucePlugin_Vst3ComponentFlags, | |||
| JucePlugin_Vst3Category, | |||
| JucePlugin_Manufacturer, | |||
| JucePlugin_VersionString, | |||
| kVstVersionString); | |||
| globalFactory->registerClass (araFactoryClass, createARAFactoryInstance); | |||
| #endif | |||
| } | |||
| else | |||
| { | |||
| @@ -121,3 +121,7 @@ | |||
| #endif | |||
| #include "utility/juce_CreatePluginFilter.h" | |||
| #if JucePlugin_Enable_ARA | |||
| #include "ARA/juce_ARA_Wrapper.h" | |||
| #endif | |||
| @@ -0,0 +1,19 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE 7 technical preview. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| You may use this code under the terms of the GPL v3 | |||
| (see www.gnu.org/licenses). | |||
| For the technical preview this file cannot be licensed commercially. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| #include "ARA/juce_ARA_Wrapper.cpp" | |||
| @@ -30,6 +30,10 @@ inline AudioProcessor* JUCE_API JUCE_CALLTYPE createPluginFilterOfType (AudioPro | |||
| // your createPluginFilter() method must return an object! | |||
| jassert (pluginInstance != nullptr && pluginInstance->wrapperType == type); | |||
| #if JucePlugin_Enable_ARA | |||
| jassert (dynamic_cast<juce::AudioProcessorARAExtension*> (pluginInstance) != nullptr); | |||
| #endif | |||
| return pluginInstance; | |||
| } | |||
| @@ -162,7 +162,16 @@ | |||
| #include "utilities/juce_PluginHostType.h" | |||
| #include "utilities/ARA/juce_ARA_utils.h" | |||
| // This is here to avoid missing-prototype warnings in user code. | |||
| //============================================================================== | |||
| // These declarations are here to avoid missing-prototype warnings in user code. | |||
| // If you're implementing a plugin, you should supply a body for | |||
| // this function in your own code. | |||
| juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter(); | |||
| // If you are implementing an ARA enabled plugin, you need to | |||
| // implement this function somewhere in the codebase by returning | |||
| // SubclassOfARADocumentControllerSpecialisation::createARAFactory<SubclassOfARADocumentControllerSpecialisation>(); | |||
| #if JucePlugin_Enable_ARA | |||
| const ARA::ARAFactory* JUCE_CALLTYPE createARAFactory(); | |||
| #endif | |||