diff --git a/.github/workflows/pullreq.yml b/.github/workflows/pullreq.yml index c298ed40..40861273 100644 --- a/.github/workflows/pullreq.yml +++ b/.github/workflows/pullreq.yml @@ -30,6 +30,7 @@ jobs: mkdir deps cd deps git clone https://github.com/free-audio/clap + git clone https://github.com/free-audio/clap-helpers git clone https://github.com/steinbergmedia/vst3sdk cd vst3sdk # temporary workaround, switch to vst3 sdk 3.7.7 @@ -39,7 +40,7 @@ jobs: - name: Build project run: | - cmake -S . -B ./build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -DCLAP_SDK_ROOT=deps/clap -DVST3_SDK_ROOT=deps/vst3sdk -DCLAP_WRAPPER_OUTPUT_NAME=testplug + cmake -S . -B ./build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -DCLAP_SDK_ROOT=deps/clap -DCLAP_HELPERS_SDK_ROOT=deps/clap-helpers -DVST3_SDK_ROOT=deps/vst3sdk -DCLAP_WRAPPER_OUTPUT_NAME=testplug cmake --build ./build --config Debug - name: Show Build Results diff --git a/CMakeLists.txt b/CMakeLists.txt index f8b14938..9f9922fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ # complete description # # CLAP_SDK_ROOT The location of the clap library. Defaults to ../clap +# CLAP_HELPERS_SDK_ROOT The location of the clap-helpers library. Defaults to ../clap-helpers # VST3_SDK_ROOT The location of the VST3 sdk. Defaults tp ../vst3sdk # # CLAP_WRAPPER_OUTPUT_NAME The name of the resulting .vst3 or .component diff --git a/README.md b/README.md index 182eeeff..16ea3d5b 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ git clone https://github.com/free-audio/clap-wrapper.git mkdir build cmake -B build \ -DCLAP_SDK_ROOT={path to clap sdk} \ + -DCLAP_HELPERS_SDK_ROOT={path to clap-helpers sdk} \ -DVST3_SDK_ROOT={path to vst3 sdk} \ -DCLAP_WRAPPER_OUTPUT_NAME="The Name of your CLAP" ``` diff --git a/cmake/base_sdks.cmake b/cmake/base_sdks.cmake index e60af8b5..6ad116ba 100644 --- a/cmake/base_sdks.cmake +++ b/cmake/base_sdks.cmake @@ -91,6 +91,41 @@ function(guarantee_clap) add_subdirectory(${CLAP_SDK_ROOT} base-sdk-clap EXCLUDE_FROM_ALL) endfunction(guarantee_clap) +function(guarantee_clap_helpers) + if (TARGET clap-helpers) + if (NOT TARGET base-sdk-clap-helpers) + add_library(base-sdk-clap-helpers ALIAS clap-helpers) + endif() + return() + endif() + + + if (NOT "${CLAP_HELPERS_SDK_ROOT}" STREQUAL "") + # Use the provided root + elseif (${CLAP_WRAPPER_DOWNLOAD_DEPENDENCIES}) + guarantee_cpm() + CPMAddPackage( + NAME "clap-helpers" + GITHUB_REPOSITORY "free-audio/clap-helpers" + GIT_TAG "7b53a685e11465154b4ccba3065224dbcbf8a893" + EXCLUDE_FROM_ALL TRUE + DOWNLOAD_ONLY TRUE + SOURCE_DIR cpm/clap-helpers + ) + set(CLAP_HELPERS_SDK_ROOT "${CMAKE_CURRENT_BINARY_DIR}/cpm/clap-helpers") + else() + search_for_sdk_source(SDKDIR clap-helpers RESULT CLAP_HELPERS_SDK_ROOT) + endif() + + cmake_path(CONVERT "${CLAP_HELPERS_SDK_ROOT}" TO_CMAKE_PATH_LIST CLAP_HELPERS_SDK_ROOT) + if(NOT EXISTS "${CLAP_HELPERS_SDK_ROOT}/include/clap/helpers/macros.hh") + message(FATAL_ERROR "There is no CLAP_HELPERS SDK at ${CLAP_HELPERS_SDK_ROOT}. Please set CLAP_HELPERS_SDK_ROOT appropriately ") + endif() + + message(STATUS "clap-wrapper: Configuring clap-helpers sdk") + add_subdirectory(${CLAP_HELPERS_SDK_ROOT} base-sdk-clap-helpers EXCLUDE_FROM_ALL) +endfunction(guarantee_clap_helpers) + function(guarantee_vst3sdk) if (TARGET base-sdk-vst3) return() diff --git a/cmake/shared_prologue.cmake b/cmake/shared_prologue.cmake index e6a27f37..aec9111b 100644 --- a/cmake/shared_prologue.cmake +++ b/cmake/shared_prologue.cmake @@ -142,7 +142,7 @@ function(guarantee_clap_wrapper_shared) src/detail/clap/fsutil.cpp src/detail/clap/automation.h ) - target_link_libraries(clap-wrapper-shared-detail PUBLIC clap clap-wrapper-extensions clap-wrapper-compile-options) + target_link_libraries(clap-wrapper-shared-detail PUBLIC clap clap-helpers clap-wrapper-extensions clap-wrapper-compile-options) target_include_directories(clap-wrapper-shared-detail PUBLIC libs/fmt) target_include_directories(clap-wrapper-shared-detail PUBLIC src) diff --git a/cmake/wrapper_functions.cmake b/cmake/wrapper_functions.cmake index b6d38ef9..da5eacb6 100644 --- a/cmake/wrapper_functions.cmake +++ b/cmake/wrapper_functions.cmake @@ -21,6 +21,7 @@ include(cmake/shared_prologue.cmake) include(cmake/base_sdks.cmake) guarantee_clap() +guarantee_clap_helpers() guarantee_clap_wrapper_shared() diff --git a/src/clap_proxy.cpp b/src/clap_proxy.cpp index eba01a3e..2e2d2754 100644 --- a/src/clap_proxy.cpp +++ b/src/clap_proxy.cpp @@ -1,5 +1,6 @@ #include "clap_proxy.h" #include "detail/clap/fsutil.h" +#include #include #if MAC || LIN @@ -11,119 +12,15 @@ #include #endif -namespace Clap -{ -namespace HostExt -{ -static Plugin* self(const clap_host_t* host) -{ - return static_cast(host->host_data); -} - -void host_log(const clap_host_t* host, clap_log_severity severity, const char* msg) -{ - self(host)->log(severity, msg); -} - -clap_host_log_t log = {host_log}; - -void rescan(const clap_host_t* host, clap_param_rescan_flags flags) -{ - self(host)->param_rescan(flags); -} - -// Clears references to a parameter. -// [main-thread] -void clear(const clap_host_t* host, clap_id param_id, clap_param_clear_flags flags) -{ -} +template class clap::helpers::Host; -// Request a parameter flush. -// -// If the plugin is processing, this will result in no action. The process call -// will run normally. If plugin isn't processing, the host will make a subsequent -// call to clap_plugin_params->flush(). As a result, this function is always -// safe to call from a non-audio thread (typically the UI thread on a gesture) -// whether processing is active or not. -// -// This must not be called on the [audio-thread]. -// [thread-safe,!audio-thread] -void request_flush(const clap_host_t* host) -{ - self(host)->param_request_flush(); -} -clap_host_params_t params = {rescan, clear, request_flush}; - -bool is_main_thread(const clap_host_t* host) -{ - return self(host)->is_main_thread(); -} - -// Returns true if "this" thread is one of the audio threads. -// [thread-safe] -bool is_audio_thread(const clap_host_t* host) -{ - return self(host)->is_audio_thread(); -} - -clap_host_thread_check_t threadcheck = {is_main_thread, is_audio_thread}; - -static void resize_hints_changed(const clap_host_t* host) -{ - self(host)->resize_hints_changed(); -} -static bool request_resize(const clap_host_t* host, uint32_t width, uint32_t height) -{ - return self(host)->request_resize(width, height); -} -static bool request_show(const clap_host_t* host) -{ - return self(host)->request_show(); -} -static bool request_hide(const clap_host_t* host) -{ - return self(host)->request_hide(); -} -static void closed(const clap_host_t* host, bool was_destroyed) -{ - self(host)->closed(was_destroyed); -} - -const clap_host_gui hostgui = {resize_hints_changed, request_resize, request_show, request_hide, closed}; - -const clap_host_timer_support hosttimer = { - /* register_timer */ [](const clap_host_t* host, uint32_t period_ms, clap_id* timer_id) -> bool - { return self(host)->register_timer(period_ms, timer_id); }, - /* unregister_timer */ - [](const clap_host_t* host, clap_id timer_id) -> bool - { return self(host)->unregister_timer(timer_id); }}; - -#if LIN -const clap_host_posix_fd_support hostposixfd = { - [](const clap_host_t* host, int fd, clap_posix_fd_flags_t flags) -> bool - { return self(host)->register_fd(fd, flags); }, - [](const clap_host_t* host, int fd, clap_posix_fd_flags_t flags) -> bool - { return self(host)->modify_fd(fd, flags); }, - [](const clap_host_t* host, int fd) -> bool { return self(host)->unregister_fd(fd); }}; -#endif - -const clap_host_latency latency = {[](const clap_host_t* host) -> void - { self(host)->latency_changed(); }}; - -static void tail_changed(const clap_host_t* host) +namespace Clap { - self(host)->tail_changed(); -} - -const clap_host_tail tail = {tail_changed}; - -} // namespace HostExt - std::shared_ptr Plugin::createInstance(const clap_plugin_factory* fac, const std::string& id, Clap::IHost* host) { auto plug = std::shared_ptr(new Plugin(host)); - auto instance = fac->create_plugin(fac, plug->getClapHostInterface(), id.c_str()); + auto instance = fac->create_plugin(fac, plug->clapHost(), id.c_str()); plug->connectClap(instance); return plug; @@ -143,8 +40,8 @@ std::shared_ptr Plugin::createInstance(Clap::Library& library, size_t in if (library.plugins.size() > index) { auto plug = std::shared_ptr(new Plugin(host)); - auto instance = library._pluginFactory->create_plugin( - library._pluginFactory, plug->getClapHostInterface(), library.plugins[index]->id); + auto instance = library._pluginFactory->create_plugin(library._pluginFactory, plug->clapHost(), + library.plugins[index]->id); plug->connectClap(instance); return plug; @@ -153,16 +50,7 @@ std::shared_ptr Plugin::createInstance(Clap::Library& library, size_t in } Plugin::Plugin(IHost* host) - : _host{CLAP_VERSION, - this, - "Clap-As-VST3-Wrapper", - "defiant nerd", - "https://www.defiantnerd.com", - "0.0.1", - Plugin::clapExtension, - Plugin::clapRequestRestart, - Plugin::clapRequestProcess, - Plugin::clapRequestCallback} + : PluginHostBase{"Clap-As-VST3-Wrapper", "defiant nerd", "https://www.defiantnerd.com", "0.0.1"} , _parentHost(host) { } @@ -328,17 +216,17 @@ const clap_plugin_gui_t* Plugin::getUI() const return nullptr; } -void Plugin::latency_changed() +void Plugin::latencyChanged() noexcept { _parentHost->latency_changed(); } -void Plugin::tail_changed() +void Plugin::tailChanged() noexcept { _parentHost->tail_changed(); } -void Plugin::log(clap_log_severity severity, const char* msg) +void Plugin::logLog(clap_log_severity severity, const char* msg) const noexcept { std::string n; switch (severity) @@ -382,7 +270,7 @@ void Plugin::log(clap_log_severity severity, const char* msg) #endif } -bool Plugin::is_main_thread() const +bool Plugin::threadCheckIsMainThread() const noexcept { if (this->_main_thread_override > 0) { @@ -391,7 +279,7 @@ bool Plugin::is_main_thread() const return _main_thread_id == std::this_thread::get_id(); } -bool Plugin::is_audio_thread() const +bool Plugin::threadCheckIsAudioThread() const noexcept { if (this->_audio_thread_override > 0) { @@ -410,80 +298,45 @@ CLAP_NODISCARD Raise Plugin::AlwaysMainThread() return Raise(this->_main_thread_override); } -void Plugin::param_rescan(clap_param_rescan_flags flags) +void Plugin::paramsRescan(clap_param_rescan_flags flags) noexcept { _parentHost->param_rescan(flags); } -void Plugin::param_clear(clap_id param, clap_param_clear_flags flags) +void Plugin::paramsClear(clap_id param, clap_param_clear_flags flags) noexcept { _parentHost->param_clear(param, flags); } -void Plugin::param_request_flush() +void Plugin::paramsRequestFlush() noexcept { _parentHost->param_request_flush(); } -// Query an extension. -// [thread-safe] -const void* Plugin::clapExtension(const clap_host* /*host*/, const char* extension) +void Plugin::requestRestart() noexcept { - if (!strcmp(extension, CLAP_EXT_LOG)) return &HostExt::log; - if (!strcmp(extension, CLAP_EXT_PARAMS)) return &HostExt::params; - if (!strcmp(extension, CLAP_EXT_THREAD_CHECK)) return &HostExt::threadcheck; - if (!strcmp(extension, CLAP_EXT_GUI)) return &HostExt::hostgui; - if (!strcmp(extension, CLAP_EXT_TIMER_SUPPORT)) return &HostExt::hosttimer; -#if LIN - if (!strcmp(extension, CLAP_EXT_POSIX_FD_SUPPORT)) return &HostExt::hostposixfd; -#endif - if (!strcmp(extension, CLAP_EXT_LATENCY)) return &HostExt::latency; - if (!strcmp(extension, CLAP_EXT_TAIL)) - { - return &HostExt::tail; - } - if (!strcmp(extension, CLAP_EXT_RENDER)) - { - // TODO: implement CLAP_EXT_RENDER - } - - return nullptr; + _parentHost->restartPlugin(); } -// Request the host to schedule a call to plugin->on_main_thread(plugin) on the main thread. -// [thread-safe] -void Plugin::clapRequestCallback(const clap_host* host) +void Plugin::requestProcess() noexcept { - auto self = static_cast(host->host_data); - self->_parentHost->request_callback(); -} - -// Request the host to deactivate and then reactivate the plugin. -// The operation may be delayed by the host. -// [thread-safe] -void Plugin::clapRequestRestart(const clap_host* host) -{ - auto self = static_cast(host->host_data); - self->_parentHost->restartPlugin(); + // right now, I don't know how to communicate this to the host + // in VST3 you can't force processing... } -// Request the host to activate and start processing the plugin. -// This is useful if you have external IO and need to wake up the plugin from "sleep". -// [thread-safe] -void Plugin::clapRequestProcess(const clap_host* host) +void Plugin::requestCallback() noexcept { - // right now, I don't know how to communicate this to the host - // in VST3 you can't force processing... + _parentHost->request_callback(); } // Registers a periodic timer. // The host may adjust the period if it is under a certain threshold. // 30 Hz should be allowed. // [main-thread] -bool Plugin::register_timer(uint32_t period_ms, clap_id* timer_id) +bool Plugin::timerSupportRegisterTimer(uint32_t period_ms, clap_id* timer_id) noexcept { return _parentHost->register_timer(period_ms, timer_id); } -bool Plugin::unregister_timer(clap_id timer_id) +bool Plugin::timerSupportUnregisterTimer(clap_id timer_id) noexcept { return _parentHost->unregister_timer(timer_id); } @@ -522,15 +375,15 @@ int64_t CLAP_ABI StateMemento::_write(const struct clap_ostream* stream, const v } #if LIN -bool Plugin::register_fd(int fd, clap_posix_fd_flags_t flags) +bool Plugin::posixFdSupportRegisterFd(int fd, clap_posix_fd_flags_t flags) noexcept { return _parentHost->register_fd(fd, flags); } -bool Plugin::modify_fd(int fd, clap_posix_fd_flags_t flags) +bool Plugin::posixFdSupportModifyFd(int fd, clap_posix_fd_flags_t flags) noexcept { return _parentHost->modify_fd(fd, flags); } -bool Plugin::unregister_fd(int fd) +bool Plugin::posixFdSupportUnregisterFd(int fd) noexcept { return _parentHost->unregister_fd(fd); } diff --git a/src/clap_proxy.h b/src/clap_proxy.h index cb32b5e5..4e69ea8d 100644 --- a/src/clap_proxy.h +++ b/src/clap_proxy.h @@ -25,6 +25,17 @@ #endif #include "detail/clap/fsutil.h" +#include + +namespace Clap +{ +constexpr auto Plugin_MH = clap::helpers::MisbehaviourHandler::Ignore; +constexpr auto Plugin_CL = clap::helpers::CheckingLevel::None; + +using PluginHostBase = clap::helpers::Host; +} // namespace Clap + +extern template class clap::helpers::Host; namespace Clap { @@ -119,7 +130,7 @@ class Raise /// Plugin is the `host` for the CLAP plugin instance /// and the interface for the VST3 plugin wrapper /// -class Plugin +class Plugin : Clap::PluginHostBase { public: static std::shared_ptr createInstance(const clap_plugin_factory*, const std::string& id, @@ -130,10 +141,6 @@ class Plugin protected: // only the Clap::Library is allowed to create instances Plugin(IHost* host); - const clap_host_t* getClapHostInterface() - { - return &_host; - } void connectClap(const clap_plugin_t* clap); public: @@ -164,71 +171,101 @@ class Plugin ClapPluginExtensions _ext; const clap_plugin_t* _plugin = nullptr; - void log(clap_log_severity severity, const char* msg); - - // threadcheck - bool is_main_thread() const; - bool is_audio_thread() const; - // param - void param_rescan(clap_param_rescan_flags flags); - void param_clear(clap_id param, clap_param_clear_flags flags); - void param_request_flush(); + CLAP_NODISCARD Raise AlwaysAudioThread(); + CLAP_NODISCARD Raise AlwaysMainThread(); - // latency - void latency_changed(); + protected: + //////////////////// + // PluginHostBase // + //////////////////// - // tail - void tail_changed(); + // clap_host + void requestRestart() noexcept override; + void requestProcess() noexcept override; + void requestCallback() noexcept override; - // hostgui - void resize_hints_changed() + // clap_host_gui + bool implementsGui() const noexcept override { + return true; } - bool request_resize(uint32_t width, uint32_t height) + void guiResizeHintsChanged() noexcept override { - if (_parentHost->gui_can_resize()) - { - _parentHost->gui_request_resize(width, height); - return true; - } - return false; } - bool request_show() + bool guiRequestResize(uint32_t width, uint32_t height) noexcept override + { + if (!_parentHost->gui_can_resize()) return false; + + _parentHost->gui_request_resize(width, height); + return true; + } + bool guiRequestShow() noexcept override { return _parentHost->gui_request_show(); } - bool request_hide() + bool guiRequestHide() noexcept override { return false; } - void closed(bool was_destroyed) + void guiClosed(bool /*wasDestroyed*/) noexcept override + { + } + + // clap_host_latency + bool implementsLatency() const noexcept override { + return true; } + void latencyChanged() noexcept override; - // clap_timer support - bool register_timer(uint32_t period_ms, clap_id* timer_id); - bool unregister_timer(clap_id timer_id); + // clap_host_log + bool implementsLog() const noexcept override + { + return true; + } + void logLog(clap_log_severity severity, const char* message) const noexcept override; -#if LIN - bool register_fd(int fd, clap_posix_fd_flags_t flags); - bool modify_fd(int fd, clap_posix_fd_flags_t flags); - bool unregister_fd(int fd); + // clap_host_params + bool implementsParams() const noexcept override + { + return true; + } + void paramsRescan(clap_param_rescan_flags flags) noexcept override; + void paramsClear(clap_id paramId, clap_param_clear_flags flags) noexcept override; + void paramsRequestFlush() noexcept override; +#if LIN + // clap_host_posix_fd_support + bool implementsPosixFdSupport() const noexcept override + { + return true; + } + bool posixFdSupportRegisterFd(int fd, clap_posix_fd_flags_t flags) noexcept override; + bool posixFdSupportModifyFd(int fd, clap_posix_fd_flags_t flags) noexcept override; + bool posixFdSupportUnregisterFd(int fd) noexcept override; #endif - CLAP_NODISCARD Raise AlwaysAudioThread(); - CLAP_NODISCARD Raise AlwaysMainThread(); - private: - static const void* clapExtension(const clap_host* host, const char* extension); - static void clapRequestCallback(const clap_host* host); - static void clapRequestRestart(const clap_host* host); - static void clapRequestProcess(const clap_host* host); + // clap_host_tail + bool implementsTail() const noexcept override + { + return true; + } + void tailChanged() noexcept override; - //static bool clapIsMainThread(const clap_host* host); - //static bool clapIsAudioThread(const clap_host* host); + // clap_host_timer_support + bool implementsTimerSupport() const noexcept override + { + return true; + } + bool timerSupportRegisterTimer(uint32_t periodMs, clap_id* timerId) noexcept override; + bool timerSupportUnregisterTimer(clap_id timerId) noexcept override; - clap_host_t _host; // the host_t structure for the proxy + // clap_host_thread_check + bool threadCheckIsMainThread() const noexcept override; + bool threadCheckIsAudioThread() const noexcept override; + + private: IHost* _parentHost = nullptr; const std::thread::id _main_thread_id = std::this_thread::get_id(); std::atomic _audio_thread_override = 0;