Skip to content

Commit

Permalink
Adding ARA support (#255)
Browse files Browse the repository at this point in the history
* adding ARA support for VST3 wrapper
* version 0.9.2
  • Loading branch information
defiantnerd authored Jun 11, 2024
1 parent 0e25b03 commit 6fa9e1c
Show file tree
Hide file tree
Showing 13 changed files with 402 additions and 15 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ option(CLAP_WRAPPER_BUILD_TESTS "Build test CLAP wrappers" OFF)

project(clap-wrapper
LANGUAGES C CXX
VERSION 0.9.1
VERSION 0.9.2
DESCRIPTION "CLAP-as-X wrappers"
)
set(CLAP_WRAPPER_VERSION "${CMAKE_PROJECT_VERSION}" CACHE STRING "Version of the wrapper project")
Expand Down
2 changes: 2 additions & 0 deletions cmake/wrap_vst3.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function(private_add_vst3_wrapper_sources)
${sd}/src/wrapasvst3.h
${sd}/src/wrapasvst3.cpp
${sd}/src/wrapasvst3_entry.cpp
${sd}/src/detail/ara/ara.h
${sd}/src/detail/vst3/parameter.h
${sd}/src/detail/vst3/parameter.cpp
${sd}/src/detail/vst3/plugview.h
Expand All @@ -26,6 +27,7 @@ function(private_add_vst3_wrapper_sources)
${sd}/src/detail/vst3/process.cpp
${sd}/src/detail/vst3/categories.h
${sd}/src/detail/vst3/categories.cpp
${sd}/src/detail/vst3/aravst3.h
)

target_include_directories(${V3_TARGET}-clap-wrapper-vst3-lib PRIVATE "${sd}/include")
Expand Down
1 change: 1 addition & 0 deletions src/clap_proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ void Plugin::connectClap(const clap_plugin_t* clap)
getExtension(_plugin, _ext._tail, CLAP_EXT_TAIL);
getExtension(_plugin, _ext._gui, CLAP_EXT_GUI);
getExtension(_plugin, _ext._timer, CLAP_EXT_TIMER_SUPPORT);
getExtension(_plugin, _ext._ara, CLAP_EXT_ARA_PLUGINEXTENSION);

getExtension(_plugin, _ext._contextmenu, CLAP_EXT_CONTEXT_MENU);
if (_ext._contextmenu == nullptr)
Expand Down
1 change: 1 addition & 0 deletions src/clap_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ struct ClapPluginExtensions
const clap_plugin_tail_t* _tail = nullptr;
const clap_plugin_timer_support_t* _timer = nullptr;
const clap_plugin_context_menu_t* _contextmenu = nullptr;
const clap_ara_plugin_extension_t* _ara = nullptr;
#if LIN
const clap_plugin_posix_fd_support* _posixfd = nullptr;
#endif
Expand Down
109 changes: 109 additions & 0 deletions src/detail/ara/ara.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#pragma once

#include <clap/clap.h>

// ARA support for clap-wrapper
//
// the structs and identifiers are a copy from the ARA SDK (file ARA_API/ARACLAP.h from https://github.com/Celemony/ARA_API).
// ARA is Copyright (c) 2022-2024, Celemony Software GmbH, All Rights Reserved.
//
// The ARA SDK is available under Apache-2.0 license here:
//
// https://github.com/Celemony/ARA_SDK
//
// More information about ARA can be found here: https://vwww.celemony.com/ara
//
// the clap-wrapper itself does not require the ARA SDK because CLAP is a shell API only and
// just passes interface pointers through extensions between the foreign host and the ARA plugin core.
//
// do not use this file in your clap plugin, use the ARA SDK!
//

//! Factory ID for retrieving the clap_ara_factory_t extension from clap_plugin_entry_t.get_factory()
static CLAP_CONSTEXPR const char CLAP_EXT_ARA_FACTORY[] = "org.ara-audio.ara.factory/2";

