clap

CLAP Audio Plugin API
Log | Files | Refs | README | LICENSE

commit ddbae27c12153c864a31662ea41a5757e5603a1c
parent 650ad6a6b0215b3f964c49a004667ad876689dfd
Author: Alexandre Bique <bique.alexandre@gmail.com>
Date:   Fri, 30 Dec 2022 18:18:52 +0100

Merge pull request #253 from free-audio/next

1.1.5
Diffstat:
M.github/workflows/cmake.yml | 2+-
MCMakeLists.txt | 7++++---
MChangeLog.md | 15+++++++++++++++
Minclude/clap/clap.h | 2+-
Minclude/clap/ext/audio-ports.h | 2+-
Minclude/clap/ext/draft/audio-ports-activation.h | 8++++----
Minclude/clap/ext/params.h | 71++++++++++++++++++++++++++++++++++++-----------------------------------
Minclude/clap/host.h | 4+++-
Minclude/clap/plugin.h | 11+++++++++++
Minclude/clap/version.h | 15+++++++++++----
Msrc/main.cc | 29+++++++++++++++++++++++++++--
Msrc/plugin-template.c | 50+++++++++++++++++++++++++++++++++++++++++---------
12 files changed, 155 insertions(+), 61 deletions(-)

diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml @@ -10,7 +10,7 @@ # described by the vcpkg.json manifest file. It will be a no-op if those are restored from cache. # - Finally builds the sources with Ninja. name: build -on: [push, workflow_dispatch] +on: [push, pull_request, workflow_dispatch] jobs: VCPKG: diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -3,11 +3,11 @@ enable_testing() # Extract the version from header file file(READ "include/clap/version.h" clap_version_header) -string(REGEX MATCH "CLAP_VERSION_MAJOR \\(\\(uint32_t\\)([0-9]+)\\)" _ ${clap_version_header}) +string(REGEX MATCH "CLAP_VERSION_MAJOR ([0-9]+)" _ ${clap_version_header}) set(CLAP_VERSION_MAJOR ${CMAKE_MATCH_1}) -string(REGEX MATCH "CLAP_VERSION_MINOR \\(\\(uint32_t\\)([0-9]+)\\)" _ ${clap_version_header}) +string(REGEX MATCH "CLAP_VERSION_MINOR ([0-9]+)" _ ${clap_version_header}) set(CLAP_VERSION_MINOR ${CMAKE_MATCH_1}) -string(REGEX MATCH "CLAP_VERSION_REVISION \\(\\(uint32_t\\)([0-9]+)\\)" _ ${clap_version_header}) +string(REGEX MATCH "CLAP_VERSION_REVISION ([0-9]+)" _ ${clap_version_header}) set(CLAP_VERSION_REVISION ${CMAKE_MATCH_1}) message(STATUS "CLAP version: ${CLAP_VERSION_MAJOR}.${CLAP_VERSION_MINOR}.${CLAP_VERSION_REVISION}") @@ -79,6 +79,7 @@ if (${CLAP_BUILD_TESTS}) add_library(clap-plugin-template MODULE EXCLUDE_FROM_ALL src/plugin-template.c) target_link_libraries(clap-plugin-template PRIVATE clap) set_target_properties(clap-plugin-template PROPERTIES C_STANDARD 11) + add_dependencies(clap-tests clap-plugin-template) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(clap-plugin-template PRIVATE -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/linux-my_plug.version) diff --git a/ChangeLog.md b/ChangeLog.md @@ -1,3 +1,18 @@ +# Changes in 1.1.5 + +* [plugin.h](include/clap/plugin.h): clarify plugin state after init() +* [plugin.h](include/clap/plugin.h): clarify when it is allowed to call get_extension() +* [plugin.h](include/clap/plugin.h): advice for plugin id and version strings +* [host.h](include/clap/host.h): clarify when it is allowed to call get_extension() +* [CMakeLists.txt](CMakeLists.txt): the target `clap-test` now includes `clap-plugin-template` +* Remove UTF-8 BOM from a few files +* [plugin-template.c](src/plugin-template.c): add state impl and some comments +* [audio-ports-activation.h](include/clap/ext/draft/audio-ports-activation.h): improved documentation +* [version.h](include/clap/version.h): + * Add a CLAP_VERSION_GE(maj,min,rev), _EQ and _LT macro. + * Remove the uint32_t cast from CLAP_VERSION_MAJOR, _MINOR, and _REVISION macro, and introduce it to the CLAP_VERSION_INIT macro. + * If you rely on these macros being a uint32_t or parse this header using external software, this may be a breaking change. + # Changes in 1.1.4 * CMake: update some targets to link against `clap` instead of `clap-core` diff --git a/include/clap/clap.h b/include/clap/clap.h @@ -1,4 +1,4 @@ -/* +/* * CLAP - CLever Audio Plugin * ~~~~~~~~~~~~~~~~~~~~~~~~~~ * diff --git a/include/clap/ext/audio-ports.h b/include/clap/ext/audio-ports.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "../plugin.h" #include "../string-sizes.h" diff --git a/include/clap/ext/draft/audio-ports-activation.h b/include/clap/ext/draft/audio-ports-activation.h @@ -7,9 +7,9 @@ /// This extension provides a way for the host to activate and de-activate audio ports. /// Deactivating a port provides the following benefits: /// - the plugin knows ahead of time that a given input is not present and can choose -/// an optimized computation path +/// an optimized computation path, /// - the plugin knows that an output is not consumed by the host, and doesn't need to -/// compute it +/// compute it. /// /// Audio ports can only be activated or deactivated when the plugin is deactivated, unless /// can_activate_while_processing() returns true. @@ -39,8 +39,8 @@ typedef struct clap_plugin_audio_ports_activation { // Activate the given port. // - // It is only possible to activate on the audio-thread if can_activate_while_processing() returns - // true. + // It is only possible to activate and de-activate on the audio-thread if + // can_activate_while_processing() returns true. // // [active ? audio-thread : main-thread] void(CLAP_ABI *set_active)(const clap_plugin_t *plugin, diff --git a/include/clap/ext/params.h b/include/clap/ext/params.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "../plugin.h" #include "../string-sizes.h" @@ -97,7 +97,7 @@ /// ..... . . /// before: . . and after: . . /// -/// Advices for the host: +/// Advice for the host: /// - store plain values in the document (automation) /// - store modulation amount in plain value delta, not in percentage /// - when you apply a CC mapping, remember the min/max plain values so you can adjust @@ -185,14 +185,13 @@ typedef uint32_t clap_param_info_flags; /* This describes a parameter */ typedef struct clap_param_info { - // stable parameter identifier, it must never change. + // Stable parameter identifier, it must never change. clap_id id; clap_param_info_flags flags; // This value is optional and set by the plugin. - // Its purpose is to provide a fast access to the - // plugin parameter object by caching its pointer. + // Its purpose is to provide a fast access to the plugin parameter object by caching its pointer. // For instance: // // in clap_plugin_params.get_info(): @@ -205,32 +204,31 @@ typedef struct clap_param_info { // if (!p) [[unlikely]] // p = findParameter(event->param_id); // - // where findParameter() is a function the plugin implements - // to map parameter ids to internal objects. + // where findParameter() is a function the plugin implements to map parameter ids to internal + // objects. // // Important: - // - The cookie is invalidated by a call to - // clap_host_params->rescan(CLAP_PARAM_RESCAN_ALL) or when the plugin is - // destroyed. - // - The host will either provide the cookie as issued or nullptr - // in events addressing parameters. - // - The plugin must gracefully handle the case of a cookie - // which is nullptr. - // - Many plugins will process the parameter events more quickly if the host - // can provide the cookie in a faster time than a hashmap lookup per param - // per event. + // - The cookie is invalidated by a call to clap_host_params->rescan(CLAP_PARAM_RESCAN_ALL) or + // when the plugin is destroyed. + // - The host will either provide the cookie as issued or nullptr in events addressing + // parameters. + // - The plugin must gracefully handle the case of a cookie which is nullptr. + // - Many plugins will process the parameter events more quickly if the host can provide the + // cookie in a faster time than a hashmap lookup per param per event. void *cookie; - // the display name + // The display name. eg: "Volume". This does not need to be unique. Do not include the module + // text in this. The host should concatenate/format the module + name in the case where showing + // the name alone would be too vague. char name[CLAP_NAME_SIZE]; - // the module path containing the param, eg:"oscillators/wt1" - // '/' will be used as a separator to show a tree like structure. + // The module path containing the param, eg: "Oscillators/Wavetable 1". + // '/' will be used as a separator to show a tree-like structure. char module[CLAP_PATH_SIZE]; - double min_value; // minimum plain value - double max_value; // maximum plain value - double default_value; // default plain value + double min_value; // Minimum plain value + double max_value; // Maximum plain value + double default_value; // Default plain value } clap_param_info_t; typedef struct clap_plugin_params { @@ -238,29 +236,32 @@ typedef struct clap_plugin_params { // [main-thread] uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin); - // Copies the parameter's info to param_info and returns true on success. + // Copies the parameter's info to param_info. Returns true on success. // [main-thread] bool(CLAP_ABI *get_info)(const clap_plugin_t *plugin, uint32_t param_index, clap_param_info_t *param_info); - // Gets the parameter plain value. + // Writes the parameter's current value to out_value. Returns true on success. // [main-thread] - bool(CLAP_ABI *get_value)(const clap_plugin_t *plugin, clap_id param_id, double *value); + bool(CLAP_ABI *get_value)(const clap_plugin_t *plugin, clap_id param_id, double *out_value); - // Formats the display text for the given parameter value. - // The host should always format the parameter value to text using this function - // before displaying it to the user. - // [main-thread] - bool(CLAP_ABI *value_to_text)( - const clap_plugin_t *plugin, clap_id param_id, double value, char *display, uint32_t size); + // Fills out_buffer with a null-terminated UTF-8 string that represents the parameter at the + // given 'value' argument. eg: "2.3 kHz". Returns true on success. The host should always use + // this to format parameter values before displaying it to the user. [main-thread] + bool(CLAP_ABI *value_to_text)(const clap_plugin_t *plugin, + clap_id param_id, + double value, + char *out_buffer, + uint32_t out_buffer_capacity); - // Converts the display text to a parameter value. + // Converts the null-terminated UTF-8 param_value_text into a double and writes it to out_value. + // Returns true on success. The host can use this to convert user input into a parameter value. // [main-thread] bool(CLAP_ABI *text_to_value)(const clap_plugin_t *plugin, clap_id param_id, - const char *display, - double *value); + const char *param_value_text, + double *out_value); // Flushes a set of parameter changes. // This method must not be called concurrently to clap_plugin->process(). diff --git a/include/clap/host.h b/include/clap/host.h @@ -15,9 +15,11 @@ typedef struct clap_host { const char *name; // eg: "Bitwig Studio" const char *vendor; // eg: "Bitwig GmbH" const char *url; // eg: "https://bitwig.com" - const char *version; // eg: "4.3" + const char *version; // eg: "4.3", see plugin.h for advice on how to format the version // Query an extension. + // It is forbidden to call it before plugin->init(). + // You can call it within plugin->init() call, and after. // [thread-safe] const void *(CLAP_ABI *get_extension)(const struct clap_host *host, const char *extension_id); diff --git a/include/clap/plugin.h b/include/clap/plugin.h @@ -14,6 +14,14 @@ typedef struct clap_plugin_descriptor { // Mandatory fields must be set and must not be blank. // Otherwise the fields can be null or blank, though it is safer to make them blank. + // + // Some indications regarding id and version + // - id is an arbritrary string which should be unique to your plugin, + // we encourage you to use a reverse URI eg: "com.u-he.diva" + // - version is an arbitrary string which describes a plugin, + // it is useful for the host to understand and be able to compare two different + // version strings, so here is a regex like expression which is likely to be + // understood by most hosts: MAJOR(.MINOR(.REVISION)?)?( (Alpha|Beta) XREV)? const char *id; // eg: "com.u-he.diva", mandatory const char *name; // eg: "Diva", mandatory const char *vendor; // eg: "u-he" @@ -37,6 +45,7 @@ typedef struct clap_plugin { // Must be called after creating the plugin. // If init returns false, the host must destroy the plugin instance. + // If init returns true, then the plugin is initialized and in the deactivated state. // [main-thread] bool(CLAP_ABI *init)(const struct clap_plugin *plugin); @@ -84,6 +93,8 @@ typedef struct clap_plugin { // Query an extension. // The returned pointer is owned by the plugin. + // It is forbidden to call it before plugin->init(). + // You can call it within plugin->init() call, and after. // [thread-safe] const void *(CLAP_ABI *get_extension)(const struct clap_plugin *plugin, const char *id); diff --git a/include/clap/version.h b/include/clap/version.h @@ -20,11 +20,18 @@ typedef struct clap_version { } #endif -#define CLAP_VERSION_MAJOR ((uint32_t)1) -#define CLAP_VERSION_MINOR ((uint32_t)1) -#define CLAP_VERSION_REVISION ((uint32_t)4) +#define CLAP_VERSION_MAJOR 1 +#define CLAP_VERSION_MINOR 1 +#define CLAP_VERSION_REVISION 5 + #define CLAP_VERSION_INIT \ - { CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION } + { (uint32_t)CLAP_VERSION_MAJOR, (uint32_t)CLAP_VERSION_MINOR, (uint32_t)CLAP_VERSION_REVISION } + +#define CLAP_VERSION_LT(maj,min,rev) (((maj) < CLAP_VERSION_MAJOR) || \ + ((maj) == CLAP_VERSION_MAJOR && (min) < CLAP_VERSION_MINOR ) || \ + ((maj) == CLAP_VERSION_MAJOR && (min) == CLAP_VERSION_MINOR && (rev) < CLAP_VERSION_REVISION)) +#define CLAP_VERSION_EQ(maj,min,rev) (((maj) == CLAP_VERSION_MAJOR) && ((min) == CLAP_VERSION_MINOR) && ((rev) == CLAP_VERSION_REVISION)) +#define CLAP_VERSION_GE(maj,min,rev) (!CLAP_VERSION_LT(maj,min,rev)) static const CLAP_CONSTEXPR clap_version_t CLAP_VERSION = CLAP_VERSION_INIT; diff --git a/src/main.cc b/src/main.cc @@ -2,8 +2,34 @@ // The purpose of this file is to check that all headers compile +#if CLAP_VERSION_LT(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION) +#error CLAP_VERSION_LT is inconsistent +#endif + +#if !CLAP_VERSION_EQ(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION) +#error CLAP_VERSION_EQ is inconsistent +#endif + +#if CLAP_VERSION_EQ(CLAP_VERSION_MAJOR + 1, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION) +#error CLAP_VERSION_EQ is inconsistent (MAJOR) +#endif +#if CLAP_VERSION_EQ(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR + 1, CLAP_VERSION_REVISION) +#error CLAP_VERSION_EQ is inconsistent (MINOR) +#endif +#if CLAP_VERSION_EQ(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION + 1) +#error CLAP_VERSION_EQ is inconsistent (REVISION) +#endif + +#if !CLAP_VERSION_GE(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION) +#error CLAP_VERSION_GE is inconsistent +#endif + +#if CLAP_VERSION_LT(1,1,5) +#error CLAP_VERSION_GE was inroduced in 1.1.5 so we should be later than that version. +#endif + static const CLAP_CONSTEXPR clap_version m = CLAP_VERSION; int main(int, char **) { return !clap_version_is_compatible(m); -} -\ No newline at end of file +} diff --git a/src/plugin-template.c b/src/plugin-template.c @@ -25,9 +25,10 @@ static const clap_plugin_descriptor_t s_my_plug_desc = { typedef struct { clap_plugin_t plugin; const clap_host_t *host; - const clap_host_latency_t *hostLatency; - const clap_host_log_t *hostLog; - const clap_host_thread_check_t *hostThreadCheck; + const clap_host_latency_t *host_latency; + const clap_host_log_t *host_log; + const clap_host_thread_check_t *host_thread_check; + const clap_host_state_t *host_state; uint32_t latency; } my_plug_t; @@ -36,7 +37,10 @@ typedef struct { // clap_plugin_audio_ports // ///////////////////////////// -static uint32_t my_plug_audio_ports_count(const clap_plugin_t *plugin, bool is_input) { return 1; } +static uint32_t my_plug_audio_ports_count(const clap_plugin_t *plugin, bool is_input) { + // We just declare 1 audio input and 1 audio output + return 1; +} static bool my_plug_audio_ports_get(const clap_plugin_t *plugin, uint32_t index, @@ -62,7 +66,10 @@ static const clap_plugin_audio_ports_t s_my_plug_audio_ports = { // clap_plugin_note_ports // //////////////////////////// -static uint32_t my_plug_note_ports_count(const clap_plugin_t *plugin, bool is_input) { return 1; } +static uint32_t my_plug_note_ports_count(const clap_plugin_t *plugin, bool is_input) { + // We just declare 1 note input + return 1; +} static bool my_plug_note_ports_get(const clap_plugin_t *plugin, uint32_t index, @@ -96,6 +103,27 @@ static const clap_plugin_latency_t s_my_plug_latency = { .get = my_plug_latency_get, }; +//////////////// +// clap_state // +//////////////// + +bool my_plug_state_save(const clap_plugin_t *plugin, const clap_ostream_t *stream) { + my_plug_t *plug = plugin->plugin_data; + // TODO: write the state into stream + return true; +} + +bool my_plug_state_load(const clap_plugin_t *plugin, const clap_istream_t *stream) { + my_plug_t *plug = plugin->plugin_data; + // TODO: read the state from stream + return true; +} + +static const clap_plugin_state_t s_my_plug_state = { + .save = my_plug_state_save, + .load = my_plug_state_load, +}; + ///////////////// // clap_plugin // ///////////////// @@ -104,9 +132,11 @@ static bool my_plug_init(const struct clap_plugin *plugin) { my_plug_t *plug = plugin->plugin_data; // Fetch host's extensions here - plug->hostLog = plug->host->get_extension(plug->host, CLAP_EXT_LOG); - plug->hostThreadCheck = plug->host->get_extension(plug->host, CLAP_EXT_THREAD_CHECK); - plug->hostLatency = plug->host->get_extension(plug->host, CLAP_EXT_LATENCY); + // Make sure to check that the interface functions are not null pointers + plug->host_log = (const clap_host_log_t *)plug->host->get_extension(plug->host, CLAP_EXT_LOG); + plug->host_thread_check = (const clap_host_thread_check_t *)plug->host->get_extension(plug->host, CLAP_EXT_THREAD_CHECK); + plug->host_latency = (const clap_host_latency_t *)plug->host->get_extension(plug->host, CLAP_EXT_LATENCY); + plug->host_state = (const clap_host_state_t *)plug->host->get_extension(plug->host, CLAP_EXT_STATE); return true; } @@ -249,8 +279,9 @@ static const void *my_plug_get_extension(const struct clap_plugin *plugin, const return &s_my_plug_audio_ports; if (!strcmp(id, CLAP_EXT_NOTE_PORTS)) return &s_my_plug_note_ports; + if (!strcmp(id, CLAP_EXT_STATE)) + return &s_my_plug_state; // TODO: add support to CLAP_EXT_PARAMS - // TODO: add support to CLAP_EXT_STATE return NULL; } @@ -340,6 +371,7 @@ static const void *entry_get_factory(const char *factory_id) { return NULL; } +// This symbol will be resolved by the host CLAP_EXPORT const clap_plugin_entry_t clap_entry = { .clap_version = CLAP_VERSION_INIT, .init = entry_init,