clap

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

commit e3f2bf9c9d02cce33e6c9b1a3b1dd3e28105de9c
parent 14251f8f99445e956344c9dbe420582b5bee3b08
Author: Russell McClellan <russell.mcclellan@gmail.com>
Date:   Fri, 10 Mar 2023 10:07:26 -0500

Merge remote-tracking branch 'origin/main' into gui-threading-clarification

Diffstat:
M.github/workflows/cmake.yml | 2+-
MCMakeLists.txt | 13+++++++++----
MChangeLog.md | 41+++++++++++++++++++++++++++++++++++++++++
MREADME.md | 2+-
Minclude/clap/clap.h | 10++++++----
Minclude/clap/entry.h | 2+-
Minclude/clap/events.h | 40++++++++++++++++++++--------------------
Minclude/clap/ext/audio-ports-config.h | 25+++++++++++++++++++++++++
Minclude/clap/ext/audio-ports.h | 2+-
Minclude/clap/ext/draft/ambisonic.h | 6+++++-
Minclude/clap/ext/draft/audio-ports-activation.h | 13+++++++------
Minclude/clap/ext/draft/context-menu.h | 12++++++++++++
Minclude/clap/ext/draft/preset-load.h | 31++++++++++++++++++++++++++++---
Minclude/clap/ext/draft/remote-controls.h | 6+++++-
Minclude/clap/ext/draft/surround.h | 8++++++--
Minclude/clap/ext/params.h | 77+++++++++++++++++++++++++++++++++++++++--------------------------------------
Ainclude/clap/factory/draft/plugin-invalidation.h | 47+++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/clap/factory/draft/preset-discovery.h | 316+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/clap/factory/plugin-factory.h | 42++++++++++++++++++++++++++++++++++++++++++
Minclude/clap/host.h | 5++++-
Dinclude/clap/plugin-factory.h | 40----------------------------------------
Minclude/clap/plugin-features.h | 5+++--
Dinclude/clap/plugin-invalidation.h | 45---------------------------------------------
Minclude/clap/plugin.h | 11+++++++++++
Minclude/clap/version.h | 15+++++++++++----
Msrc/main.cc | 49+++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/plugin-template.c | 50+++++++++++++++++++++++++++++++++++++++++---------
27 files changed, 729 insertions(+), 186 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}") @@ -72,13 +72,18 @@ if (${CLAP_BUILD_TESTS}) clap_compile_cpp(c11 c 11 11) clap_compile_cpp(cpp11 cc 11 11) clap_compile_cpp(cpp14 cc 11 14) - clap_compile_cpp(c17 c 17 17) + if(${CMAKE_VERSION} VERSION_LESS "3.21") + message(STATUS "Skipping C17 tests due to older CMAKE_VERSION ${CMAKE_VERSION}") + else() + clap_compile_cpp(c17 c 17 17) + endif() clap_compile_cpp(cpp17 cc 17 17) clap_compile_cpp(cpp20 cc 17 20) 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,44 @@ +# Changes in 1.1.7 + +* Add a [factory](include/clap/factory) folder for better organization and move our factories there +* [params.h](include/clap/ext/params.h): fix typos +* CMake: disable C17 targets for CMake < 3.21 +* [plugin-features.h](include/clap/plugin-features.h): adds `note-detector` category for plugins which converts audio to notes + +## Draft extensions + +* [context-menu.h](include/clap/ext/draft/context-menu.h): add "title" menu entry +* [preset-load.h](include/clap/ext/draft/preset-load.h): load from URI instead of path, making the extension more powerful +* [remote-controls.h](include/clap/ext/draft/remote-controls.h): distinguish between device pages and preset pages +* [audio-ports-activation.h](include/clap/ext/draft/audio-ports-activation.h): `set_active()` now returns bool instead of void, this helps catching problems earlier especially with invalid arguments +* [audio-ports-config.h](include/clap/ext/audio-ports-config.h): add new draft extension: `clap_plugin_audio_ports_config_info` which lets the host query detailed port information in a given configuration. +* [surround.h](include/clap/ext/draft/surround.h): add `config_id` parameter when fetching port info +* [ambisonic.h](include/clap/ext/draft/ambisonic.h): add `config_id` parameter when fetching port info + +## Draft factories + +* [preset-discovery.h](include/clap/factory/draft/preset-discovery.h): new factory which allows the host to index the plugin presets which are stored on disk. + +# Changes in 1.1.6 + +* [version.h](include/clap/version.h) `CLAP_VERSION_LT` was backwards (comparing current with arg + vs arg with current). Correct and enhance tests. + +# 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/README.md b/README.md @@ -37,7 +37,7 @@ The entry point is declared in [entry.h](include/clap/entry.h). ## Extensions -Most features comes from extensions, which are in fact C interfaces. +Most features come from extensions, which are in fact C interfaces. ```C // host extension const clap_host_log *log = host->extension(host, CLAP_EXT_LOG); diff --git a/include/clap/clap.h b/include/clap/clap.h @@ -1,4 +1,4 @@ -/* +/* * CLAP - CLever Audio Plugin * ~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -26,11 +26,13 @@ #pragma once #include "entry.h" -#include "plugin-factory.h" -#include "plugin-invalidation.h" -#include "plugin-features.h" + +#include "factory/plugin-factory.h" +#include "factory/draft/plugin-invalidation.h" +#include "factory/draft/preset-discovery.h" #include "plugin.h" +#include "plugin-features.h" #include "host.h" #include "ext/audio-ports-config.h" diff --git a/include/clap/entry.h b/include/clap/entry.h @@ -53,7 +53,7 @@ typedef struct clap_plugin_entry { // No more calls into the DSO must be made after calling deinit(). void(CLAP_ABI *deinit)(void); - // Get the pointer to a factory. See plugin-factory.h for an example. + // Get the pointer to a factory. See factory/plugin-factory.h for an example. // // Returns null if the factory is not provided. // The returned pointer must *not* be freed by the caller. diff --git a/include/clap/events.h b/include/clap/events.h @@ -83,14 +83,14 @@ enum { // Plugin->Host NoteEnd(port:0, channel:0, key:16, time:ignored) // // These four events use clap_event_note. - CLAP_EVENT_NOTE_ON, - CLAP_EVENT_NOTE_OFF, - CLAP_EVENT_NOTE_CHOKE, - CLAP_EVENT_NOTE_END, + CLAP_EVENT_NOTE_ON = 0, + CLAP_EVENT_NOTE_OFF = 1, + CLAP_EVENT_NOTE_CHOKE = 2, + CLAP_EVENT_NOTE_END = 3, // Represents a note expression. // Uses clap_event_note_expression. - CLAP_EVENT_NOTE_EXPRESSION, + CLAP_EVENT_NOTE_EXPRESSION = 4, // PARAM_VALUE sets the parameter's value; uses clap_event_param_value. // PARAM_MOD sets the parameter's modulation amount; uses clap_event_param_mod. @@ -100,20 +100,20 @@ enum { // In case of a concurrent global value/modulation versus a polyphonic one, // the voice should only use the polyphonic one and the polyphonic modulation // amount will already include the monophonic signal. - CLAP_EVENT_PARAM_VALUE, - CLAP_EVENT_PARAM_MOD, + CLAP_EVENT_PARAM_VALUE = 5, + CLAP_EVENT_PARAM_MOD = 6, // Indicates that the user started or finished adjusting a knob. // This is not mandatory to wrap parameter changes with gesture events, but this improves // the user experience a lot when recording automation or overriding automation playback. // Uses clap_event_param_gesture. - CLAP_EVENT_PARAM_GESTURE_BEGIN, - CLAP_EVENT_PARAM_GESTURE_END, + CLAP_EVENT_PARAM_GESTURE_BEGIN = 7, + CLAP_EVENT_PARAM_GESTURE_END = 8, - CLAP_EVENT_TRANSPORT, // update the transport info; clap_event_transport - CLAP_EVENT_MIDI, // raw midi event; clap_event_midi - CLAP_EVENT_MIDI_SYSEX, // raw midi sysex event; clap_event_midi_sysex - CLAP_EVENT_MIDI2, // raw midi 2 event; clap_event_midi2 + CLAP_EVENT_TRANSPORT = 9, // update the transport info; clap_event_transport + CLAP_EVENT_MIDI = 10, // raw midi event; clap_event_midi + CLAP_EVENT_MIDI_SYSEX = 11, // raw midi sysex event; clap_event_midi_sysex + CLAP_EVENT_MIDI2 = 12, // raw midi 2 event; clap_event_midi2 }; // Note on, off, end and choke events. @@ -132,19 +132,19 @@ typedef struct clap_event_note { enum { // with 0 < x <= 4, plain = 20 * log(x) - CLAP_NOTE_EXPRESSION_VOLUME, + CLAP_NOTE_EXPRESSION_VOLUME = 0, // pan, 0 left, 0.5 center, 1 right - CLAP_NOTE_EXPRESSION_PAN, + CLAP_NOTE_EXPRESSION_PAN = 1, // relative tuning in semitone, from -120 to +120 - CLAP_NOTE_EXPRESSION_TUNING, + CLAP_NOTE_EXPRESSION_TUNING = 2, // 0..1 - CLAP_NOTE_EXPRESSION_VIBRATO, - CLAP_NOTE_EXPRESSION_EXPRESSION, - CLAP_NOTE_EXPRESSION_BRIGHTNESS, - CLAP_NOTE_EXPRESSION_PRESSURE, + CLAP_NOTE_EXPRESSION_VIBRATO = 3, + CLAP_NOTE_EXPRESSION_EXPRESSION = 4, + CLAP_NOTE_EXPRESSION_BRIGHTNESS = 5, + CLAP_NOTE_EXPRESSION_PRESSURE = 6, }; typedef int32_t clap_note_expression; diff --git a/include/clap/ext/audio-ports-config.h b/include/clap/ext/audio-ports-config.h @@ -2,6 +2,7 @@ #include "../string-sizes.h" #include "../plugin.h" +#include "audio-ports.h" /// @page Audio Ports Config /// @@ -20,8 +21,13 @@ /// /// Plugins with very complex configuration possibilities should let the user configure the ports /// from the plugin GUI, and call @ref clap_host_audio_ports.rescan(CLAP_AUDIO_PORTS_RESCAN_ALL). +/// +/// To inquire the exact bus layout, the plugin implements the clap_plugin_audio_ports_config_info_t +/// extension where all busses can be retrieved in the same way as in the audio-port extension. static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_CONFIG[] = "clap.audio-ports-config"; +static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_CONFIG_INFO[] = + "clap.audio-ports-config-info/draft-0"; #ifdef __cplusplus extern "C" { @@ -65,6 +71,25 @@ typedef struct clap_plugin_audio_ports_config { bool(CLAP_ABI *select)(const clap_plugin_t *plugin, clap_id config_id); } clap_plugin_audio_ports_config_t; +// Extended config info +typedef struct clap_plugin_audio_ports_config_info { + + // Gets the id of the currently selected config, or CLAP_INVALID_ID if the current port + // layout isn't part of the config list. + // + // [main-thread] + clap_id(CLAP_ABI *current_config)(const clap_plugin_t *plugin); + + // Get info about about an audio port, for a given config_id. + // This is analogous to clap_plugin_audio_ports.get(). + // [main-thread] + bool(CLAP_ABI *get)(const clap_plugin_t *plugin, + clap_id config_id, + uint32_t port_index, + bool is_input, + clap_audio_port_info_t *info); +} clap_plugin_audio_ports_config_info_t; + typedef struct clap_host_audio_ports_config { // Rescan the full list of configs. // [main-thread] 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/ambisonic.h b/include/clap/ext/draft/ambisonic.h @@ -4,7 +4,7 @@ // This extension can be used to specify the channel mapping used by the plugin. -static CLAP_CONSTEXPR const char CLAP_EXT_AMBISONIC[] = "clap.ambisonic.draft/0"; +static CLAP_CONSTEXPR const char CLAP_EXT_AMBISONIC[] = "clap.ambisonic.draft/1"; static CLAP_CONSTEXPR const char CLAP_PORT_AMBISONIC[] = "ambisonic"; @@ -35,8 +35,12 @@ typedef struct clap_ambisonic_info { typedef struct clap_plugin_ambisonic { // Returns true on success + // + // config_id: the configuration id, see clap_plugin_audio_ports_config. + // If config_id is CLAP_INVALID_ID, then this function queries the current port info. // [main-thread] bool(CLAP_ABI *get_info)(const clap_plugin_t *plugin, + clap_id config_id, bool is_input, uint32_t port_index, clap_ambisonic_info_t *info); 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. @@ -26,7 +26,7 @@ /// clap_host_audio_ports.rescan(CLAP_AUDIO_PORTS_RESCAN_LIST). static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_ACTIVATION[] = - "clap.audio-ports-activation/draft-0"; + "clap.audio-ports-activation/draft-1"; #ifdef __cplusplus extern "C" { @@ -39,11 +39,12 @@ 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. // + // returns false if failed, or invalid parameters // [active ? audio-thread : main-thread] - void(CLAP_ABI *set_active)(const clap_plugin_t *plugin, + bool(CLAP_ABI *set_active)(const clap_plugin_t *plugin, bool is_input, uint32_t port_index, bool is_active); diff --git a/include/clap/ext/draft/context-menu.h b/include/clap/ext/draft/context-menu.h @@ -44,6 +44,10 @@ enum { // Ends the current sub menu. // data: NULL CLAP_CONTEXT_MENU_ITEM_END_SUBMENU, + + // Adds a title entry + // data: const clap_context_menu_item_title_t * + CLAP_CONTEXT_MENU_ITEM_TITLE, }; typedef uint32_t clap_context_menu_item_kind_t; @@ -68,6 +72,14 @@ typedef struct clap_context_menu_check_entry { clap_id action_id; } clap_context_menu_check_entry_t; +typedef struct clap_context_menu_item_title { + // text to be displayed + const char *title; + + // if false, then the menu entry is greyed out + bool is_enabled; +} clap_context_menu_item_title_t; + typedef struct clap_context_menu_submenu { // text to be displayed const char *label; diff --git a/include/clap/ext/draft/preset-load.h b/include/clap/ext/draft/preset-load.h @@ -2,18 +2,43 @@ #include "../../plugin.h" -static const char CLAP_EXT_PRESET_LOAD[] = "clap.preset-load.draft/0"; +static const char CLAP_EXT_PRESET_LOAD[] = "clap.preset-load.draft/1"; #ifdef __cplusplus extern "C" { #endif typedef struct clap_plugin_preset_load { - // Loads a preset in the plugin native preset file format from a path. + // Loads a preset in the plugin native preset file format from a URI. eg: + // - "file:///home/abique/.u-he/Diva/Presets/Diva/HS Bass Nine.h2p", load_key: null + // - "plugin://<plugin-id>", load_key: <XXX> + // + // The preset discovery provider defines the uri and load_key to be passed to this function. + // // [main-thread] - bool(CLAP_ABI *from_file)(const clap_plugin_t *plugin, const char *path); + bool(CLAP_ABI *from_uri)(const clap_plugin_t *plugin, const char *uri, const char *load_key); } clap_plugin_preset_load_t; +typedef struct clap_host_preset_load { + // Called if clap_plugin_preset_load.load() failed. + // os_error: the operating system error, if applicable. If not applicable set it to a non-error + // value, eg: 0 on unix and Windows. + // + // [main-thread] + void(CLAP_ABI *on_error)(const clap_host_t *host, + const char *uri, + int32_t os_error, + const char *msg); + + // Informs the host that the following preset has been loaded. + // This contributes to keep in sync the host preset browser and plugin preset browser. + // If the preset was loaded from a container file, then the load_key must be set, otherwise it + // must be null. + // + // [main-thread] + void(CLAP_ABI *loaded)(const clap_host_t *host, const char *uri, const char *load_key); +} clap_host_preset_load_t; + #ifdef __cplusplus } #endif diff --git a/include/clap/ext/draft/remote-controls.h b/include/clap/ext/draft/remote-controls.h @@ -31,7 +31,7 @@ // Pressing that button once gets you to the first page of the section. // Press it again to cycle through the section's pages. -static CLAP_CONSTEXPR const char CLAP_EXT_REMOTE_CONTROLS[] = "clap.remote-controls.draft/1"; +static CLAP_CONSTEXPR const char CLAP_EXT_REMOTE_CONTROLS[] = "clap.remote-controls.draft/2"; #ifdef __cplusplus extern "C" { @@ -44,6 +44,10 @@ typedef struct clap_remote_controls_page { clap_id page_id; char page_name[CLAP_NAME_SIZE]; clap_id param_ids[CLAP_REMOTE_CONTROLS_COUNT]; + + // This is used to separate device pages versus preset pages. + // If true, then this page is specific to this preset. + bool is_for_preset; } clap_remote_controls_page_t; typedef struct clap_plugin_remote_controls { diff --git a/include/clap/ext/draft/surround.h b/include/clap/ext/draft/surround.h @@ -24,7 +24,7 @@ // 3. host calls clap_plugin_surround->get_channel_map() // 4. host activates the plugin and can start processing audio -static CLAP_CONSTEXPR const char CLAP_EXT_SURROUND[] = "clap.surround.draft/1"; +static CLAP_CONSTEXPR const char CLAP_EXT_SURROUND[] = "clap.surround.draft/2"; static CLAP_CONSTEXPR const char CLAP_PORT_SURROUND[] = "surround"; @@ -55,9 +55,13 @@ enum { typedef struct clap_plugin_surround { // Stores into the channel_map array, the surround identifer of each channels. - // Returns the number of elements stored in channel_map + // Returns the number of elements stored in channel_map. + // + // config_id: the configuration id, see clap_plugin_audio_ports_config. + // If config_id is CLAP_INVALID_ID, then this function queries the current port info. // [main-thread] uint32_t(CLAP_ABI *get_channel_map)(const clap_plugin_t *plugin, + clap_id config_id, bool is_input, uint32_t port_index, uint8_t *channel_map, 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" @@ -46,7 +46,7 @@ /// (latency, audio ports, new parameters, ...) be sure to wait for the host /// to deactivate the plugin to apply those changes. /// If there are no breaking changes, the plugin can apply them them right away. -/// The plugin is resonsible for updating both its audio processor and its gui. +/// The plugin is responsible for updating both its audio processor and its gui. /// /// II. Turning a knob on the DAW interface /// - the host will send an automation event to the plugin via a process() or flush() @@ -63,8 +63,8 @@ /// - the plugin is responsible for updating its GUI /// /// V. Turning a knob via plugin's internal MIDI mapping -/// - the plugin sends a CLAP_EVENT_PARAM_SET output event, set should_record to false -/// - the plugin is responsible to update its GUI +/// - the plugin sends a CLAP_EVENT_PARAM_VALUE output event, set should_record to false +/// - the plugin is responsible for updating its GUI /// /// VI. Adding or removing parameters /// - if the plugin is activated call clap_host->restart() @@ -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/factory/draft/plugin-invalidation.h b/include/clap/factory/draft/plugin-invalidation.h @@ -0,0 +1,47 @@ +#pragma once + +#include "../../private/std.h" +#include "../../private/macros.h" + +// Use it to retrieve const clap_plugin_invalidation_factory_t* from +// clap_plugin_entry.get_factory() +static const CLAP_CONSTEXPR char CLAP_PLUGIN_INVALIDATION_FACTORY_ID[] = + "clap.plugin-invalidation-factory/draft0"; + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct clap_plugin_invalidation_source { + // Directory containing the file(s) to scan, must be absolute + const char *directory; + + // globing pattern, in the form *.dll + const char *filename_glob; + + // should the directory be scanned recursively? + bool recursive_scan; +} clap_plugin_invalidation_source_t; + +// Used to figure out when a plugin needs to be scanned again. +// Imagine a situation with a single entry point: my-plugin.clap which then scans itself +// a set of "sub-plugins". New plugin may be available even if my-plugin.clap file doesn't change. +// This interfaces solves this issue and gives a way to the host to monitor additional files. +typedef struct clap_plugin_invalidation_factory { + // Get the number of invalidation source. + uint32_t(CLAP_ABI *count)(const struct clap_plugin_invalidation_factory *factory); + + // Get the invalidation source by its index. + // [thread-safe] + const clap_plugin_invalidation_source_t *(CLAP_ABI *get)( + const struct clap_plugin_invalidation_factory *factory, uint32_t index); + + // In case the host detected a invalidation event, it can call refresh() to let the + // plugin_entry update the set of plugins available. + // If the function returned false, then the plugin needs to be reloaded. + bool(CLAP_ABI *refresh)(const struct clap_plugin_invalidation_factory *factory); +} clap_plugin_invalidation_factory_t; + +#ifdef __cplusplus +} +#endif diff --git a/include/clap/factory/draft/preset-discovery.h b/include/clap/factory/draft/preset-discovery.h @@ -0,0 +1,316 @@ +/* + Preset Discovery API. + + Preset Discovery enables a plug-in host to identify where presets are found, what + extensions they have, which plug-ins they apply to, and other metadata associated with the + presets so that they can be indexed and searched for quickly within the plug-in host's browser. + + This has a number of advantages for the user: + - it allows them to browse for presets from one central location in a consistent way + - the user can browse for presets without having to commit to a particular plug-in first + + The API works as follow to index presets and presets metadata: + 1. clap_plugin_entry.get_factory(CLAP_PRESET_DISCOVERY_FACTORY_ID) + 2. clap_preset_discovery_factory_t.create(...) + 3. clap_preset_discovery_provider.init() (only necessary the first time, declarations + can be cached) + `-> clap_preset_discovery_indexer.declare_filetype() + `-> clap_preset_discovery_indexer.declare_location() + `-> clap_preset_discovery_indexer.declare_soundpack() (optional) + `-> clap_preset_discovery_indexer.set_invalidation_watch_file() (optional) + 4. crawl the given locations and monitor file system changes + `-> clap_preset_discovery_indexer.get_metadata() for each presets files + + Then to load a preset, use ext/draft/preset-load.h. + TODO: create a dedicated repo for other plugin abi preset-load extension. + + The design of this API deliberately does not define a fixed set tags or categories. It is the + plug-in host's job to try to intelligently map the raw list of features that are found for a + preset and to process this list to generate something that makes sense for the host's tagging and + categorization system. The reason for this is to reduce the work for a plug-in developer to add + Preset Discovery support for their existing preset file format and not have to be concerned with + all the different hosts and how they want to receive the metadata. + + VERY IMPORTANT: + - the whole indexing process has to be **fast** + - clap_preset_provider->get_metadata() has to be fast and avoid unnecessary operations + - the whole indexing process must not be interactive + - don't show dialogs, windows, ... + - don't ask for user input +*/ + +#pragma once + +#include "../../private/std.h" +#include "../../private/macros.h" +#include "../../version.h" + +// Use it to retrieve const clap_preset_discovery_factory_t* from +// clap_plugin_entry.get_factory() +static const CLAP_CONSTEXPR char CLAP_PRESET_DISCOVERY_FACTORY_ID[] = + "clap.preset-discovery-factory/draft-1"; + +#ifdef __cplusplus +extern "C" { +#endif + +enum clap_preset_discovery_flags { + // This is for factory or sound-pack presets. + CLAP_PRESET_DISCOVERY_IS_FACTORY_CONTENT = 1 << 0, + + // This is for user presets. + CLAP_PRESET_DISCOVERY_IS_USER_CONTENT = 1 << 1, + + // This location is meant for demo presets, those are preset which may trigger + // some limitation in the plugin because they require additionnal features which the user + // needs to purchase or the content itself needs to be bought and is only available in + // demo mode. + CLAP_PRESET_DISCOVERY_IS_DEMO_CONTENT = 1 << 2, + + // This preset is a user's favorite + CLAP_PRESET_DISCOVERY_IS_FAVORITE = 1 << 3, +}; + +// TODO: move clap_timestamp_t, CLAP_TIMESTAMP_UNKNOWN and clap_plugin_id_t to parent files once we +// settle with preset discovery + +// This type defines a timestamp: the number of seconds since UNIX EPOCH. +// See C's time_t time(time_t *). +typedef uint64_t clap_timestamp_t; + +// Value for unknown timestamp. +static const clap_timestamp_t CLAP_TIMESTAMP_UNKNOWN = 0; + +// Pair of plugin ABI and plugin identifier +typedef struct clap_plugin_id { + // The plugin ABI name, in lowercase. + // eg: "clap" + const char *abi; + + // The plugin ID, for example "com.u-he.Diva". + // If the ABI rely upon binary plugin ids, then they shall be hex encoded (lower case). + const char *id; +} clap_plugin_id_t; + +// Receiver that receives the metadata for a single preset file. +// The host would define the various callbacks in this interface and the preset parser function +// would then call them. +// +// This interface isn't thread-safe. +typedef struct clap_preset_discovery_metadata_receiver { + void *receiver_data; // reserved pointer for the metadata receiver + + // If there is an error reading metadata from a file this should be called with an error + // message. + // os_error: the operating system error, if applicable. If not applicable set it to a non-error + // value, eg: 0 on unix and Windows. + void(CLAP_ABI *on_error)(const struct clap_preset_discovery_metadata_receiver *receiver, + int32_t os_error, + const char *error_message); + + // This must be called for every preset in the file and before any preset metadata is + // sent with the calls below. + // + // If the preset file is a preset container then name and load_key are mandatory, + // otherwise they must be null. + // + // The load_key is a machine friendly string used to load the preset inside the container via a + // the preset-load plug-in extension. The load_key can also just be the subpath if that's what + // the plugin wants but it could also be some other unique id like a database primary key or a + // binary offset. It's use is entirely up to the plug-in. + // + // If the function returns false, the the provider must stop calling back into the receiver. + bool(CLAP_ABI *begin_preset)(const struct clap_preset_discovery_metadata_receiver *receiver, + const char *name, + const char *load_key); + + // Adds a plug-in id that this preset can be used with. + void(CLAP_ABI *add_plugin_id)(const struct clap_preset_discovery_metadata_receiver *receiver, + const clap_plugin_id_t *plugin_id); + + // Sets the sound pack to which the preset belongs to. + void(CLAP_ABI *set_soundpack_id)(const struct clap_preset_discovery_metadata_receiver *receiver, + const char *soundpack_id); + + // Sets the flags, see clap_preset_discovery_flags. + // If unset, they are then inherited from the location. + void(CLAP_ABI *set_flags)(const struct clap_preset_discovery_metadata_receiver *receiver, + uint32_t flags); + + // Adds a creator name for the preset. + void(CLAP_ABI *add_creator)(const struct clap_preset_discovery_metadata_receiver *receiver, + const char *creator); + + // Sets a description of the preset. + void(CLAP_ABI *set_description)(const struct clap_preset_discovery_metadata_receiver *receiver, + const char *description); + + // Sets the creation time and last modification time of the preset. + // If one of the times isn't known, set it to CLAP_TIMESTAMP_UNKNOWN. + // If this function is not called, then the indexer may look at the file's creation and + // modification time. + void(CLAP_ABI *set_timestamps)(const struct clap_preset_discovery_metadata_receiver *receiver, + clap_timestamp_t creation_time, + clap_timestamp_t modification_time); + + // Adds a feature to the preset. + // + // The feature string is arbitrary, it is the indexer's job to understand it and remap it to its + // internal categorization and tagging system. + // + // However, the strings from plugin-features.h should be understood by the indexer and one of the + // plugin category could be provided to determine if the preset will result into an audio-effect, + // instrument, ... + // + // Examples: + // kick, drum, tom, snare, clap, cymbal, bass, lead, metalic, hardsync, crossmod, acid, + // distorted, drone, pad, dirty, etc... + void(CLAP_ABI *add_feature)(const struct clap_preset_discovery_metadata_receiver *receiver, + const char *feature); + + // Adds extra information to the metadata. + void(CLAP_ABI *add_extra_info)(const struct clap_preset_discovery_metadata_receiver *receiver, + const char *key, + const char *value); +} clap_preset_discovery_metadata_receiver_t; + +typedef struct clap_preset_discovery_filetype { + const char *name; + const char *description; + + // `.' isn't included in the string. + // If empty or NULL then every file should be matched. + const char *file_extension; +} clap_preset_discovery_filetype_t; + +// Defines a place in which to search for presets +typedef struct clap_preset_discovery_location { + uint32_t flags; // see enum clap_preset_discovery_flags + const char *name; // name of this location + + // URI: + // - file:/// for pointing to a file or directory; directories are scanned recursively + // eg: file:///home/abique/.u-he/Diva/Presets/Diva (on Linux) + // eg: file:///C:/Users/abique/Documents/u-he/Diva.data/Presets/ (on Windows) + // + // - plugin:// for presets which are bundled within the plugin DSO. + // In that case, the uri must be exactly `plugin://` and nothing more. + const char *uri; +} clap_preset_discovery_location_t; + +// Describes an installed sound pack. +typedef struct clap_preset_discovery_soundpack { + uint64_t flags; // see enum clap_preset_discovery_flags + const char *id; // sound pack identifier + const char *name; // name of this sound pack + const char *description; // reasonably short description of the sound pack + const char *homepage_url; // url to the pack's homepage + const char *vendor; // sound pack's vendor + const char *image_uri; // may be an image on disk or from an http server + clap_timestamp_t release_timestamp; // release date, CLAP_TIMESTAMP_UNKNOWN if unavailable +} clap_preset_discovery_soundpack_t; + +// Describes a preset provider +typedef struct clap_preset_discovery_provider_descriptor { + clap_version_t clap_version; // initialized to CLAP_VERSION + const char *id; // see plugin.h for advice on how to choose a good identifier + const char *name; // eg: "Diva's preset provider" + const char *vendor; // eg: u-he +} clap_preset_discovery_provider_descriptor_t; + +// This interface isn't thread-safe. +typedef struct clap_preset_discovery_provider { + const clap_preset_discovery_provider_descriptor_t *desc; + + void *provider_data; // reserved pointer for the provider + + // Initialize the preset provider. + // It should declare all its locations, filetypes and sound packs. + // Returns false if initialization failed. + bool(CLAP_ABI *init)(const struct clap_preset_discovery_provider *provider); + + // Destroys the preset provider + void(CLAP_ABI *destroy)(const struct clap_preset_discovery_provider *provider); + + // reads metadata from the given file and passes them to the metadata receiver + bool(CLAP_ABI *get_metadata)(const struct clap_preset_discovery_provider *provider, + const char *uri, + const clap_preset_discovery_metadata_receiver_t *metadata_receiver); + + // Query an extension. + // The returned pointer is owned by the provider. + // It is forbidden to call it before provider->init(). + // You can call it within provider->init() call, and after. + const void *(CLAP_ABI *get_extension)(const struct clap_preset_discovery_provider *provider, + const char *extension_id); +} clap_preset_discovery_provider_t; + +// This interface isn't thread-safe +typedef struct clap_preset_discovery_indexer { + clap_version_t clap_version; // initialized to CLAP_VERSION + 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", see plugin.h for advice on how to format the version + + void *indexer_data; // reserved pointer for the indexer + + // Declares a preset filetype. + // Don't callback into the provider during this call. + // Returns false if the filetype is invalid. + bool(CLAP_ABI *declare_filetype)(const struct clap_preset_discovery_indexer *indexer, + const clap_preset_discovery_filetype_t *filetype); + + // Declares a preset location. + // Don't callback into the provider during this call. + // Returns false if the location is invalid. + bool(CLAP_ABI *declare_location)(const struct clap_preset_discovery_indexer *indexer, + const clap_preset_discovery_location_t *location); + + // Declares a sound pack. + // Don't callback into the provider during this call. + // Returns false if the sound pack is invalid. + bool(CLAP_ABI *declare_soundpack)(const struct clap_preset_discovery_indexer *indexer, + const clap_preset_discovery_soundpack_t *soundpack); + + // Query an extension. + // The returned pointer is owned by the indexer. + // It is forbidden to call it before provider->init(). + // You can call it within provider->init() call, and after. + const void *(CLAP_ABI *get_extension)(const struct clap_preset_discovery_indexer *indexer, + const char *extension_id); +} clap_preset_discovery_indexer_t; + +// Every methods in this factory must be thread-safe. +// It is encourraged to perform preset indexing in background threads, maybe even in background +// process. +// +// The host may use clap_plugin_invalidation_factory to detect filesystem changes +// which may change the factory's content. +typedef struct clap_preset_discovery_factory { + // Get the number of preset providers available. + // [thread-safe] + uint32_t(CLAP_ABI *count)(const struct clap_preset_discovery_factory *factory); + + // Retrieves a preset provider descriptor by its index. + // Returns null in case of error. + // The descriptor must not be freed. + // [thread-safe] + const clap_preset_discovery_provider_descriptor_t *(CLAP_ABI *get_descriptor)( + const struct clap_preset_discovery_factory *factory, uint32_t index); + + // Create a preset provider by its id. + // The returned pointer must be freed by calling preset_provider->destroy(preset_provider); + // The preset provider is not allowed to use the indexer callbacks in the create method. + // It is forbidden to call back into the indexer before the indexer calls provider->init(). + // Returns null in case of error. + // [thread-safe] + const clap_preset_discovery_provider_t *(CLAP_ABI *create)( + const struct clap_preset_discovery_factory *factory, + const clap_preset_discovery_indexer_t *indexer, + const char *provider_id); +} clap_preset_discovery_factory_t; + +#ifdef __cplusplus +} +#endif diff --git a/include/clap/factory/plugin-factory.h b/include/clap/factory/plugin-factory.h @@ -0,0 +1,42 @@ +#pragma once + +#include "../plugin.h" + +// Use it to retrieve const clap_plugin_factory_t* from +// clap_plugin_entry.get_factory() +static const CLAP_CONSTEXPR char CLAP_PLUGIN_FACTORY_ID[] = "clap.plugin-factory"; + +#ifdef __cplusplus +extern "C" { +#endif + +// Every method must be thread-safe. +// It is very important to be able to scan the plugin as quickly as possible. +// +// The host may use clap_plugin_invalidation_factory to detect filesystem changes +// which may change the factory's content. +typedef struct clap_plugin_factory { + // Get the number of plugins available. + // [thread-safe] + uint32_t(CLAP_ABI *get_plugin_count)(const struct clap_plugin_factory *factory); + + // Retrieves a plugin descriptor by its index. + // Returns null in case of error. + // The descriptor must not be freed. + // [thread-safe] + const clap_plugin_descriptor_t *(CLAP_ABI *get_plugin_descriptor)( + const struct clap_plugin_factory *factory, uint32_t index); + + // Create a clap_plugin by its plugin_id. + // The returned pointer must be freed by calling plugin->destroy(plugin); + // The plugin is not allowed to use the host callbacks in the create method. + // Returns null in case of error. + // [thread-safe] + const clap_plugin_t *(CLAP_ABI *create_plugin)(const struct clap_plugin_factory *factory, + const clap_host_t *host, + const char *plugin_id); +} clap_plugin_factory_t; + +#ifdef __cplusplus +} +#endif diff --git a/include/clap/host.h b/include/clap/host.h @@ -15,9 +15,12 @@ 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. + // The returned pointer is owned by the host. + // 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-factory.h b/include/clap/plugin-factory.h @@ -1,40 +0,0 @@ -#pragma once - -#include "plugin.h" - -static const CLAP_CONSTEXPR char CLAP_PLUGIN_FACTORY_ID[] = "clap.plugin-factory"; - -#ifdef __cplusplus -extern "C" { -#endif - -// Every method must be thread-safe. -// It is very important to be able to scan the plugin as quickly as possible. -// -// The host may use clap_plugin_invalidation_factory to detect filesystem changes -// which may change the factory's content. -typedef struct clap_plugin_factory { - // Get the number of plugins available. - // [thread-safe] - uint32_t(CLAP_ABI *get_plugin_count)(const struct clap_plugin_factory *factory); - - // Retrieves a plugin descriptor by its index. - // Returns null in case of error. - // The descriptor must not be freed. - // [thread-safe] - const clap_plugin_descriptor_t *(CLAP_ABI *get_plugin_descriptor)( - const struct clap_plugin_factory *factory, uint32_t index); - - // Create a clap_plugin by its plugin_id. - // The returned pointer must be freed by calling plugin->destroy(plugin); - // The plugin is not allowed to use the host callbacks in the create method. - // Returns null in case of error. - // [thread-safe] - const clap_plugin_t *(CLAP_ABI *create_plugin)(const struct clap_plugin_factory *factory, - const clap_host_t *host, - const char *plugin_id); -} clap_plugin_factory_t; - -#ifdef __cplusplus -} -#endif diff --git a/include/clap/plugin-features.h b/include/clap/plugin-features.h @@ -1,7 +1,5 @@ #pragma once -#include "private/macros.h" - // This file provides a set of standard plugin features meant to be used // within clap_plugin_descriptor.features. // @@ -23,6 +21,9 @@ // Add this feature if your plugin is a note effect or a note generator/sequencer #define CLAP_PLUGIN_FEATURE_NOTE_EFFECT "note-effect" +// Add this feature if your plugin converts audio to notes +#define CLAP_PLUGIN_FEATURE_NOTE_DETECTOR "note-detector" + // Add this feature if your plugin is an analyzer #define CLAP_PLUGIN_FEATURE_ANALYZER "analyzer" diff --git a/include/clap/plugin-invalidation.h b/include/clap/plugin-invalidation.h @@ -1,45 +0,0 @@ -#pragma once - -#include "private/std.h" -#include "private/macros.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct clap_plugin_invalidation_source { - // Directory containing the file(s) to scan, must be absolute - const char *directory; - - // globing pattern, in the form *.dll - const char *filename_glob; - - // should the directory be scanned recursively? - bool recursive_scan; -} clap_plugin_invalidation_source_t; - -static const CLAP_CONSTEXPR char CLAP_PLUGIN_INVALIDATION_FACTORY_ID[] = - "clap.plugin-invalidation-factory/draft0"; - -// Used to figure out when a plugin needs to be scanned again. -// Imagine a situation with a single entry point: my-plugin.clap which then scans itself -// a set of "sub-plugins". New plugin may be available even if my-plugin.clap file doesn't change. -// This interfaces solves this issue and gives a way to the host to monitor additional files. -typedef struct clap_plugin_invalidation_factory { - // Get the number of invalidation source. - uint32_t(CLAP_ABI *count)(const struct clap_plugin_invalidation_factory *factory); - - // Get the invalidation source by its index. - // [thread-safe] - const clap_plugin_invalidation_source_t *(CLAP_ABI *get)( - const struct clap_plugin_invalidation_factory *factory, uint32_t index); - - // In case the host detected a invalidation event, it can call refresh() to let the - // plugin_entry update the set of plugins available. - // If the function returned false, then the plugin needs to be reloaded. - bool(CLAP_ABI *refresh)(const struct clap_plugin_invalidation_factory *factory); -} clap_plugin_invalidation_factory_t; - -#ifdef __cplusplus -} -#endif 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 7 + #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) ((CLAP_VERSION_MAJOR < (maj)) || \ + ((maj) == CLAP_VERSION_MAJOR && CLAP_VERSION_MINOR < (min)) || \ + ((maj) == CLAP_VERSION_MAJOR && (min) == CLAP_VERSION_MINOR && CLAP_VERSION_REVISION < (rev))) +#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,54 @@ // 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 + +#if !CLAP_VERSION_LT(CLAP_VERSION_MAJOR + 1, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION) +#error CLAP_VERSION_LT is inconsistent (MAJOR) +#endif +#if !CLAP_VERSION_LT(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR + 1, CLAP_VERSION_REVISION) +#error CLAP_VERSION_LT is inconsistent (MINOR) +#endif +#if !CLAP_VERSION_LT(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION + 1) +#error CLAP_VERSION_LT is inconsistent (REVISION) +#endif + +#if CLAP_VERSION_LT(CLAP_VERSION_MAJOR - 1, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION) +#error CLAP_VERSION_LT is inconsistent (MAJOR) +#endif +#if CLAP_VERSION_LT(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR - 1, CLAP_VERSION_REVISION) +#error CLAP_VERSION_LT is inconsistent (MINOR) +#endif +#if CLAP_VERSION_LT(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION - 1) +#error CLAP_VERSION_LT is inconsistent (REVISION) +#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,