Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing VST3 off-by-one bug in stepped parameters #261

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 62 additions & 7 deletions .github/workflows/pullreq.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
name: Linux External SDK
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
submodules: recursive

Expand Down Expand Up @@ -73,11 +73,11 @@ jobs:
run_aptget: false
name: Windows gcc/minGW

- os: windows-latest
cmakeargs: -GNinja -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang
install_ninja: true
run_aptget: false
name: Windows clang
#- os: windows-latest
# cmakeargs: -GNinja -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang
# install_ninja: true
# run_aptget: false
# name: Windows clang

- os: ubuntu-latest
cmakeargs: -DCMAKE_CXX_COMPILER=g++-11 -DCMAKE_C_COMPILER=gcc-11
Expand Down Expand Up @@ -129,7 +129,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
submodules: recursive

Expand All @@ -156,3 +156,58 @@ jobs:
find build -name downloadplug.vst3 -print
find build -name downloadplug.component -print


build_feature_clapfirst-test:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: windows-latest
cmakeargs: -A x64
install_ninja: false
run_aptget: false
name: ClapFirst Windows 64bit MSVC

- os: ubuntu-latest
cmakeargs: -DCMAKE_CXX_COMPILER=g++-11 -DCMAKE_C_COMPILER=gcc-11
install_ninja: false
run_aptget: true
name: ClapFirst Linux gcc11

- os: macos-latest
cmakeargs: -G"Xcode"
install_ninja: false
run_aptget: false
name: ClapFirst MacOS Xcode


steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive

- name: Apt Update
if: ${{ matrix.run_aptget }}
run: sudo apt-get update

- name: Get Deps
if: ${{ matrix.run_aptget }}
run: sudo apt-get install -y alsa alsa-tools libasound2-dev libjack-dev libgtk-3-dev

- name: Install Ninja
if: ${{ matrix.install_ninja }}
uses: seanmiddleditch/gha-setup-ninja@master

- name: Build project
run: |
cmake -S . -B ./build ${{ matrix.cmakeargs }} -DCMAKE_BUILD_TYPE=Debug -DCLAP_WRAPPER_DOWNLOAD_DEPENDENCIES=TRUE -DCLAP_WRAPPER_BUILD_TESTS=TRUE
cmake --build ./build --config Debug --target clap-first-distortion_all_plugins

- name: Show Build Results
shell: bash
run: |
find build -name "ClapFirst*" -print


6 changes: 5 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ set(CMAKE_COLOR_DIAGNOSTICS ON)
# can just build with this turned on and it will forward all note expressions to your CLAP
option(CLAP_SUPPORTS_ALL_NOTE_EXPRESSIONS "Does the underlying CLAP support note expressions" OFF)
option(CLAP_WRAPPER_WINDOWS_SINGLE_FILE "Build a single fine (rather than folder) on windows" ON)
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 Expand Up @@ -131,3 +132,6 @@ else()

endif()

if (${CLAP_WRAPPER_BUILD_TESTS})
add_subdirectory(tests)
endif()
5 changes: 5 additions & 0 deletions cmake/base_sdks.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@ function(guarantee_rtaudio)
set(BUILD_TESTING OFF CACHE BOOL "don't eject test targets")


if (APPLE)
# If you brew install jack, rtaudio will find it but not link it properly
set(RTAUDIO_API_JACK FALSE CACHE STRING "No jack by default on macos")
endif()

if (NOT "${RTAUDIO_SDK_ROOT}" STREQUAL "")
# Use the provided root
elseif (${CLAP_WRAPPER_DOWNLOAD_DEPENDENCIES})
Expand Down
5 changes: 4 additions & 1 deletion 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 Expand Up @@ -84,7 +86,8 @@ function(target_add_vst3_wrapper)
target_link_libraries(${V3_TARGET}-clap-wrapper-vst3-lib PUBLIC clap base-sdk-vst3)

# clap-wrapper-extensions are PUBLIC, so a clap linking the library can access the clap-wrapper-extensions
target_link_libraries(${V3_TARGET}-clap-wrapper-vst3-lib PUBLIC clap-wrapper-extensions clap-wrapper-compile-options clap-wrapper-shared-detail)
target_link_libraries(${V3_TARGET}-clap-wrapper-vst3-lib PUBLIC clap-wrapper-extensions clap-wrapper-shared-detail)
target_link_libraries(${V3_TARGET}-clap-wrapper-vst3-lib PRIVATE clap-wrapper-compile-options)

target_compile_options(${V3_TARGET}-clap-wrapper-vst3-lib PRIVATE
-DCLAP_SUPPORTS_ALL_NOTE_EXPRESSIONS=$<IF:$<BOOL:${V3_SUPPORTS_ALL_NOTE_EXPRESSIONS}>,1,0>
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
Loading
Loading