commit dfcd4b54556e5bbdaefed1882831e50bf37bcc5c
parent e8591df085031ffad1c823107acf415027c2a092
Author: Alexandre Bique <bique.alexandre@gmail.com>
Date: Mon, 8 Jan 2024 12:47:43 +0100
Merge branch 'next' into draft-includes
Diffstat:
7 files changed, 123 insertions(+), 102 deletions(-)
diff --git a/ChangeLog.md b/ChangeLog.md
@@ -14,6 +14,11 @@
Note: we kept the last draft extension ID in order to not break plugins already using it.
+## Removed draft extensions
+
+* `CLAP_EXT_CHECK_FOR_UPDATE` wasn't used and it's design needed more thought.
+* `CLAP_EXT_MIDI_MAPPING` wasn't used. MIDI2 seems to do it better, and the interface wasn't satisfying.
+
## Stabilize factory
* `CLAP_PRESET_DISCOVERY_FACTORY_ID`
diff --git a/README.md b/README.md
@@ -11,7 +11,7 @@
- [Extensions](#extensions)
- [Fundamental extensions](#fundamental-extensions)
- [Support extensions](#support-extensions)
- - [Extra extensions](#extra-extensions)
+ - [Deeper Host integration](#deeper-host-integration)
- [Third-party extensions](#third-party-extensions)
- [Adapters](#adapters)
- [Resources](#resources)
@@ -81,34 +81,45 @@ You can create your own extensions and share them. Make sure that the extension
This is a list of the extensions that you most likely want to implement
and use to get a basic plugin experience:
-- [log](include/clap/ext/log.h), lets the host aggregate plugin logs
-- [thread-check](include/clap/ext/thread-check.h), check which thread you are currently on, useful for correctness validation
-- [audio-ports](include/clap/ext/audio-ports.h), define the audio ports
-- [note-ports](include/clap/ext/note-ports.h), define the note ports
+- [state](include/clap/ext/state.h), save and load the plugin state
+ - [state-context](include/clap/ext/state-context.h), same as state but with additional context info (preset, duplicate, project)
+ - [resource-directory](include/clap/ext/draft/resource-directory.h), host provided folder for the plugin to save extra resource like multi-samples, ... (draft)
- [params](include/clap/ext/params.h), parameters management
-- [latency](include/clap/ext/latency.h), report the plugin latency
+- [note-ports](include/clap/ext/note-ports.h), define the note ports
+- [audio-ports](include/clap/ext/audio-ports.h), define the audio ports
+ - [surround](include/clap/ext/surround.h), inspect surround channel mapping
+ - [ambisonic](include/clap/ext/draft/ambisonic.h), inspect ambisonic channel mapping (draft)
+ - [cv](include/clap/ext/draft/cv.h), inspect CV channel mapping (draft)
+ - [configurable-audio-ports](include/clap/ext/configurable-audio-ports.h), request the plugin to apply a given configuration
+ - [audio-ports-config](include/clap/ext/audio-ports-config.h), simple list of pre-defined audio ports configurations
+ - [audio-ports-activation](include/clap/ext/audio-ports-activation.h), activate and deactivate a given audio port
+ - [extensible-audio-ports](include/clap/ext/draft/extensible-audio-ports.h), let the host add audio ports to the plugin, this is useful for dynamic number of audio inputs (draft)
- [render](include/clap/ext/render.h), renders realtime or offline
+- [latency](include/clap/ext/latency.h), report the plugin latency
- [tail](include/clap/ext/tail.h), processing tail length
-- [state](include/clap/ext/state.h), save and load the plugin state
- [gui](include/clap/ext/gui.h), generic gui controller
+- [voice-info](include/clap/ext/voice-info.h), let the host know how many voices the plugin has, this is important for polyphonic modulations
+- [track-info](include/clap/ext/track-info.h), give some info to the plugin about the track it belongs to
+- [tuning](include/clap/ext/draft/tuning.h), host provided microtuning (draft)
+- [triggers](include/clap/ext/draft/triggers.h), plugin's triggers, similar to parameters but stateless
## Support extensions
+- [thread-check](include/clap/ext/thread-check.h), check which thread you are currently on, useful for correctness validation
- [thread-pool](include/clap/ext/thread-pool.h), use the host thread pool
+- [log](include/clap/ext/log.h), lets the host aggregate plugin logs
- [timer-support](include/clap/ext/timer-support.h), lets the plugin register timer handlers
- [posix-fd-support](include/clap/ext/posix-fd-support.h), lets the plugin register I/O handlers
-## Extra extensions
+## Deeper Host integration
+- [remote-controls](include/clap/ext/remote-controls.h), bank of controls that can be mapped on a controlles with 8 knobs
+- [preset-discovery](include/clap/factory/preset-discovery.h), let the host index the plugin's preset in their native file format
+- [preset-load](include/clap/ext/preset-load.h), let the host ask the plugin to load a preset
+- [param-indication](include/clap/ext/param-indication.h), let the plugin know when a physical control is mapped to a parameter and if there is automation data
- [note-name](include/clap/ext/note-name.h), give a name to notes, useful for drum machines
-- [tuning](include/clap/ext/draft/tuning.h), host provided microtuning
-- [track-info](include/clap/ext/draft/track-info.h)
-- [quick-controls](include/clap/ext/draft/quick-controls.h), bank of controls that can be mapped on a controlles with 8 knobs
-- [file-reference](include/clap/ext/draft/file-reference.h), let the host know about the plugin's file reference, and perform "Collect & Save"
-- [check-for-update](include/clap/ext/draft/check-for-update.h), check if there is a new version of a plugin
-- [audio-ports-config](include/clap/ext/audio-ports-config.h), simple list of possible configurations
-- [surround](include/clap/ext/draft/surround.h), inspect surround channel mapping
-- [ambisonic](include/clap/ext/draft/ambisonic.h), inspect ambisonic channel mapping
+- [transport-control](include/clap/ext/draft/transport-control.h), let the plugin control the host's transport (draft)
+- [context-menu](include/clap/ext/context-menu.h), exchange context menu entries between host and plugin, let the plugin ask the host to popup its own context menu
## Third-party extensions
diff --git a/include/clap/entry.h b/include/clap/entry.h
@@ -31,34 +31,100 @@ extern "C" {
// Each directory should be recursively searched for files and/or bundles as appropriate in your OS
// ending with the extension `.clap`.
//
-// Every method must be thread-safe.
+// init and deinit in most cases are called once, in a matched pair, when the dso is loaded / unloaded.
+// In some rare situations it may be called multiple times in a process, so the functions must be defensive,
+// mutex locking and counting calls if undertaking non trivial non idempotent actions.
+//
+// Rationale:
+//
+// The intent of the init() and deinit() functions is to provide a "normal" initialization patterh
+// which occurs when the shared object is loaded or unloaded. As such, hosts will call each once and
+// in matched pairs. In clap specifications prior to 1.1.11, this single-call was documented as a
+// requirement.
+//
+// We realized, though, that this is not a requirement hosts can meet. If hosts load a plugin
+// which itself wraps another CLAP for instance, while also loading that same clap in its memory
+// space, both the host and the wrapper will call init() and deinit() and have no means to communicate
+// the state.
+//
+// With clap 1.1.11 and beyond we are changing the spec to indicate that a host should make an
+// absolute best effort to call init() and deinit() once, and always in matched pairs (for every
+// init() which returns true, one deinit() should be called).
+//
+// This takes the de-facto burden on plugin writers to deal with multiple calls into a hard requirement.
+//
+// Most init() / deinit() pairs we have seen are the relatively trivial {return true;} and {}. But
+// if your init() function does non-trivial one time work, the plugin author must maintain a counter
+// and must manage a mutex lock. The most obvious implementation will maintain a static counter and a
+// global mutex, increment the counter on each init, decrement it on each deinit, and only undertake
+// the init or deinit action when the counter is zero.
typedef struct clap_plugin_entry {
clap_version_t clap_version; // initialized to CLAP_VERSION
- // This function must be called first, and can only be called once.
+ // Initializes the DSO.
+ //
+ // This function must be called first, before any-other CLAP-related function or symbol from this
+ // DSO.
+ //
+ // It also must only be called once, until a later call to deinit() is made, after which init()
+ // can be called once more to re-initialize the DSO.
+ // This enables hosts to e.g. quickly load and unload a DSO for scanning its plugins, and then
+ // load it again later to actually use the plugins if needed.
+ //
+ // As stated above, even though hosts are forbidden to do so directly, multiple calls before any
+ // deinit() call may still happen. Implementations *should* take this into account, and *must*
+ // do so as of CLAP 1.11.
//
// It should be as fast as possible, in order to perform a very quick scan of the plugin
// descriptors.
//
- // It is forbidden to display graphical user interface in this call.
- // It is forbidden to perform user interaction in this call.
+ // It is forbidden to display graphical user interfaces in this call.
+ // It is forbidden to perform any user interaction in this call.
//
// If the initialization depends upon expensive computation, maybe try to do them ahead of time
// and cache the result.
//
- // If init() returns false, then the host must not call deinit() nor any other clap
- // related symbols from the DSO.
+ // Returns true on success. If init() returns false, then the DSO must be considered
+ // uninitialized, and the host must not call deinit() nor any other CLAP-related symbols from the
+ // DSO.
+ // This function also returns true in the case where the DSO is already initialized, and no
+ // actual initialization work is done in this call, as explain above.
//
// plugin_path is the path to the DSO (Linux, Windows), or the bundle (macOS).
+ //
+ // This function may be called on any thread, including a different one from the one a later call
+ // to deinit() (or a later init()) can be made.
+ // However, it is forbidden to call this function simultaneously from multiple threads.
+ // It is also forbidden to call it simultaneously with *any* other CLAP-related symbols from the
+ // DSO, including (but not limited to) deinit().
bool(CLAP_ABI *init)(const char *plugin_path);
- // No more calls into the DSO must be made after calling deinit().
+ // De-initializes the DSO, freeing any resources allocated or initialized by init().
+ //
+ // After this function is called, no more calls into the DSO must be made, except calling init()
+ // again to re-initialize the DSO.
+ // This means that after deinit() is called, the DSO can be considered to be in the same state
+ // as if init() was never called at all yet, enabling it to be re-initialized as needed.
+ //
+ // As stated above, even though hosts are forbidden to do so directly, multiple calls before any
+ // new init() call may still happen. Implementations *should* take this into account, and *must*
+ // do so as of CLAP 1.11.
+ //
+ // Just like init(), this function may be called on any thread, including a different one from
+ // the one init() was called from, or from the one a later init() call can be made.
+ // However, it is forbidden to call this function simultaneously from multiple threads.
+ // It is also forbidden to call it simultaneously with *any* other CLAP-related symbols from the
+ // DSO, including (but not limited to) deinit().
void(CLAP_ABI *deinit)(void);
// 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.
+ //
+ // Unlike init() and deinit(), this function can be called simultaneously by multiple threads.
+ //
+ // [thread-safe]
const void *(CLAP_ABI *get_factory)(const char *factory_id);
} clap_plugin_entry_t;
diff --git a/include/clap/ext/draft/check-for-update.h b/include/clap/ext/draft/check-for-update.h
@@ -1,32 +0,0 @@
-#pragma once
-
-#include "../../plugin.h"
-
-static CLAP_CONSTEXPR const char CLAP_EXT_CHECK_FOR_UPDATE[] = "clap.check_for_update.draft/0";
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct clap_check_for_update_info {
- const char *version; // latest version
- const char *release_date; // YYYY-MM-DD
- const char *url; // url to a download page which the user can visit
-
- bool is_preview; // true if this version is a preview release
-} clap_check_for_update_info_t;
-
-typedef struct clap_plugin_check_for_update {
- // [main-thread]
- void(CLAP_ABI *check)(const clap_plugin_t *plugin, bool include_preview);
-} clap_plugin_check_for_update_t;
-
-typedef struct clap_host_check_for_update {
- // [main-thread]
- void(CLAP_ABI *on_new_version)(const clap_host_t *host,
- const clap_check_for_update_info_t *update_info);
-} clap_host_check_for_update_t;
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/include/clap/ext/draft/midi-mappings.h b/include/clap/ext/draft/midi-mappings.h
@@ -1,41 +0,0 @@
-#pragma once
-
-#include "../../plugin.h"
-
-static CLAP_CONSTEXPR const char CLAP_EXT_MIDI_MAPPINGS[] = "clap.midi-mappings.draft/0";
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-enum {
- CLAP_MIDI_MAPPING_CC7,
- CLAP_MIDI_MAPPING_CC14,
- CLAP_MIDI_MAPPING_RPN,
- CLAP_MIDI_MAPPING_NRPN,
-};
-typedef int32_t clap_midi_mapping_type;
-
-typedef struct clap_midi_mapping {
- int32_t channel;
- int32_t number;
- clap_id param_id;
-} clap_midi_mapping_t;
-
-typedef struct clap_plugin_midi_mappings {
- // [main-thread]
- uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin);
-
- // Returns true on success and stores the result into mapping.
- // [main-thread]
- bool(CLAP_ABI *get)(const clap_plugin_t *plugin, uint32_t index, clap_midi_mapping_t *mapping);
-} clap_plugin_midi_mappings_t;
-
-typedef struct clap_host_midi_mappings {
- // [main-thread]
- void(CLAP_ABI *changed)(const clap_host_t *host);
-} clap_host_midi_mappings_t;
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/include/clap/ext/state-context.h b/include/clap/ext/state-context.h
@@ -10,7 +10,8 @@
/// on the context.
///
/// Briefly, when loading a preset or duplicating a device, the plugin may want to partially load
-/// the state and initialize certain things differently.
+/// the state and initialize certain things differently, like handling limited resources or fixed
+/// connections to external hardware resources.
///
/// Save and Load operations may have a different context.
/// All three operations should be equivalent:
@@ -20,21 +21,28 @@
/// clap_plugin_state_context.save(CLAP_STATE_CONTEXT_FOR_PRESET),
/// CLAP_STATE_CONTEXT_FOR_PRESET)
///
+/// If in doubt, fallback to clap_plugin_state.
+///
/// If the plugin implements CLAP_EXT_STATE_CONTEXT then it is mandatory to also implement
/// CLAP_EXT_STATE.
+///
+/// It is unspecified which context is equivalent to clap_plugin_state.{save,load}()
#ifdef __cplusplus
extern "C" {
#endif
-static CLAP_CONSTEXPR const char CLAP_EXT_STATE_CONTEXT[] = "clap.state-context.draft/1";
+static CLAP_CONSTEXPR const char CLAP_EXT_STATE_CONTEXT[] = "clap.state-context/2";
enum clap_plugin_state_context_type {
- // suitable for duplicating a plugin instance
- CLAP_STATE_CONTEXT_FOR_DUPLICATE = 1,
+ // suitable for storing and loading a state as a preset
+ CLAP_STATE_CONTEXT_FOR_PRESET = 1,
+
+ // suitable for duplicating a plugin instance
+ CLAP_STATE_CONTEXT_FOR_DUPLICATE = 2,
- // suitable for loading a state as a preset
- CLAP_STATE_CONTEXT_FOR_PRESET = 2,
+ // suitable for storing and loading a state within a project/song
+ CLAP_STATE_CONTEXT_FOR_PROJECT = 3,
};
typedef struct clap_plugin_state_context {
diff --git a/include/clap/ext/state.h b/include/clap/ext/state.h
@@ -10,6 +10,10 @@
/// values and non-parameter state. This is used to persist a plugin's state
/// between project reloads, when duplicating and copying plugin instances, and
/// for host-side preset management.
+///
+/// If you need to know if the save/load operation is meant for duplicating a plugin
+/// instance, for saving/loading a plugin preset or while saving/loading the project
+/// then have a look at clap_plugin_state_context_t.
static CLAP_CONSTEXPR const char CLAP_EXT_STATE[] = "clap.state";