//! Extension ID for retrieving the clap_ara_plugin_extension_t from clap_plugin_t.get_extension()
static CLAP_CONSTEXPR const char CLAP_EXT_ARA_PLUGINEXTENSION[] = "org.ara-audio.ara.pluginextension/2";

//! Add this feature if your plugin supports ARA.
//! This allows hosts to detect ARA early on in the setup phase.
#define CLAP_PLUGIN_FEATURE_ARA_SUPPORTED "ara:supported"

//! Add this feature if your plugin requires ARA to operate (will not work as normal insert plug-in).
//! This allows non-ARA CLAP hosts to suppress the plug-in since it cannot be used there.
#define CLAP_PLUGIN_FEATURE_ARA_REQUIRED "ara:required"

// VST3 class category name
#define kARAMainFactoryClass "ARA Main Factory Class"

#ifdef __cplusplus
extern "C"
{
#endif

// substitute types so we don't need to include the ARA SDK in full
typedef void *ARAFactoryPtr;
typedef void *ARAPlugInExtensionInstancePtr;
typedef void *ARADocumentControllerRef;
typedef int32_t ARAPlugInInstanceRoleFlags;

/***************************************************************************************************/
//! Extension interface to connect to ARA at CLAP factory level.
//! The host can pass CLAP_EXT_ARA_FACTORY to clap_plugin_entry_t.get_factory() to directly obtain an
//! ARAFactory, which allows for creating and maintaining the model independently of any clap_plugin_t
//! instances, enabling tasks such as automatic tempo detection or audio-to-MIDI conversion.
//! For rendering and editing the model however, there must be an associated clap_plugin_t provided in
//! the same binary - the descriptor for which is returned at the same index as the related ARAFactory.

typedef struct clap_ara_factory
{
//! Get the number of ARA factories (i.e. ARA-capable plug.-ins) available.
//! Note that the regular clap_plugin_factory can contain more plug-ins if these do not support
//! ARA - make no assumption about items returned here being related to the items returned there
//! in terms of count or order.
uint32_t(CLAP_ABI *get_factory_count)(const struct clap_ara_factory *factory);

//! Get the ARA factory for the plug-in at the given index.
//! The returned pointer must remain valid until clap_plugin_entry_t.deinit() is called.
//! The returned ARAFactory must be equal to the ARAFactory returned from instances of the
//! associated CLAP plug-in through their clap_ara_plugin_extension_t.get_factory().
ARAFactoryPtr(CLAP_ABI *get_ara_factory)(const struct clap_ara_factory *factory, uint32_t index);

//! Get the ID of the CLAP plug-in associated with the ARA factory for the given index.
//! The plug-in must be in the same binary.
//! The returned pointer must remain valid until clap_plugin_entry_t.deinit is called.
const char *(CLAP_ABI *get_plugin_id)(const struct clap_ara_factory *factory, uint32_t index);
} clap_ara_factory_t;

/***************************************************************************************************/
//! Extension interface to connect to ARA at CLAP plug-in level.
//! This interface provides access to the ARA specific extension of a CLAP plug-in.
//! Return an pointer to a clap_ara_plugin_extension_t when clap_plugin_t.get_extension() is called
//! with CLAP_EXT_ARA_PLUGINEXTENSION.

typedef struct clap_ara_plugin_extension
{
//! Access the ARAFactory associated with this plug-in.
ARAFactoryPtr(CLAP_ABI *get_factory)(const clap_plugin_t *plugin);

//! Bind the CLAP instance to an ARA document controller, switching it from "normal" operation
//! to ARA mode with the assigned roles, and exposing the ARA plug-in extension.
//! \p knownRoles encodes all roles that the host considered in its implementation and will explicitly
//! assign to some plug-in instance(s), while \p assignedRoles describes the roles that this specific
//! instance will fulfill.
//! This may be called only once during the lifetime of the CLAP plug-in, before the first call
//! to clap_plugin_t.activate() or clap_host_state_t.load() or other processing related extensions
//! or the creation of the GUI.
//! The ARA document controller must remain valid as long as the plug-in is in use - rendering,
//! showing its UI, etc. However, when tearing down the plug-in, the actual order for deleting
//! the clap_plugin_t instance and for deleting ARA document controller is undefined.
//! Plug-ins must handle both potential destruction orders to allow for a simpler reference
//! counting implementation on the host side.
ARAPlugInExtensionInstancePtr(CLAP_ABI *bind_to_document_controller)(
const clap_plugin_t *plugin, ARADocumentControllerRef documentControllerRef,
ARAPlugInInstanceRoleFlags knownRoles, ARAPlugInInstanceRoleFlags assignedRoles);
} clap_ara_plugin_extension_t;

#ifdef __cplusplus
}
#endif
4 changes: 4 additions & 0 deletions src/detail/clap/fsutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,16 @@ void Library::setupPluginsFromPluginEntry(const char *path)
_pluginEntry->get_factory(CLAP_PLUGIN_FACTORY_INFO_VST3));
_pluginFactoryAUv2Info = static_cast<const clap_plugin_factory_as_auv2 *>(
_pluginEntry->get_factory(CLAP_PLUGIN_FACTORY_INFO_AUV2));
_pluginFactoryARAInfo =
static_cast<const clap_ara_factory_t *>(_pluginEntry->get_factory(CLAP_EXT_ARA_FACTORY));

