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:
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 {