diff --git a/ChangeLog.md b/ChangeLog.md index ccf8fc63..6aa544f5 100644 --- 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 index 52dc263c..0d3a99b9 100644 --- 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 index c1f9d9c3..44747224 100644 --- 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 deleted file mode 100644 index 71ebe81c..00000000 --- a/include/clap/ext/draft/check-for-update.h +++ /dev/null @@ -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 deleted file mode 100644 index c584e9d8..00000000 --- a/include/clap/ext/draft/midi-mappings.h +++ /dev/null @@ -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 index 4ab5f6f4..20248b37 100644 --- 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 index 8d028e1b..b8b698c0 100644 --- 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";