// detect plugins that do not check the CLAP_PLUGIN_FACTORY_ID
if ((void *)_pluginFactory == (void *)_pluginFactoryVst3Info)
{
// in this case, don't trust anything from there
_pluginFactoryVst3Info = nullptr;
_pluginFactoryAUv2Info = nullptr;
_pluginFactoryARAInfo = nullptr;
}

auto count = _pluginFactory->get_plugin_count(_pluginFactory);
Expand Down
2 changes: 2 additions & 0 deletions src/detail/clap/fsutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ namespace fs = std::filesystem;

#include "clapwrapper/vst3.h"
#include "clapwrapper/auv2.h"
#include "../ara/ara.h"

#if MAC
#include <CoreFoundation/CoreFoundation.h>
Expand All @@ -55,6 +56,7 @@ class Library
const clap_plugin_factory_t* _pluginFactory = nullptr;
const clap_plugin_factory_as_vst3* _pluginFactoryVst3Info = nullptr;
const clap_plugin_factory_as_auv2* _pluginFactoryAUv2Info = nullptr;
const clap_ara_factory_t* _pluginFactoryARAInfo = nullptr;
std::vector<const clap_plugin_descriptor_t*> plugins;
const clap_plugin_info_as_vst3_t* get_vst3_info(uint32_t index) const;

Expand Down
112 changes: 112 additions & 0 deletions src/detail/vst3/aravst3.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#pragma once

#include "../ara/ara.h"

#include "pluginterfaces/base/funknown.h"
#include "pluginterfaces/base/falignpush.h"

// these interfaces are copied from the ARA SDK to maintain compatibility
// without the need of the full ARA SDK
//
// the types handled here are just void* for the intercommunication between
// VST3 Host and CLAP plugin.

#define ARA_DEPRECATED(x)
#define ARA_ADDENDUM(x)

