clap

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

commit 650ad6a6b0215b3f964c49a004667ad876689dfd
parent 84531b931c12285097746a9cae77690a680b8aa0
Author: Alexandre Bique <bique.alexandre@gmail.com>
Date:   Thu, 22 Dec 2022 10:44:27 +0100

Merge pull request #241 from free-audio/next

clap 1.1.4
Diffstat:
MCMakeLists.txt | 6+++---
MChangeLog.md | 27+++++++++++++++++++++++++++
MREADME.md | 6+++++-
Minclude/clap/audio-buffer.h | 2+-
Minclude/clap/clap.h | 8++++++--
Minclude/clap/events.h | 3++-
Minclude/clap/ext/audio-ports-config.h | 3++-
Ainclude/clap/ext/draft/audio-ports-activation.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/clap/ext/draft/context-menu.h | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dinclude/clap/ext/draft/file-reference.h | 96-------------------------------------------------------------------------------
Ainclude/clap/ext/draft/param-indication.h | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dinclude/clap/ext/draft/quick-controls.h | 47-----------------------------------------------
Ainclude/clap/ext/draft/remote-controls.h | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/clap/ext/draft/resource-directory.h | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/clap/ext/draft/track-info.h | 40++++++++++++++++++++++++++++++++--------
Ainclude/clap/ext/draft/triggers.h | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/clap/ext/params.h | 41+++++++++++++++++++++++++++++++++++++++--
Minclude/clap/ext/thread-pool.h | 4++--
Minclude/clap/plugin.h | 2++
Minclude/clap/private/std.h | 2+-
Minclude/clap/version.h | 5+++--
Msrc/plugin-template.c | 6+-----
22 files changed, 706 insertions(+), 172 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -10,7 +10,7 @@ set(CLAP_VERSION_MINOR ${CMAKE_MATCH_1}) string(REGEX MATCH "CLAP_VERSION_REVISION \\(\\(uint32_t\\)([0-9]+)\\)" _ ${clap_version_header}) set(CLAP_VERSION_REVISION ${CMAKE_MATCH_1}) -message("CLAP version: ${CLAP_VERSION_MAJOR}.${CLAP_VERSION_MINOR}.${CLAP_VERSION_REVISION}") +message(STATUS "CLAP version: ${CLAP_VERSION_MAJOR}.${CLAP_VERSION_MINOR}.${CLAP_VERSION_REVISION}") project(CLAP LANGUAGES C CXX VERSION ${CLAP_VERSION_MAJOR}.${CLAP_VERSION_MINOR}.${CLAP_VERSION_REVISION}) @@ -53,7 +53,7 @@ if (${CLAP_BUILD_TESTS}) macro(clap_compile_cpp SUFFIX EXT STDC STDCPP) add_executable(clap-compile-${SUFFIX} EXCLUDE_FROM_ALL src/main.${EXT}) - target_link_libraries(clap-compile-${SUFFIX} clap-core) + target_link_libraries(clap-compile-${SUFFIX} clap) set_target_properties(clap-compile-${SUFFIX} PROPERTIES C_STANDARD ${STDC} CXX_STANDARD ${STDCPP}) @@ -77,7 +77,7 @@ if (${CLAP_BUILD_TESTS}) 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-core) + target_link_libraries(clap-plugin-template PRIVATE clap) set_target_properties(clap-plugin-template PROPERTIES C_STANDARD 11) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") diff --git a/ChangeLog.md b/ChangeLog.md @@ -1,3 +1,30 @@ +# Changes in 1.1.4 + +* CMake: update some targets to link against `clap` instead of `clap-core` +* [params.h](include/clap/ext/params.h): clarify parameter range change, fix documentation typos, add missing items +* [plugin.h](include/clap/plugin.h): clarify data lifetime in `process()` +* [audio-ports-config.h](include/clap/ext/audio-ports-config.h): clarify `select()`: if succeed, the host should rescan the audio ports + +## Draft extensions + +### New + +* [audio-ports-activation.h](include/clap/ext/draft/audio-ports-activation.h): new draft extension which allows a host to inform a plugin whether an audio port is an active port in the host audio context, and allow the host to respond accordingly +* [context-menu.h](include/clap/ext/draft/context-menu.h): new draft extension which let the host and plugin exchange context menu entries and popup the menu +* [param-indication.h](include/clap/ext/draft/param-indication.h): new draft extension which let the host inform the plugin that a parameter is currently mapped to a physical controller +* [remote-controls.h](include/clap/ext/draft/remote-controls.h): new draft extension which replaces `quick-controls.h` +* [resource-directory.h](include/clap/ext/draft/resource-directory.h): new draft extension which lets the plugin save resources in a directory provided by the host +* [triggers.h](include/clap/ext/draft/triggers.h): new draft extension which exposes triggers to the host, triggers are data-less events + +### Improved + +* [track-info](include/clap/ext/draft/track-info.h): refine the draft extension + +### Removed + +* `file-reference.h`: removed in favor of [resource-directory.h](include/clap/ext/draft/resource-directory.h) +* `quick-controls.h`: removed in favor of [remote-controls.h](include/clap/ext/draft/remote-controls.h) + # Changes in 1.1.3 * CMake: generate CMake and pkg-config package files on install diff --git a/README.md b/README.md @@ -12,6 +12,7 @@ - [Fundamental extensions](#fundamental-extensions) - [Support extensions](#support-extensions) - [Extra extensions](#extra-extensions) + - [Third-party extensions](#third-party-extensions) - [Adapters](#adapters) - [Resources](#resources) - [Examples](#examples) @@ -103,6 +104,10 @@ and use to get a basic plugin experience: - [surround](include/clap/ext/draft/surround.h), inspect surround channel mapping - [ambisonic](include/clap/ext/draft/ambisonic.h), inspect ambisonic channel mapping +## Third-party extensions + +- [`cockos.reaper_extension`](https://github.com/justinfrankel/reaper-sdk/blob/main/reaper-plugins/reaper_plugin.h#L138), access the [REAPER](http://reaper.fm) API + # Adapters - [clap-wrapper](https://github.com/free-audio/clap-wrapper), wrappers for using CLAP in other plugin environments @@ -116,7 +121,6 @@ and use to get a basic plugin experience: - [clap-host](https://github.com/free-audio/clap-host), very simple host - [clap-plugins](https://github.com/free-audio/clap-plugins), very simple plugins -- [schwaaa's plugin](https://github.com/schwaaa/clap-plugin), basic example for prototyping CLAP audio plugins using Dear ImGui as the user interface ## Community related projects diff --git a/include/clap/audio-buffer.h b/include/clap/audio-buffer.h @@ -28,7 +28,7 @@ typedef struct clap_audio_buffer { float **data32; double **data64; uint32_t channel_count; - uint32_t latency; // latency from/to the audio interface + uint32_t latency; // latency from/to the audio interface uint64_t constant_mask; } clap_audio_buffer_t; diff --git a/include/clap/clap.h b/include/clap/clap.h @@ -52,12 +52,16 @@ #include "ext/voice-info.h" #include "ext/draft/ambisonic.h" +#include "ext/draft/audio-ports-activation.h" +#include "ext/draft/context-menu.h" #include "ext/draft/cv.h" -#include "ext/draft/file-reference.h" #include "ext/draft/midi-mappings.h" +#include "ext/draft/param-indication.h" #include "ext/draft/preset-load.h" -#include "ext/draft/quick-controls.h" +#include "ext/draft/remote-controls.h" +#include "ext/draft/resource-directory.h" #include "ext/draft/state-context.h" #include "ext/draft/surround.h" #include "ext/draft/track-info.h" +#include "ext/draft/triggers.h" #include "ext/draft/tuning.h" diff --git a/include/clap/events.h b/include/clap/events.h @@ -50,7 +50,8 @@ enum { // A NOTE_ON with a velocity of 0 is valid and should not be interpreted as a NOTE_OFF. // // NOTE_CHOKE is meant to choke the voice(s), like in a drum machine when a closed hihat - // chokes an open hihat. This event can be sent by the host to the plugin. Here are two use cases: + // chokes an open hihat. This event can be sent by the host to the plugin. Here are two use + // cases: // - a plugin is inside a drum pad in Bitwig Studio's drum machine, and this pad is choked by // another one // - the user double clicks the DAW's stop button in the transport which then stops the sound on diff --git a/include/clap/ext/audio-ports-config.h b/include/clap/ext/audio-ports-config.h @@ -59,7 +59,8 @@ typedef struct clap_plugin_audio_ports_config { clap_audio_ports_config_t *config); // selects the configuration designated by id - // returns true if the configuration could be applied + // returns true if the configuration could be applied. + // Once applied the host should scan again the audio ports. // [main-thread,plugin-deactivated] bool(CLAP_ABI *select)(const clap_plugin_t *plugin, clap_id config_id); } clap_plugin_audio_ports_config_t; diff --git a/include/clap/ext/draft/audio-ports-activation.h b/include/clap/ext/draft/audio-ports-activation.h @@ -0,0 +1,54 @@ +#pragma once + +#include "../../plugin.h" + +/// @page Audio Ports Activation +/// +/// 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 +/// - the plugin knows that an output is not consumed by the host, and doesn't need to +/// compute it +/// +/// Audio ports can only be activated or deactivated when the plugin is deactivated, unless +/// can_activate_while_processing() returns true. +/// +/// Audio buffers must still be provided if the audio port is deactivated. +/// In such case, they shall be filled with 0 (or whatever is the neutral value in your context) +/// and the constant_mask shall be set. +/// +/// Audio ports are initially in the active state after creating the plugin instance. +/// Audio ports state are not saved in the plugin state, so the host must restore the +/// audio ports state after creating the plugin instance. +/// +/// Audio ports state is invalidated by clap_plugin_audio_ports_config.select() and +/// 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"; + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct clap_plugin_audio_ports_activation { + // Returns true if the plugin supports activation/deactivation while processing. + // [main-thread] + bool(CLAP_ABI *can_activate_while_processing)(const clap_plugin_t *plugin); + + // Activate the given port. + // + // It is only possible to 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, + bool is_input, + uint32_t port_index, + bool is_active); +} clap_plugin_audio_ports_activation_t; + +#ifdef __cplusplus +} +#endif diff --git a/include/clap/ext/draft/context-menu.h b/include/clap/ext/draft/context-menu.h @@ -0,0 +1,146 @@ +#pragma once + +#include "../../plugin.h" + +// This extension lets the host and plugin exchange menu items and let the plugin ask the host to +// show its context menu. + +static CLAP_CONSTEXPR const char CLAP_EXT_CONTEXT_MENU[] = "clap.context-menu.draft/0"; + +#ifdef __cplusplus +extern "C" { +#endif + +// There can be different target kind for a context menu +enum { + CLAP_CONTEXT_MENU_TARGET_KIND_GLOBAL = 0, + CLAP_CONTEXT_MENU_TARGET_KIND_PARAM = 1, + // TODO: kind trigger once the trigger ext is marked as stable +}; + +// Describes the context menu target +typedef struct clap_context_menu_target { + uint32_t kind; + clap_id id; +} clap_context_menu_target_t; + +enum { + // Adds a clickable menu entry. + // data: const clap_context_menu_item_entry_t* + CLAP_CONTEXT_MENU_ITEM_ENTRY, + + // Adds a clickable menu entry which will feature both a checkmark and a label. + // data: const clap_context_menu_item_check_entry_t* + CLAP_CONTEXT_MENU_ITEM_CHECK_ENTRY, + + // Adds a separator line. + // data: NULL + CLAP_CONTEXT_MENU_ITEM_SEPARATOR, + + // Starts a sub menu with the given label. + // data: const clap_context_menu_item_begin_submenu_t* + CLAP_CONTEXT_MENU_ITEM_BEGIN_SUBMENU, + + // Ends the current sub menu. + // data: NULL + CLAP_CONTEXT_MENU_ITEM_END_SUBMENU, +}; +typedef uint32_t clap_context_menu_item_kind_t; + +typedef struct clap_context_menu_entry { + // text to be displayed + const char *label; + + // if false, then the menu entry is greyed out and not clickable + bool is_enabled; + clap_id action_id; +} clap_context_menu_entry_t; + +typedef struct clap_context_menu_check_entry { + // text to be displayed + const char *label; + + // if false, then the menu entry is greyed out and not clickable + bool is_enabled; + + // if true, then the menu entry will be displayed as checked + bool is_checked; + clap_id action_id; +} clap_context_menu_check_entry_t; + +typedef struct clap_context_menu_submenu { + // text to be displayed + const char *label; + + // if false, then the menu entry is greyed out and won't show submenu + bool is_enabled; +} clap_context_menu_submenu_t; + +// Context menu builder. +// This object isn't thread-safe and must be used on the same thread as it was provided. +typedef struct clap_context_menu_builder { + void *ctx; + + // Adds an entry to the menu. + // entry_data type is determined by entry_kind. + bool(CLAP_ABI *add_item)(const struct clap_context_menu_builder *builder, + clap_context_menu_item_kind_t item_kind, + const void *item_data); + + // Returns true if the menu builder supports the given item kind + bool(CLAP_ABI *supports)(const struct clap_context_menu_builder *builder, + clap_context_menu_item_kind_t item_kind); +} clap_context_menu_builder_t; + +typedef struct clap_plugin_context_menu { + // Insert plugin's menu items into the menu builder. + // If target is null, assume global context. + // [main-thread] + bool(CLAP_ABI *populate)(const clap_plugin_t *plugin, + const clap_context_menu_target_t *target, + const clap_context_menu_builder_t *builder); + + // Performs the given action, which was previously provided to the host via populate(). + // If target is null, assume global context. + // [main-thread] + bool(CLAP_ABI *perform)(const clap_plugin_t *plugin, + const clap_context_menu_target_t *target, + clap_id action_id); +} clap_plugin_context_menu_t; + +typedef struct clap_host_context_menu { + // Insert host's menu items into the menu builder. + // If target is null, assume global context. + // [main-thread] + bool(CLAP_ABI *populate)(const clap_host_t *host, + const clap_context_menu_target_t *target, + const clap_context_menu_builder_t *builder); + + // Performs the given action, which was previously provided to the plugin via populate(). + // If target is null, assume global context. + // [main-thread] + bool(CLAP_ABI *perform)(const clap_host_t *host, + const clap_context_menu_target_t *target, + clap_id action_id); + + // Returns true if the host can display a popup menu for the plugin. + // This may depends upon the current windowing system used to display the plugin, so the + // return value is invalidated after creating the plugin window. + // [main-thread] + bool(CLAP_ABI *can_popup)(const clap_host_t *host); + + // Shows the host popup menu for a given parameter. + // If the plugin is using embedded GUI, then x and y are relative to the plugin's window, + // otherwise they're absolute coordinate, and screen index might be set accordingly. + // If target is null, assume global context. + // [main-thread] + bool(CLAP_ABI *popup)(const clap_host_t *host, + const clap_context_menu_target_t *target, + int32_t screen_index, + int32_t x, + int32_t y); +} clap_host_context_menu_t; + +#ifdef __cplusplus +} +#endif diff --git a/include/clap/ext/draft/file-reference.h b/include/clap/ext/draft/file-reference.h @@ -1,96 +0,0 @@ -#pragma once - -#include "../../plugin.h" -#include "../../string-sizes.h" - -static CLAP_CONSTEXPR const char CLAP_EXT_FILE_REFERENCE[] = "clap.file-reference.draft/0"; - -#ifdef __cplusplus -extern "C" { -#endif - -/// @page File Reference -/// -/// This extension provides a way for the host to know about files which are used -/// by the plugin, like a wavetable, a sample, ... -/// -/// The host can then: -/// - collect and save -/// - search for missing files by using: -/// - filename -/// - hash -/// - file size -/// - be aware that some external file references are marked as dirty and need to be saved. -/// -/// Regarding the hashing algorithm, as of 2022 BLAKE3 seems to be the best choice in regards to -/// performances and robustness while also providing a very small pure C library with permissive -/// licensing. For more info see https://github.com/BLAKE3-team/BLAKE3 -/// -/// This extension only exposes one hashing algorithm on purpose. - -// This describes a file currently used by the plugin -typedef struct clap_file_reference { - clap_id resource_id; - - // Flag indicating that the plugin may be able to (re-)install a collection that provides - // this resource. DAWs can provide a user option to ignore or include this resource during - // "collect and save". - bool belongs_to_plugin_collection; - - size_t path_capacity; // [in] the number of bytes reserved in path - size_t path_size; // [out] the actual length of the path, can be bigger than path_capacity - char *path; // [in,out] absolute path to the file on the disk, must be null terminated, and - // may be truncated if the capacity is less than the size -} clap_file_reference_t; - -typedef struct clap_plugin_file_reference { - // Returns the number of file reference this plugin has - // [main-thread] - uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin); - - // Gets the file reference at index - // returns true on success - // [main-thread] - bool(CLAP_ABI *get)(const clap_plugin_t *plugin, - uint32_t index, - clap_file_reference_t *file_reference); - - // This method can be called even if the file is missing. - // So the plugin is encouraged to store the digest in its state. - // - // digest is an array of 32 bytes. - // - // [main-thread] - bool(CLAP_ABI *get_blake3_digest)(const clap_plugin_t *plugin, - clap_id resource_id, - uint8_t *digest); - - // This method can be called even if the file is missing. - // So the plugin is encouraged to store the file's size in its state. - // - // [main-thread] - bool(CLAP_ABI *get_file_size)(const clap_plugin_t *plugin, clap_id resource_id, uint64_t *size); - - // Updates the path to a file reference - // [main-thread] - bool(CLAP_ABI *update_path)(const clap_plugin_t *plugin, clap_id resource_id, const char *path); - - // Request all pending changes to be flushed to disk (e.g. for destructive - // sample editor plugins), needed during "collect and save". - // [main-thread] - bool(CLAP_ABI *save_resources)(const clap_plugin_t *plugin); -} clap_plugin_file_reference_t; - -typedef struct clap_host_file_reference { - // Informs the host that the file references have changed, the host should schedule a full rescan. - // [main-thread] - void(CLAP_ABI *changed)(const clap_host_t *host); - - // Informs the host that file contents have changed, a call to save_resources() is needed. - // [main-thread] - void(CLAP_ABI *set_dirty)(const clap_host_t *host, clap_id resource_id); -} clap_host_file_reference; - -#ifdef __cplusplus -} -#endif diff --git a/include/clap/ext/draft/param-indication.h b/include/clap/ext/draft/param-indication.h @@ -0,0 +1,73 @@ +#pragma once + +#include "../params.h" +#include "../../color.h" + +// This extension lets the host tell the plugin to display a little color based indication on the +// parameter. This can be used to indicate: +// - a physical controller is mapped to a parameter +// - the parameter is current playing an automation +// - the parameter is overriding the automation +// - etc... +// +// The color semantic depends upon the host here and the goal is to have a consistent experience +// across all plugins. + +static CLAP_CONSTEXPR const char CLAP_EXT_PARAM_INDICATION[] = "clap.param-indication.draft/4"; + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + // The host doesn't have an automation for this parameter + CLAP_PARAM_INDICATION_AUTOMATION_NONE = 0, + + // The host has an automation for this parameter, but it isn't playing it + CLAP_PARAM_INDICATION_AUTOMATION_PRESENT = 1, + + // The host is playing an automation for this parameter + CLAP_PARAM_INDICATION_AUTOMATION_PLAYING = 2, + + // The host is recording an automation on this parameter + CLAP_PARAM_INDICATION_AUTOMATION_RECORDING = 3, + + // The host should play an automation for this parameter, but the user has started to ajust this + // parameter and is overriding the automation playback + CLAP_PARAM_INDICATION_AUTOMATION_OVERRIDING = 4, +}; + +typedef struct clap_plugin_param_indication { + // Sets or clears a mapping indication. + // + // has_mapping: does the parameter currently has a mapping? + // color: if set, the color to use to highlight the control in the plugin GUI + // label: if set, a small string to display on top of the knob which identifies the hardware + // controller description: if set, a string which can be used in a tooltip, which describes the + // current mapping + // + // Parameter indications should not be saved in the plugin context, and are off by default. + // [main-thread] + void(CLAP_ABI *set_mapping)(const clap_plugin_t *plugin, + clap_id param_id, + bool has_mapping, + const clap_color_t *color, + const char *label, + const char *description); + + // Sets or clears an automation indication. + // + // automation_state: current automation state for the given parameter + // color: if set, the color to use to display the automation indication in the plugin GUI + // + // Parameter indications should not be saved in the plugin context, and are off by default. + // [main-thread] + void(CLAP_ABI *set_automation)(const clap_plugin_t *plugin, + clap_id param_id, + uint32_t automation_state, + const clap_color_t *color); +} clap_plugin_param_indication_t; + +#ifdef __cplusplus +} +#endif diff --git a/include/clap/ext/draft/quick-controls.h b/include/clap/ext/draft/quick-controls.h @@ -1,47 +0,0 @@ -#pragma once - -#include "../../plugin.h" -#include "../../string-sizes.h" - -// This extensions provides a set of pages, where each page contains up to 8 controls. -// Those controls are param_id, and they are meant to be mapped onto a physical controller. -// We chose 8 because this what most controllers offer, and it is more or less a standard. - -static CLAP_CONSTEXPR const char CLAP_EXT_QUICK_CONTROLS[] = "clap.quick-controls.draft/0"; - -#ifdef __cplusplus -extern "C" { -#endif - -enum { CLAP_QUICK_CONTROLS_COUNT = 8 }; - -typedef struct clap_quick_controls_page { - clap_id id; - char name[CLAP_NAME_SIZE]; - clap_id param_ids[CLAP_QUICK_CONTROLS_COUNT]; -} clap_quick_controls_page_t; - -typedef struct clap_plugin_quick_controls { - // [main-thread] - uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin); - - // [main-thread] - bool(CLAP_ABI *get)(const clap_plugin_t *plugin, - uint32_t page_index, - clap_quick_controls_page_t *page); -} clap_plugin_quick_controls_t; - -typedef struct clap_host_quick_controls { - // Informs the host that the quick controls have changed. - // [main-thread] - void(CLAP_ABI *changed)(const clap_host_t *host); - - // Suggest a page to the host because it correspond to what the user is currently editing in the - // plugin's GUI. - // [main-thread] - void(CLAP_ABI *suggest_page)(const clap_host_t *host, clap_id page_id); -} clap_host_quick_controls_t; - -#ifdef __cplusplus -} -#endif diff --git a/include/clap/ext/draft/remote-controls.h b/include/clap/ext/draft/remote-controls.h @@ -0,0 +1,74 @@ +#pragma once + +#include "../../plugin.h" +#include "../../string-sizes.h" + +// This extension let the plugin provide a structured way of mapping parameters to an hardware +// controller. +// +// This is done by providing a set of remote control pages organized by section. +// A page contains up to 8 controls, which references parameters using param_id. +// +// |`- [section:main] +// | `- [name:main] performance controls +// |`- [section:osc] +// | |`- [name:osc1] osc1 page +// | |`- [name:osc2] osc2 page +// | |`- [name:osc-sync] osc sync page +// | `- [name:osc-noise] osc noise page +// |`- [section:filter] +// | |`- [name:flt1] filter 1 page +// | `- [name:flt2] filter 2 page +// |`- [section:env] +// | |`- [name:env1] env1 page +// | `- [name:env2] env2 page +// |`- [section:lfo] +// | |`- [name:lfo1] env1 page +// | `- [name:lfo2] env2 page +// `- etc... +// +// One possible workflow is to have a set of buttons, which correspond to a section. +// 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"; + +#ifdef __cplusplus +extern "C" { +#endif + +enum { CLAP_REMOTE_CONTROLS_COUNT = 8 }; + +typedef struct clap_remote_controls_page { + char section_name[CLAP_NAME_SIZE]; + clap_id page_id; + char page_name[CLAP_NAME_SIZE]; + clap_id param_ids[CLAP_REMOTE_CONTROLS_COUNT]; +} clap_remote_controls_page_t; + +typedef struct clap_plugin_remote_controls { + // Returns the number of pages. + // [main-thread] + uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin); + + // Get a page by index. + // [main-thread] + bool(CLAP_ABI *get)(const clap_plugin_t *plugin, + uint32_t page_index, + clap_remote_controls_page_t *page); +} clap_plugin_remote_controls_t; + +typedef struct clap_host_remote_controls { + // Informs the host that the remote controls have changed. + // [main-thread] + void(CLAP_ABI *changed)(const clap_host_t *host); + + // Suggest a page to the host because it correspond to what the user is currently editing in the + // plugin's GUI. + // [main-thread] + void(CLAP_ABI *suggest_page)(const clap_host_t *host, clap_id page_id); +} clap_host_remote_controls_t; + +#ifdef __cplusplus +} +#endif diff --git a/include/clap/ext/draft/resource-directory.h b/include/clap/ext/draft/resource-directory.h @@ -0,0 +1,88 @@ +#pragma once + +#include "../../plugin.h" + +static CLAP_CONSTEXPR const char CLAP_EXT_RESOURCE_DIRECTORY[] = "clap.resource-directory.draft/0"; + +#ifdef __cplusplus +extern "C" { +#endif + +/// @page Resource Directory +/// +/// This extension provides a way for the plugin to store its resources as file in a directory +/// provided by the host and recover them later on. +/// +/// The plugin **must** store relative path in its state toward resource directories. +/// +/// Resource sharing: +/// - shared directory is shared among all plugin instances, hence mostly appropriate for read-only +/// content +/// -> suitable for read-only content +/// - exclusive directory is exclusive to the plugin instance +/// -> if the plugin, then its exclusive directory must be duplicated too +/// -> suitable for read-write content +/// +/// Keeping the shared directory clean: +/// - to avoid clashes in the shared directory, plugins are encourraged to organize their files in +/// sub-folders, for example create one subdirectory using the vendor name +/// - don't use symbolic links or hard links which points outside of the directory +/// +/// Resource life-time: +/// - exclusive folder content is managed by the plugin instance +/// - exclusive folder content is deleted when the plugin instance is removed from the project +/// - shared folder content isn't managed by the host, until all plugins using the shared directory +/// are removed from the project +/// +/// Note for the host +/// - try to use the filesytem's copy-on-write feature when possible for reducing exclusive folder +/// space usage on duplication +/// - host can "garbage collect" the files in the shared folder using: +/// clap_plugin_resource_directory.get_files_count() +/// clap_plugin_resource_directory.get_file_path() +/// but be **very** careful before deleting any resources + +typedef struct clap_plugin_resource_directory { + // Sets the directory in which the plugin can save its resources. + // The directory remains valid until it is overriden or the plugin is destroyed. + // If path is null or blank, it clears the directory location. + // path must be absolute. + // [main-thread] + void(CLAP_ABI *set_directory)(const clap_plugin_t *plugin, const char *path, bool is_shared); + + // Asks the plugin to put its resources into the resources directory. + // It is not necessary to collect files which belongs to the plugin's + // factory content unless the param all is true. + // [main-thread] + void(CLAP_ABI *collect)(const clap_plugin_t *plugin, bool all); + + // Returns the number of files used by the plugin in the shared resource folder. + // [main-thread] + uint32_t(CLAP_ABI *get_files_count)(const clap_plugin_t *plugin); + + // Retrieves relative file path to the resources directory. + // @param path writable memory to store the path + // @param path_size number of available bytes in path + // Returns the number of bytes in the path, or -1 on error + // [main-thread] + int32_t(CLAP_ABI *get_file_path)(const clap_plugin_t *plugin, + uint32_t index, + char *path, + uint32_t path_size); +} clap_plugin_resource_directory_t; + +typedef struct clap_host_resource_directory { + // Request the host to setup a resource directory with the specified sharing. + // Returns true if the host will perform the request. + // [main-thread] + bool(CLAP_ABI *request_directory)(const clap_host_t *host, bool is_shared); + + // Tell the host that the resource directory of the specified sharing is no longer required. + // If is_shared = false, then the host may delete the directory content. + // [main-thread] + void(CLAP_ABI *release_directory)(const clap_host_t *host, bool is_shared); +} clap_host_resource_directory_t; + +#ifdef __cplusplus +} +#endif diff --git a/include/clap/ext/draft/track-info.h b/include/clap/ext/draft/track-info.h @@ -4,24 +4,48 @@ #include "../../color.h" #include "../../string-sizes.h" -static CLAP_CONSTEXPR const char CLAP_EXT_TRACK_INFO[] = "clap.track-info.draft/0"; +// This extensions let the plugin query info about the track it's in. +// It is useful when the plugin is created, to initialize some parameters (mix, dry, wet) +// and pick a suitable configuartion regarding audio port type and channel count. + +static CLAP_CONSTEXPR const char CLAP_EXT_TRACK_INFO[] = "clap.track-info.draft/1"; #ifdef __cplusplus extern "C" { #endif +enum { + CLAP_TRACK_INFO_HAS_TRACK_NAME = (1 << 0), + CLAP_TRACK_INFO_HAS_TRACK_COLOR = (1 << 1), + CLAP_TRACK_INFO_HAS_AUDIO_CHANNEL = (1 << 2), + + // This plugin is on a return track, initialize with wet 100% + CLAP_TRACK_INFO_IS_FOR_RETURN_TRACK = (1 << 3), + + // This plugin is on a bus track, initialize with appropriate settings for bus processing + CLAP_TRACK_INFO_IS_FOR_BUS = (1 << 4), + + // This plugin is on the master, initialize with appropriate settings for channel processing + CLAP_TRACK_INFO_IS_FOR_MASTER = (1 << 5), +}; + typedef struct clap_track_info { - clap_id id; - int32_t index; - char name[CLAP_NAME_SIZE]; - char path[CLAP_PATH_SIZE]; // Like "/group1/group2/drum-machine/drum-pad-13" - int32_t channel_count; - const char *audio_port_type; + uint64_t flags; // see the flags above + + // track name, available if flags contain CLAP_TRACK_INFO_HAS_TRACK_NAME + char name[CLAP_NAME_SIZE]; + + // track color, available if flags contain CLAP_TRACK_INFO_HAS_TRACK_COLOR clap_color_t color; - bool is_return_track; + + // availabe if flags contain CLAP_TRACK_INFO_HAS_AUDIO_CHANNEL + // see audio-ports.h, struct clap_audio_port_info to learn how to use channel count and port type + int32_t audio_channel_count; + const char *audio_port_type; } clap_track_info_t; typedef struct clap_plugin_track_info { + // Called when the info changes. // [main-thread] void(CLAP_ABI *changed)(const clap_plugin_t *plugin); } clap_plugin_track_info_t; diff --git a/include/clap/ext/draft/triggers.h b/include/clap/ext/draft/triggers.h @@ -0,0 +1,145 @@ +#pragma once + +#include "../../plugin.h" +#include "../../events.h" +#include "../../string-sizes.h" + +static CLAP_CONSTEXPR const char CLAP_EXT_TRIGGERS[] = "clap.triggers.draft/0"; + +#ifdef __cplusplus +extern "C" { +#endif + +/// @page Trigger events +/// +/// This extension enables the plugin to expose a set of triggers to the host. +/// +/// Some examples for triggers: +/// - trigger an envelope which is independent of the notes +/// - trigger a sample-and-hold unit (maybe even per-voice) + +enum { + // Does this trigger support per note automations? + CLAP_TRIGGER_IS_AUTOMATABLE_PER_NOTE_ID = 1 << 0, + + // Does this trigger support per key automations? + CLAP_TRIGGER_IS_AUTOMATABLE_PER_KEY = 1 << 1, + + // Does this trigger support per channel automations? + CLAP_TRIGGER_IS_AUTOMATABLE_PER_CHANNEL = 1 << 2, + + // Does this trigger support per port automations? + CLAP_TRIGGER_IS_AUTOMATABLE_PER_PORT = 1 << 3, +}; +typedef uint32_t clap_trigger_info_flags; + +// Given that this extension is still draft, it'll use the event-registry and its own event +// namespace until we stabilize it. +// +// #include <clap/ext/event-registry.h> +// +// uint16_t CLAP_EXT_TRIGGER_EVENT_SPACE_ID = UINT16_MAX; +// if (host_event_registry->query(host, CLAP_EXT_TRIGGERS, &CLAP_EXT_TRIGGER_EVENT_SPACE_ID)) { +// /* we can use trigger events */ +// } +// +// /* later on */ +// clap_event_trigger ev; +// ev.header.space_id = CLAP_EXT_TRIGGER_EVENT_SPACE_ID; +// ev.header.type = CLAP_EVENT_TRIGGER; + +enum { CLAP_EVENT_TRIGGER = 0 }; + +typedef struct clap_event_trigger { + clap_event_header_t header; + + // target trigger + clap_id trigger_id; // @ref clap_trigger_info.id + void *cookie; // @ref clap_trigger_info.cookie + + // target a specific note_id, port, key and channel, -1 for global + int32_t note_id; + int16_t port_index; + int16_t channel; + int16_t key; +} clap_event_trigger_t; + +/* This describes a trigger */ +typedef struct clap_trigger_info { + // stable trigger identifier, it must never change. + clap_id id; + + clap_trigger_info_flags flags; + + // in analogy to clap_param_info.cookie + void *cookie; + + // displayable name + char name[CLAP_NAME_SIZE]; + + // the module path containing the trigger, eg:"sequencers/seq1" + // '/' will be used as a separator to show a tree like structure. + char module[CLAP_PATH_SIZE]; +} clap_trigger_info_t; + +typedef struct clap_plugin_triggers { + // Returns the number of triggers. + // [main-thread] + uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin); + + // Copies the trigger's info to trigger_info and returns true on success. + // Returns true on success. + // [main-thread] + bool(CLAP_ABI *get_info)(const clap_plugin_t *plugin, + uint32_t index, + clap_trigger_info_t *trigger_info); +} clap_plugin_triggers_t; + +enum { + // The trigger info did change, use this flag for: + // - name change + // - module change + // New info takes effect immediately. + CLAP_TRIGGER_RESCAN_INFO = 1 << 0, + + // Invalidates everything the host knows about triggers. + // It can only be used while the plugin is deactivated. + // If the plugin is activated use clap_host->restart() and delay any change until the host calls + // clap_plugin->deactivate(). + // + // You must use this flag if: + // - some triggers were added or removed. + // - some triggers had critical changes: + // - is_per_note (flag) + // - is_per_key (flag) + // - is_per_channel (flag) + // - is_per_port (flag) + // - cookie + CLAP_TRIGGER_RESCAN_ALL = 1 << 1, +}; +typedef uint32_t clap_trigger_rescan_flags; + +enum { + // Clears all possible references to a trigger + CLAP_TRIGGER_CLEAR_ALL = 1 << 0, + + // Clears all automations to a trigger + CLAP_TRIGGER_CLEAR_AUTOMATIONS = 1 << 1, +}; +typedef uint32_t clap_trigger_clear_flags; + +typedef struct clap_host_triggers { + // Rescan the full list of triggers according to the flags. + // [main-thread] + void(CLAP_ABI *rescan)(const clap_host_t *host, clap_trigger_rescan_flags flags); + + // Clears references to a trigger. + // [main-thread] + void(CLAP_ABI *clear)(const clap_host_t *host, + clap_id trigger_id, + clap_trigger_clear_flags flags); +} clap_host_triggers_t; + +#ifdef __cplusplus +} +#endif diff --git a/include/clap/ext/params.h b/include/clap/ext/params.h @@ -73,6 +73,41 @@ /// - if a parameter is gone or is created with an id that may have been used before, /// call clap_host_params.clear(host, param_id, CLAP_PARAM_CLEAR_ALL) /// - call clap_host_params->rescan(CLAP_PARAM_RESCAN_ALL) +/// +/// CLAP allows the plugin to change the parameter range, yet the plugin developper +/// should be aware that doing so isn't without risk, especially if you made the +/// promise to never change the sound. If you want to be 100% certain that the +/// sound will not change with all host, then simply never change the range. +/// +/// There are two approaches to automations, either you automate the plain value, +/// or you automate the knob position. The first option will be robust to a range +/// increase, while the second won't be. +/// +/// If the host goes with the second approach (automating the knob position), it means +/// that the plugin is hosted in a relaxed environment regarding sound changes (they are +/// accepted, and not a concern as long as they are reasonable). Though, stepped parameters +/// should be stored as plain value in the document. +/// +/// If the host goes with the first approach, there will still be situation where the +/// sound may innevitably change. For example, if the plugin increase the range, there +/// is an automation playing at the max value and on top of that an LFO is applied. +/// See the following curve: +/// . +/// . . +/// ..... . . +/// before: . . and after: . . +/// +/// Advices 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 +/// +/// Advice for the plugin: +/// - think carefully about your parameter range when designing your DSP +/// - avoid shrinking parameter ranges, they are very likely to change the sound +/// - consider changing the parameter range as a tradeoff: what you improve vs what you break +/// - if you plan to use adapters for other plugin formats, then you need to pay extra +/// attention to the adapter requirements static CLAP_CONSTEXPR const char CLAP_EXT_PARAMS[] = "clap.params"; @@ -107,7 +142,7 @@ enum { // // The host can send live user changes for this parameter regardless of this flag. // - // If this parameters affect the internal processing structure of the plugin, ie: max delay, fft + // If this parameter affects the internal processing structure of the plugin, ie: max delay, fft // size, ... and the plugins needs to re-allocate its working buffers, then it should call // host->request_restart(), and perform the change once the plugin is re-activated. CLAP_PARAM_IS_AUTOMATABLE = 1 << 5, @@ -231,7 +266,7 @@ typedef struct clap_plugin_params { // This method must not be called concurrently to clap_plugin->process(). // // Note: if the plugin is processing, then the process() call will already achieve the - // parameter update (bi-directionnal), so a call to flush isn't required, also be aware + // parameter update (bi-directional), so a call to flush isn't required, also be aware // that the plugin may use the sample offset in process(), while this information would be // lost within flush(). // @@ -268,7 +303,9 @@ enum { // - some parameters were added or removed. // - some parameters had critical changes: // - is_per_note (flag) + // - is_per_key (flag) // - is_per_channel (flag) + // - is_per_port (flag) // - is_readonly (flag) // - is_bypass (flag) // - is_stepped (flag) diff --git a/include/clap/ext/thread-pool.h b/include/clap/ext/thread-pool.h @@ -7,8 +7,8 @@ /// This extension lets the plugin use the host's thread pool. /// /// The plugin must provide @ref clap_plugin_thread_pool, and the host may provide @ref -/// clap_host_thread_pool. If it doesn't, the plugin should process its data by its own means. In the -/// worst case, a single threaded for-loop. +/// clap_host_thread_pool. If it doesn't, the plugin should process its data by its own means. In +/// the worst case, a single threaded for-loop. /// /// Simple example with N voices to process /// diff --git a/include/clap/plugin.h b/include/clap/plugin.h @@ -76,6 +76,8 @@ typedef struct clap_plugin { void(CLAP_ABI *reset)(const struct clap_plugin *plugin); // process audio, events, ... + // All the pointers coming from clap_process_t and its nested attributes, + // are valid until process() returns. // [audio-thread & active_state & processing_state] clap_process_status(CLAP_ABI *process)(const struct clap_plugin *plugin, const clap_process_t *process); diff --git a/include/clap/private/std.h b/include/clap/private/std.h @@ -8,7 +8,7 @@ # include <stdint.h> #endif -#ifdef __cplusplus +#ifdef __cplusplus # include <cstddef> #else # include <stddef.h> diff --git a/include/clap/version.h b/include/clap/version.h @@ -22,8 +22,9 @@ typedef struct clap_version { #define CLAP_VERSION_MAJOR ((uint32_t)1) #define CLAP_VERSION_MINOR ((uint32_t)1) -#define CLAP_VERSION_REVISION ((uint32_t)3) -#define CLAP_VERSION_INIT {CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION} +#define CLAP_VERSION_REVISION ((uint32_t)4) +#define CLAP_VERSION_INIT \ + { CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION } static const CLAP_CONSTEXPR clap_version_t CLAP_VERSION = CLAP_VERSION_INIT; diff --git a/src/plugin-template.c b/src/plugin-template.c @@ -19,11 +19,7 @@ static const clap_plugin_descriptor_t s_my_plug_desc = { .support_url = "https://your-domain.com/support", .version = "1.4.2", .description = "The plugin description.", - .features = (const char *[]){ - CLAP_PLUGIN_FEATURE_INSTRUMENT, - CLAP_PLUGIN_FEATURE_STEREO, - NULL - }, + .features = (const char *[]){CLAP_PLUGIN_FEATURE_INSTRUMENT, CLAP_PLUGIN_FEATURE_STEREO, NULL}, }; typedef struct {