From 03457f0f0f2a5a3729ca053229d525f4fcfffeda Mon Sep 17 00:00:00 2001 From: Paul Walker Date: Mon, 10 Jun 2024 09:40:43 -0400 Subject: [PATCH] Add an small example and test of clap-first in-linked wrapper - Bring over my c99 distortion and make it C++ - Show that making all flavors as statically linked - add it to CI --- .github/workflows/pullreq.yml | 69 ++- CMakeLists.txt | 4 + cmake/base_sdks.cmake | 5 + cmake/wrap_vst3.cmake | 3 +- tests/CMakeLists.txt | 1 + tests/clap-first-example/CMakeLists.txt | 103 ++++ tests/clap-first-example/distortion_clap.cpp | 510 ++++++++++++++++++ .../distortion_clap_entry.cpp | 58 ++ 8 files changed, 745 insertions(+), 8 deletions(-) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/clap-first-example/CMakeLists.txt create mode 100644 tests/clap-first-example/distortion_clap.cpp create mode 100644 tests/clap-first-example/distortion_clap_entry.cpp diff --git a/.github/workflows/pullreq.yml b/.github/workflows/pullreq.yml index beb2e1c4..0c3cd625 100644 --- a/.github/workflows/pullreq.yml +++ b/.github/workflows/pullreq.yml @@ -21,7 +21,7 @@ jobs: name: Linux External SDK steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: recursive @@ -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 @@ -129,7 +129,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: recursive @@ -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 + + diff --git a/CMakeLists.txt b/CMakeLists.txt index aedfcd85..d46c664f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ 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 @@ -131,3 +132,6 @@ else() endif() +if (${CLAP_WRAPPER_BUILD_TESTS}) + add_subdirectory(tests) +endif() diff --git a/cmake/base_sdks.cmake b/cmake/base_sdks.cmake index e60af8b5..64bd1e46 100644 --- a/cmake/base_sdks.cmake +++ b/cmake/base_sdks.cmake @@ -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}) diff --git a/cmake/wrap_vst3.cmake b/cmake/wrap_vst3.cmake index 07c47e2e..8850fedf 100644 --- a/cmake/wrap_vst3.cmake +++ b/cmake/wrap_vst3.cmake @@ -84,7 +84,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=$,1,0> diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..b89abe7a --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(clap-first-example) diff --git a/tests/clap-first-example/CMakeLists.txt b/tests/clap-first-example/CMakeLists.txt new file mode 100644 index 00000000..8e91fa4c --- /dev/null +++ b/tests/clap-first-example/CMakeLists.txt @@ -0,0 +1,103 @@ +project(clap-first-distortion) + +# By A "Clap First" plugin, we mean a plugin which is *just* implmented as +# CLAP and uses this wrapper to project into the various other platforms. +# +# One special form of clap first plugin does that by creating a +# the platform plugins with the entire clap library built in. While there's +# a few ways to do this the pattern which eemse to work best is +# +# 1. Write the etnire DSP, handling, etc... of your clap as a library without +# the clap entry +# 2. Have a minimal clap entry c++ file which just exposes the entry points from +# that static library +# 3. Make each of the clap, vst3, auv2 etc... link the static library from 1 but +# recomile the entry from 2, so the resulting plugin has the entry point exposed. +# +# We show an example of that here with a slightly modified version of the +# basic c99 distortion plugin, here re-coded using a C++ compiler. + +# So first make the actual plugin as a static library +add_library(${PROJECT_NAME}_base STATIC distortion_clap.cpp) +target_link_libraries(${PROJECT_NAME}_base PUBLIC clap) + + +# Now build and configure the CLAP. +add_library(${PROJECT_NAME}_clap MODULE + distortion_clap_entry.cpp +) +target_link_libraries(${PROJECT_NAME}_clap ${PROJECT_NAME}_base) + +if(APPLE) + set_target_properties(${PROJECT_NAME}_clap PROPERTIES + BUNDLE True + BUNDLE_EXTENSION clap + LIBRARY_OUTPUT_NAME ClapFirstDistortion + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_GUI_IDENTIFIER org.free-audio.clapfirst + MACOSX_BUNDLE_BUNDLE_NAME ClapFirstDistortion + MACOSX_BUNDLE_BUNDLE_VERSION "1" + MACOSX_BUNDLE_SHORT_VERSION_STRING "1" + XCODE_ATTRIBUTE_GENERATE_PKGINFO_FILE "YES" + ) +elseif(UNIX) + set_target_properties(${PROJECT_NAME}_clap PROPERTIES OUTPUT_NAME ClapFirstDistortion SUFFIX ".clap" PREFIX "") + +else() + set_target_properties(${PROJECT_NAME}_clap + PROPERTIES + OUTPUT_NAME ClapFirstDistortion + SUFFIX ".clap" PREFIX "" + LIBRARY_OUTPUT_DIRECTORY CLAP + ) +endif() + +# Building a VST3 is now easy. Make a MODULE library which compiles the entry code +# and links the base library, then use the wraper cmake code to expose it as a VST3 +set(VST3_TARGET ${PROJECT_NAME}_vst3) +add_library(${VST3_TARGET} MODULE) +target_sources(${VST3_TARGET} PRIVATE distortion_clap_entry.cpp) +target_add_vst3_wrapper(TARGET ${VST3_TARGET} + OUTPUT_NAME "ClapFirstDistortion" +) +target_link_libraries(${VST3_TARGET} PRIVATE ${PROJECT_NAME}_base) + + +# And the same for the standalone +set(SA_TARGET ${PROJECT_NAME}_standalone) +add_executable(${SA_TARGET}) +target_sources(${SA_TARGET} PRIVATE distortion_clap_entry.cpp) +target_link_libraries(${SA_TARGET} PRIVATE ${PROJECT_NAME}_base) +target_add_standalone_wrapper(TARGET ${SA_TARGET} + OUTPUT_NAME "ClapFirstDistortion" + STATICALLY_LINKED_CLAP_ENTRY True + PLUGIN_ID "org.free-audio.clap-first-bad-distortion") + +if (APPLE) + # And the same for the AUV2 + set(AUV2_TARGET ${PROJECT_NAME}_auv2) + add_library(${AUV2_TARGET} MODULE) + target_sources(${AUV2_TARGET} PRIVATE distortion_clap_entry.cpp) + target_link_libraries(${AUV2_TARGET} PRIVATE ${PROJECT_NAME}_base) + target_add_auv2_wrapper( + TARGET ${AUV2_TARGET} + OUTPUT_NAME "ClapFirstDistortion" + BUNDLE_IDENTIFIER "org.freeaudio.bad-clap-first" + BUNDLE_VERSION "1" + + MANUFACTURER_NAME "FreeAudio Team" + MANUFACTURER_CODE "FrAU" + SUBTYPE_CODE "clDi" + INSTRUMENT_TYPE "aufx" + + CLAP_TARGET_FOR_CONFIG ${PROJECT_NAME}_clap + ) +endif() + + +# FInally collect those all in a utility target +add_custom_target(${PROJECT_NAME}_all_plugins) +add_dependencies(${PROJECT_NAME}_all_plugins ${PROJECT_NAME}_clap ${PROJECT_NAME}_vst3 ${PROJECT_NAME}_standalone) +if (APPLE) + add_dependencies(${PROJECT_NAME}_all_plugins ${PROJECT_NAME}_auv2) +endif() \ No newline at end of file diff --git a/tests/clap-first-example/distortion_clap.cpp b/tests/clap-first-example/distortion_clap.cpp new file mode 100644 index 00000000..7df9bfb0 --- /dev/null +++ b/tests/clap-first-example/distortion_clap.cpp @@ -0,0 +1,510 @@ +/* + * This implements a distortion CLAP and creates the appropriate factory + * methods as a static library, which are then consumed by the various plugins + * via distortion_clap_entry + * + * The DSP here is bad. Really. This is an example you don't want to use this for music. + */ + +#include +#include +#include + +#include +#include +#include + +static const char *features[] = {CLAP_PLUGIN_FEATURE_AUDIO_EFFECT, CLAP_PLUGIN_FEATURE_STEREO, CLAP_PLUGIN_FEATURE_DISTORTION, nullptr}; + +static const clap_plugin_descriptor_t s_clap1stDist_desc = { + CLAP_VERSION_INIT, + "org.free-audio.clap-first-bad-distortion", + "ClapFirstBadDistortion", + "Surge Synth Team", + "https://surge-synth-team.org/", + "", + "", + "1.0.0", + "A few sloppy distortion algorithms using naive waveshapers", + &features[0] +}; + +enum ClipType +{ + HARD = 0, + SOFT = 1, + FOLD = 2 +}; + +enum ParamIds +{ + pid_DRIVE = 2112, + pid_MIX = 8675309, + pid_MODE = 5150 +}; + +typedef struct +{ + clap_plugin_t plugin; + const clap_host_t *host; + const clap_host_log_t *hostLog; + const clap_host_params_t *hostParams; + + float drive; + float mix; + int32_t mode; +} clap1st_distortion_plug; + +static void clap1stDist_process_event(clap1st_distortion_plug *plug, const clap_event_header_t *hdr); + +///////////////////////////// +// clap_plugin_audio_ports // +///////////////////////////// + +static uint32_t clap1stDist_audio_ports_count(const clap_plugin_t *plugin, bool is_input) +{ + auto generateAWarningOnPurpose = 1.f; + return 1; +} + +static bool clap1stDist_audio_ports_get(const clap_plugin_t *plugin, uint32_t index, bool is_input, + clap_audio_port_info_t *info) +{ + if (index > 0) return false; + info->id = 0; + if (is_input) + snprintf(info->name, sizeof(info->name), "%s", "Stereo In"); + else + snprintf(info->name, sizeof(info->name), "%s", "Distorted Output"); + info->channel_count = 2; + info->flags = CLAP_AUDIO_PORT_IS_MAIN; + info->port_type = CLAP_PORT_STEREO; + info->in_place_pair = CLAP_INVALID_ID; + return true; +} + +static const clap_plugin_audio_ports_t s_clap1stDist_audio_ports = { + clap1stDist_audio_ports_count, + clap1stDist_audio_ports_get, +}; + +////////////////// +// clap_porams // +////////////////// + +uint32_t clap1stDist_param_count(const clap_plugin_t *plugin) +{ + return 3; +} +bool clap1stDist_param_get_info(const clap_plugin_t *plugin, uint32_t param_index, + clap_param_info_t *param_info) +{ + switch (param_index) + { + case 0: // drive + param_info->id = pid_DRIVE; + strncpy(param_info->name, "Drive", CLAP_NAME_SIZE); + param_info->module[0] = 0; + param_info->default_value = 0.; + param_info->min_value = -1; + param_info->max_value = 6; + param_info->flags = CLAP_PARAM_IS_AUTOMATABLE; + param_info->cookie = NULL; + break; + case 1: // mix + param_info->id = pid_MIX; + strncpy(param_info->name, "MIX", CLAP_NAME_SIZE); + param_info->module[0] = 0; + param_info->default_value = 0.5; + param_info->min_value = 0; + param_info->max_value = 1; + param_info->flags = CLAP_PARAM_IS_AUTOMATABLE; + param_info->cookie = NULL; + break; + case 2: // mode + param_info->id = pid_MODE; + strncpy(param_info->name, "Mode", CLAP_NAME_SIZE); + param_info->module[0] = 0; + param_info->default_value = 0.; + param_info->min_value = 0; + param_info->max_value = 2; + param_info->flags = CLAP_PARAM_IS_AUTOMATABLE | CLAP_PARAM_IS_STEPPED; + param_info->cookie = NULL; + break; + default: + return false; + } + return true; +} +bool clap1stDist_param_get_value(const clap_plugin_t *plugin, clap_id param_id, double *value) +{ + auto *plug = (clap1st_distortion_plug *)plugin->plugin_data; + + switch (param_id) + { + case pid_DRIVE: + *value = plug->drive; + return true; + break; + + case pid_MIX: + *value = plug->mix; + return true; + break; + + case pid_MODE: + *value = plug->mode; + return true; + break; + } + + return false; +} +bool clap1stDist_param_value_to_text(const clap_plugin_t *plugin, clap_id param_id, double value, + char *display, uint32_t size) +{ + auto *plug = (clap1st_distortion_plug *)plugin->plugin_data; + + switch (param_id) + { + case pid_DRIVE: + case pid_MIX: + snprintf(display, size, "%f", value); + return true; + break; + case pid_MODE: + { + int v = (int)value; + switch (v) + { + case 0: + snprintf(display, size, "Hard Clip"); + break; + case 1: + snprintf(display, size, "Soft Clip (Tanh)"); + break; + case 2: + snprintf(display, size, "Simple Folder"); + break; + } + + return true; + } + break; + } + return false; +} +bool clap1stDist_text_to_value(const clap_plugin_t *plugin, clap_id param_id, const char *display, + double *value) +{ + // I'm not going to bother to support this + return false; +} +void clap1stDist_flush(const clap_plugin_t *plugin, const clap_input_events_t *in, + const clap_output_events_t *out) +{ + auto *plug = (clap1st_distortion_plug *)plugin->plugin_data; + + int s = in->size(in); + int q; + for (q = 0; q < s; ++q) + { + const clap_event_header_t *hdr = in->get(in, q); + + clap1stDist_process_event(plug, hdr); + } +} + +static const clap_plugin_params_t s_clap1stDist_params = {clap1stDist_param_count, + clap1stDist_param_get_info, + clap1stDist_param_get_value, + clap1stDist_param_value_to_text, + clap1stDist_text_to_value, + clap1stDist_flush}; + +bool clap1stDist_state_save(const clap_plugin_t *plugin, const clap_ostream_t *stream) +{ + auto *plug = (clap1st_distortion_plug *)plugin->plugin_data; + + // We need to save 2 doubles and an int to save our state plus a version. This is, of course, a + // terrible implementation of state. You should do better. + assert(sizeof(float) == 4); + assert(sizeof(int32_t) == 4); + + int buffersize = 16; + char buffer[16]; + + int32_t version = 1; + memcpy(buffer, &version, sizeof(int32_t)); + memcpy(buffer + 4, &(plug->drive), sizeof(float)); + memcpy(buffer + 8, &(plug->mix), sizeof(float)); + memcpy(buffer + 12, &(plug->mode), sizeof(int32_t)); + + int written = 0; + char *curr = buffer; + while (written != buffersize) + { + int thiswrite = stream->write(stream, curr, buffersize - written); + if (thiswrite < 0) return false; + curr += thiswrite; + written += thiswrite; + } + + return true; +} + +bool clap1stDist_state_load(const clap_plugin_t *plugin, const clap_istream_t *stream) +{ + auto *plug = (clap1st_distortion_plug *)plugin->plugin_data; + + int buffersize = 16; + char buffer[16]; + + int read = 0; + char *curr = buffer; + while (read != buffersize) + { + int thisread = stream->read(stream, curr, buffersize - read); + if (thisread < 0) return false; + curr += thisread; + read += thisread; + } + + int32_t version; + memcpy(&version, buffer, sizeof(int32_t)); + memcpy(&plug->drive, buffer + 4, sizeof(float)); + memcpy(&plug->mix, buffer + 8, sizeof(float)); + memcpy(&plug->mode, buffer + 12, sizeof(int32_t)); + + return true; +} +static const clap_plugin_state_t s_clap1stDist_state = {clap1stDist_state_save, + clap1stDist_state_load}; + +///////////////// +// clap_plugin // +///////////////// + +static bool clap1stDist_init(const struct clap_plugin *plugin) +{ + auto *plug = (clap1st_distortion_plug *)plugin->plugin_data; + + // Fetch host's extensions here + plug->hostLog = (const clap_host_log_t *)plug->host->get_extension(plug->host, CLAP_EXT_LOG); + + plug->drive = 0.f; + plug->mix = 0.5f; + plug->mode = HARD; + + if (plug->hostLog && plug->host) + { + plug->hostLog->log(plug->host, CLAP_LOG_INFO, "Created clap1st Distortion compiled with C++"); + } + + return true; +} + +static void clap1stDist_destroy(const struct clap_plugin *plugin) +{ + auto *plug = (clap1st_distortion_plug *)plugin->plugin_data; + free(plug); +} + +static bool clap1stDist_activate(const struct clap_plugin *plugin, double sample_rate, + uint32_t min_frames_count, uint32_t max_frames_count) +{ + return true; +} + +static void clap1stDist_deactivate(const struct clap_plugin *plugin) +{ +} + +static bool clap1stDist_start_processing(const struct clap_plugin *plugin) +{ + return true; +} + +static void clap1stDist_stop_processing(const struct clap_plugin *plugin) +{ +} + +static void clap1stDist_reset(const struct clap_plugin *plugin) +{ +} + +static void clap1stDist_process_event(clap1st_distortion_plug *plug, const clap_event_header_t *hdr) +{ + if (hdr->space_id == CLAP_CORE_EVENT_SPACE_ID) + { + switch (hdr->type) + { + case CLAP_EVENT_PARAM_VALUE: + { + const clap_event_param_value_t *ev = (const clap_event_param_value_t *)hdr; + // TODO: handle parameter change + switch (ev->param_id) + { + case pid_DRIVE: + plug->drive = ev->value; + break; + case pid_MIX: + plug->mix = ev->value; + break; + case pid_MODE: + plug->mode = (int)(ev->value); + break; + } + break; + } + } + } +} + +static clap_process_status clap1stDist_process(const struct clap_plugin *plugin, + const clap_process_t *process) +{ + auto *plug = (clap1st_distortion_plug *)plugin->plugin_data; + + const uint32_t nframes = process->frames_count; + const uint32_t nev = process->in_events->size(process->in_events); + uint32_t ev_index = 0; + uint32_t next_ev_frame = nev > 0 ? 0 : nframes; + + for (uint32_t i = 0; i < nframes;) + { + /* handle every events that happrens at the frame "i" */ + while (ev_index < nev && next_ev_frame == i) + { + const clap_event_header_t *hdr = process->in_events->get(process->in_events, ev_index); + if (hdr->time != i) + { + next_ev_frame = hdr->time; + break; + } + + clap1stDist_process_event(plug, hdr); + ++ev_index; + + if (ev_index == nev) + { + // we reached the end of the event list + next_ev_frame = nframes; + break; + } + } + + /* process every samples until the next event */ + for (; i < next_ev_frame; ++i) + { + // fetch input samples + const float in_l = process->audio_inputs[0].data32[0][i]; + const float in_r = process->audio_inputs[0].data32[1][i]; + + float out_l, out_r; + out_l = 0; + out_r = 0; + + float tl = in_l * (1.0 + plug->drive); + float tr = in_r * (1.0 + plug->drive); + + // Obviously this is inefficient but + switch (plug->mode) + { + case HARD: + { + tl = (tl > 1 ? 1 : tl < -1 ? -1 : tl); + tr = (tr > 1 ? 1 : tr < -1 ? -1 : tr); + } + break; + case SOFT: + { + tl = (tl > 1 ? 1 : tl < -1 ? -1 : tl); + tl = 1.5 * tl - 0.5 * tl * tl * tl; + + tr = (tr > 1 ? 1 : tr < -1 ? -1 : tr); + tr = 1.5 * tr - 0.5 * tr * tr * tr; + } + break; + case FOLD: + { + tl = sin(2.0 * 3.14159265 * tl); + tr = sin(2.0 * 3.14159265 * tr); + } + break; + } + + float mix = plug->mix; + out_l = mix * tl + (1.0 - mix) * in_l; + out_r = mix * tr + (1.0 - mix) * in_r; + + // store output samples + process->audio_outputs[0].data32[0][i] = out_l; + process->audio_outputs[0].data32[1][i] = out_r; + } + } + + return CLAP_PROCESS_CONTINUE; +} + +static const void *clap1stDist_get_extension(const struct clap_plugin *plugin, const char *id) +{ + if (!strcmp(id, CLAP_EXT_AUDIO_PORTS)) return &s_clap1stDist_audio_ports; + if (!strcmp(id, CLAP_EXT_PARAMS)) return &s_clap1stDist_params; + if (!strcmp(id, CLAP_EXT_STATE)) return &s_clap1stDist_state; + return NULL; +} + +static void clap1stDist_on_main_thread(const struct clap_plugin *plugin) +{ +} + +clap_plugin_t *clap1stDist_create(const clap_host_t *host) +{ + auto *p = (clap1st_distortion_plug *)calloc(1, sizeof(clap1st_distortion_plug)); + p->host = host; + p->plugin.desc = &s_clap1stDist_desc; + p->plugin.plugin_data = p; + p->plugin.init = clap1stDist_init; + p->plugin.destroy = clap1stDist_destroy; + p->plugin.activate = clap1stDist_activate; + p->plugin.deactivate = clap1stDist_deactivate; + p->plugin.start_processing = clap1stDist_start_processing; + p->plugin.stop_processing = clap1stDist_stop_processing; + p->plugin.reset = clap1stDist_reset; + p->plugin.process = clap1stDist_process; + p->plugin.get_extension = clap1stDist_get_extension; + p->plugin.on_main_thread = clap1stDist_on_main_thread; + + // Don't call into the host here + + return &p->plugin; +} + +///////////////////////// +// clap_plugin_factory // +///////////////////////// + +uint32_t plugin_factory_get_plugin_count(const struct clap_plugin_factory *factory) +{ + return 1; +} + +const clap_plugin_descriptor_t *plugin_factory_get_plugin_descriptor( + const struct clap_plugin_factory *factory, uint32_t index) +{ + return &s_clap1stDist_desc; +} + +const clap_plugin_t *plugin_factory_create_plugin(const struct clap_plugin_factory *factory, + const clap_host_t *host, const char *plugin_id) +{ + if (!clap_version_is_compatible(host->clap_version)) + { + return nullptr; + } + + if (!strcmp(plugin_id, s_clap1stDist_desc.id)) return clap1stDist_create(host); + + return nullptr; +} diff --git a/tests/clap-first-example/distortion_clap_entry.cpp b/tests/clap-first-example/distortion_clap_entry.cpp new file mode 100644 index 00000000..70c755a5 --- /dev/null +++ b/tests/clap-first-example/distortion_clap_entry.cpp @@ -0,0 +1,58 @@ +/* + * distortion_clap_entry + * + * This uses extern versions of the factory methods from a static library to create + * a working exported C linkage clap entry point + */ + +#include +#include + +extern uint32_t plugin_factory_get_plugin_count(const struct clap_plugin_factory *factory); +extern const clap_plugin_descriptor_t *plugin_factory_get_plugin_descriptor( + const struct clap_plugin_factory *factory, uint32_t index); + +extern const clap_plugin_t *plugin_factory_create_plugin(const struct clap_plugin_factory *factory, + const clap_host_t *host, const char *plugin_id); + +static const clap_plugin_factory_t s_plugin_factory = { + plugin_factory_get_plugin_count, + plugin_factory_get_plugin_descriptor, + plugin_factory_create_plugin, +}; + +//////////////// +// clap_entry // +//////////////// + +static bool entry_init(const char *plugin_path) +{ + // called only once, and very first + return true; +} + +static void entry_deinit(void) +{ + // called before unloading the DSO +} + +static const void *entry_get_factory(const char *factory_id) +{ + if (!strcmp(factory_id, CLAP_PLUGIN_FACTORY_ID)) return &s_plugin_factory; + return nullptr; +} + +extern "C" +{ +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wattributes" // other peoples errors are outside my scope +#endif + + const CLAP_EXPORT struct clap_plugin_entry clap_entry = {CLAP_VERSION, entry_init, entry_deinit, + entry_get_factory}; + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +} \ No newline at end of file