namespace ARA
{
/***************************************************************************************************/
//! Interface class to be implemented by an object provided by the VST3 factory.
//! The host can use the VST3 factory to directly obtain the ARA factory, which allows for creating
//! and maintaining the model independently of any IAudioProcessor instances, enabling tasks such as
//! automatic tempo detection or audio-to-MIDI conversion.
//! For rendering and editing the model however, there must be an associated IAudioProcessor class
//! provided in the same binary.
//! This match is usually trivial because there typically is only one such class in the binary, but
//! there are cases such as WaveShell where multiple plug-ins live in the same binary, and only a
//! subset of those plug-ins support ARA. In this scenario, the plug-in must use the same class name
//! for the matching pair of ARA::IMainFactory and IAudioProcessor classes - this enables the host
//! to quickly identify the matching pairs without having to create instances of all the
//! IAudioProcessor classes to query their IPlugInEntryPoint::getFactory ()->factoryID to perform
//! the matching.
class IMainFactory : public Steinberg::FUnknown
{
public:
//! Get the ARA factory.
//! The returned pointer must remain valid throughout the lifetime of the object that provided it.
//! The returned ARAFactory must be equal to the ARAFactory provided by the associated
//! IAudioProcessor class through its IPlugInEntryPoint.
virtual ARAFactoryPtr PLUGIN_API getFactory() = 0;
virtual ~IMainFactory()
{
}
static const Steinberg::FUID iid;
};

//! Class category name for the ARA::IMainFactory.
#if !defined(kARAMainFactoryClass)
#define kARAMainFactoryClass "ARA Main Factory Class"
#endif

DECLARE_CLASS_IID(IMainFactory, 0xDB2A1669, 0xFAFD42A5, 0xA82F864F, 0x7B6872EA)

/***************************************************************************************************/
//! Interface class to be implemented by the VST3 IAudioProcessor component (kVstAudioEffectClass).
class IPlugInEntryPoint : public Steinberg::FUnknown
{
public:
//! Get the ARA factory.
//! The returned pointer must remain valid throughout the lifetime of the object that provided it.
//! The returned ARAFactory must be equal to the ARAFactory provided by the associated IMainFactory.
//! To prevent ambiguities, the name of the plug-in as stored in the PClassInfo.name of this
//! class must match the ARAFactory.plugInName returned here.
virtual ARAFactoryPtr PLUGIN_API getFactory() = 0;

//! Bind the VST3 instance to an ARA document controller, switching it from "normal" operation
//! to ARA mode, and exposing the ARA plug-in extension.
//! Note that since ARA 2.0, this call has been deprecated and replaced with
//! bindToDocumentControllerWithRoles ().
//! This deprecated call is equivalent to the new call with no known roles set, however all
//! ARA 1.x hosts are in fact using all instances with playback renderer, edit renderer and
//! editor view role enabled, so plug-ins implementing ARA 1 backwards compatibility can
//! safely assume those three roles to be enabled if this call was made.
//! Same call order rules as bindToDocumentControllerWithRoles () apply.
ARA_DEPRECATED(2_0_Draft)
virtual ARAPlugInExtensionInstancePtr PLUGIN_API
bindToDocumentController(ARADocumentControllerRef documentControllerRef) = 0;

static const Steinberg::FUID iid;
};

DECLARE_CLASS_IID(IPlugInEntryPoint, 0x12814E54, 0xA1CE4076, 0x82B96813, 0x16950BD6)

//! ARA 2 extension of IPlugInEntryPoint, from the ARA SDK
class ARA_ADDENDUM(2_0_Draft) IPlugInEntryPoint2 : public Steinberg::FUnknown
{
public:
//! Extended version of bindToDocumentController ():
//! bind the VST3 instance to an ARA document controller, switching it from "normal" operation
//! to ARA mode with the assigned roles, and exposing the ARA plug-in extension.
//! \p knownRoles encodes all roles that the host considered in its implementation and will explicitly
//! assign to some plug-in instance(s), while \p assignedRoles describes the roles that this specific
//! instance will fulfill.
//! This may be called only once during the lifetime of the IAudioProcessor component, before
//! the first call to setActive () or setState () or getProcessContextRequirements () or the
//! creation of the GUI (see IPlugView).
//! The ARA document controller must remain valid as long as the plug-in is in use - rendering,
//! showing its UI, etc. However, when tearing down the plug-in, the actual order for deleting
//! the IAudioProcessor instance and for deleting ARA document controller is undefined.
//! Plug-ins must handle both potential destruction orders to allow for a simpler reference
//! counting implementation on the host side.
virtual ARAPlugInExtensionInstancePtr PLUGIN_API bindToDocumentControllerWithRoles(
ARADocumentControllerRef documentControllerRef, ARAPlugInInstanceRoleFlags knownRoles,
ARAPlugInInstanceRoleFlags assignedRoles) = 0;
static const Steinberg::FUID iid;
};

DECLARE_CLASS_IID(IPlugInEntryPoint2, 0xCD9A5913, 0xC9EB46D7, 0x96CA53AD, 0xD1DB89F5)

} // namespace ARA

#include "pluginterfaces/base/falignpop.h"
4 changes: 4 additions & 0 deletions src/detail/vst3/categories.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include <clap/plugin-features.h>
#include <pluginterfaces/base/ipluginbase.h>
#include <pluginterfaces/vst/ivstaudioprocessor.h>
#include "../ara/ara.h"

using namespace Steinberg;
using namespace Steinberg::Vst;
Expand Down Expand Up @@ -101,6 +102,9 @@ static const struct _translate
{ CLAP_PLUGIN_FEATURE_MIXING , "Mixing"},
{ CLAP_PLUGIN_FEATURE_MASTERING , "Mastering"},

{ CLAP_PLUGIN_FEATURE_ARA_SUPPORTED , "OnlyARA" },
{ CLAP_PLUGIN_FEATURE_ARA_REQUIRED , "OnlyARA" },

{ "external" , "External"},

{nullptr, nullptr}
Expand Down
5 changes: 4 additions & 1 deletion src/detail/vst3/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,10 @@ void ProcessAdapter::process(Steinberg::Vst::ProcessData& data)
;

_transport.song_pos_beats = 0;
_transport.song_pos_seconds = 0;

// samplerate and projectTimeSamples are always valid
_transport.song_pos_seconds = doubleToSecTime(_vstdata->processContext->projectTimeSamples /
_vstdata->processContext->sampleRate);

if ((_vstdata->processContext->state & Vst::ProcessContext::kProjectTimeMusicValid))
{
Expand Down
34 changes: 34 additions & 0 deletions src/wrapasvst3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
#define S16(x) u##x
#endif

DEF_CLASS_IID(ARA::IPlugInEntryPoint)
DEF_CLASS_IID(ARA::IPlugInEntryPoint2)

#if 0
--- 8< ---
struct ClapHostExtensions
Expand Down Expand Up @@ -479,6 +482,37 @@ tresult ClapAsVst3::executeMenuItem(int32 tag)
return kResultOk;
}

ARAFactoryPtr PLUGIN_API ClapAsVst3::getFactory()
{
LOGDETAIL("-> ARA::IPlugInEntryPoint::getFactory");
if (_plugin->_ext._ara)
{
return _plugin->_ext._ara->get_factory(_plugin->_plugin);
}
return nullptr;
}

ARAPlugInExtensionInstancePtr PLUGIN_API
ClapAsVst3::bindToDocumentController(ARADocumentControllerRef documentControllerRef)
{
LOGDETAIL("-> ARA::IPlugInEntryPoint::bindToDocumentController (!!! DEPRECATED !!!)");
// "call is deprecated in ARA 2, host must not call this"
return nullptr;
}

ARAPlugInExtensionInstancePtr PLUGIN_API ClapAsVst3::bindToDocumentControllerWithRoles(
ARADocumentControllerRef documentControllerRef, ARAPlugInInstanceRoleFlags knownRoles,
ARAPlugInInstanceRoleFlags assignedRoles)
{
LOGDETAIL("-> ARA::IPlugInEntryPoint2::bindToDocumentControllerWithRoles");
if (_plugin->_ext._ara)
{
return _plugin->_ext._ara->bind_to_document_controller(_plugin->_plugin, documentControllerRef,
knownRoles, assignedRoles);
}
return nullptr;
}

static Vst::SpeakerArrangement speakerArrFromPortType(const char* port_type)
{
static const std::pair<const char*, Vst::SpeakerArrangement> arrangementmap[] = {
Expand Down
Loading

0 comments on commit 6fa9e1c

Please sign in to comment.