diff --git a/CMakeLists.txt b/CMakeLists.txt index 8773c35f..3581cdde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ add_custom_target(clap-tests) if (${CLAP_BUILD_TESTS}) message(STATUS "Including CLAP tests, compile tests, and versions") + include(CheckIncludeFile) macro(clap_compile_cpp SUFFIX EXT STDC STDCPP) add_executable(clap-compile-${SUFFIX} EXCLUDE_FROM_ALL src/main.${EXT}) @@ -84,11 +85,17 @@ if (${CLAP_BUILD_TESTS}) clap_compile_cpp(cpp17 cc 17 17) clap_compile_cpp(cpp20 cc 17 20) + check_include_file(threads.h CLAP_HAS_THREADS_H) + add_library(clap-plugin-template MODULE EXCLUDE_FROM_ALL src/plugin-template.c) target_link_libraries(clap-plugin-template PRIVATE clap) set_target_properties(clap-plugin-template PROPERTIES C_STANDARD 11) add_dependencies(clap-tests clap-plugin-template) + if(CLAP_HAS_THREADS_H) + target_compile_definitions(clap-plugin-template PRIVATE CLAP_HAS_THREADS_H) + endif() + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(clap-plugin-template PRIVATE -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/linux-my_plug.version) target_link_libraries(clap-plugin-template PRIVATE -Wl,-z,defs) diff --git a/ChangeLog.md b/ChangeLog.md index f3f3a81c..1f5f511b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,77 @@ +# Changes in 1.2.0 + +## New conventions + +* [extension-id](conventions/extension-id.md): introduce some rules about extension ID naming. + +## Stabilize extensions + +* `CLAP_EXT_AMBISONIC` +* `CLAP_EXT_AUDIO_PORTS_ACTIVATION` +* `CLAP_EXT_CONFIGURABLE_AUDIO_PORTS` +* `CLAP_EXT_CONTEXT_MENU` +* `CLAP_EXT_PARAM_INDICATION` +* `CLAP_EXT_PRESET_LOAD` +* `CLAP_EXT_REMOTE_CONTROLS` +* `CLAP_EXT_STATE_CONTEXT` +* `CLAP_EXT_SURROUND` +* `CLAP_EXT_TRACK_INFO` + +### Notes regarding extension ID change after draft stabilization + +We changed the extension ID in the process of stabilization which leads to a **break**. + +To mitigate this transition, we've provided compatibility extension IDs which can be used to match and use the latest draft extensions as they are 100% compatible. + +For example, `CLAP_EXT_CONTEXT_MENU` for the stable ID and `CLAP_EXT_CONTEXT_MENU_COMPAT` for the draft ID. + +As you can see in [extension-id](conventions/extension-id.md), we introduced some rules, so this kind of break won't happen again. + +We may decide to remove the `*_COMPAT` IDs in the future once their usage becomes antiquated. + +## 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. +* `CLAP_EXT_CV` the interface wasn't satisfying. + +## Stabilize factory + +* `CLAP_PRESET_DISCOVERY_FACTORY_ID` + +Note: we kept the last draft factory ID in order to not break plugins already using it. + +## Plugin State Converter + +* Introduction of a new factory which provides a plugin state convertion mechanism. + +## Refactoring + +* `clap_plugin_id_t` was renamed to `clap_universal_plugin_id_t` to make it clear that it can describe more than just a CLAP plugin ID. +* `clap_timestamp_t` was renamed to `clap_timestamp` to be consistent with other types, like e.g. `clap_id`. Also it was moved to a separate header as `CLAP_PRESET_DISCOVERY_FACTORY_ID` was stabilized. + +## Documentation + +* [events.h](include/clap/events.h): Clarify how "Port Channel Key NoteID" matching works +* [events.h](include/clap/events.h): Clarify how `clap_event_note` fields map to MIDI, Host, etc... +* [events.h](include/clap/events.h): Expand clap note expression documentation +* [plugin.h](include/clap/plugin.h): Style cleanup +* [params.h](include/clap/ext/params.h): Fix incorrect function name reference +* [latency.h](include/clap/ext/latency.h): Require the plugin to be activated to get the latency and clarify that the latency can only be fetched when the plugin is activated + +## Plugin Template + +* [plugin-template.c](src/plugin-template.c): implement thread-safe plugin entry init counter + +## Organization + +* `clap.h` no longer includes headers from `ext/draft` or `factory/draft`. Draft extension and factory headers must now be explicitly included, either individually or via the `all.h` header. + +## Other changes + +* [voice-info.h](include/clap/ext/voice-info.h): Make the voice info id `CLAP_CONSTEXPR` like all other ids +* [preset-load.h](include/clap/ext/preset-load.h): Make the preset load id and compat id `CLAP_CONSTEXPR` like all other ids + # Changes in 1.1.10 * [params.h](include/clap/ext/params.h): add `CLAP_PARAM_IS_ENUM` flag. diff --git a/README.md b/README.md index 52dc263c..78c628d6 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) @@ -32,6 +32,7 @@ that is, a plugin binary compiled with CLAP 1.x can be loaded by any other CLAP 1.y. To work with CLAP, include [clap/clap.h](include/clap/clap.h). +To also include the draft extensions, include [clap/all.h](include/clap/all.h). The two most important objects are `clap_host` and `clap_plugin`. @@ -81,34 +82,44 @@ 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 + - [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, meant to be exposed to the user + - [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/conventions/extension-id.md b/conventions/extension-id.md new file mode 100644 index 00000000..4b0b5c11 --- /dev/null +++ b/conventions/extension-id.md @@ -0,0 +1,32 @@ +# Extension ID + +## Naming + +The extension shall be named in the form: `clap.$NAME/$REV`. +Where: +- `$NAME` is the name of the exension. +- `$REV` is the revision of the extension. This is an integer that is incremented for each iteration. It should start at 1. + +For extensions made by third-parties and not officially published by the CLAP project, please use the following form: `$REVERSE_URI.$NAME/$REV`. +Where: +- `$REVERSE_URI` would be something like `com.bitwig`. + +## Draft + +An extension is considered a draft extension if it is in the [draft](../include/clap/ext/draft/) folder. +Make sure to also include it in [all.h](../include/clap/all.h). + +When the extension is migrating from draft to stable, its extension ID must not change. +Move its inclusion from [all.h](../include/clap/all.h) into [clap.h](../include/clap/clap.h). + +All extensions must go though the draft phase first. + +## For factory ID + +Everything about the extension id symmetrically applies to factory id. + +## History + +Before version 1.2.0 when this document was written, existing extensions didn't honor these rules. +We wanted to stabilize some draft extensions without breaking compatibility, yet their extension IDs contained the string `draft`. +While these strings weren't user-facing, we still wanted to remove them, so we updated the extension IDs according to this document and introduced IDs with `_COMPAT` suffixes to provide backward compatibility with the draft versions. diff --git a/include/clap/all.h b/include/clap/all.h new file mode 100644 index 00000000..45412475 --- /dev/null +++ b/include/clap/all.h @@ -0,0 +1,12 @@ +#pragma once + +#include "clap.h" + +#include "factory/draft/plugin-invalidation.h" +#include "factory/draft/plugin-state-converter.h" + +#include "ext/draft/extensible-audio-ports.h" +#include "ext/draft/resource-directory.h" +#include "ext/draft/transport-control.h" +#include "ext/draft/triggers.h" +#include "ext/draft/tuning.h" diff --git a/include/clap/clap.h b/include/clap/clap.h index e57dbf44..6da56764 100644 --- a/include/clap/clap.h +++ b/include/clap/clap.h @@ -28,44 +28,37 @@ #include "entry.h" #include "factory/plugin-factory.h" -#include "factory/draft/plugin-invalidation.h" -#include "factory/draft/preset-discovery.h" +#include "factory/preset-discovery.h" #include "plugin.h" #include "plugin-features.h" #include "host.h" +#include "universal-plugin-id.h" +#include "ext/ambisonic.h" +#include "ext/audio-ports-activation.h" #include "ext/audio-ports-config.h" #include "ext/audio-ports.h" +#include "ext/configurable-audio-ports.h" +#include "ext/context-menu.h" #include "ext/event-registry.h" #include "ext/gui.h" #include "ext/latency.h" #include "ext/log.h" #include "ext/note-name.h" #include "ext/note-ports.h" +#include "ext/param-indication.h" #include "ext/params.h" #include "ext/posix-fd-support.h" +#include "ext/preset-load.h" +#include "ext/remote-controls.h" #include "ext/render.h" +#include "ext/state-context.h" #include "ext/state.h" +#include "ext/surround.h" #include "ext/tail.h" #include "ext/thread-check.h" #include "ext/thread-pool.h" #include "ext/timer-support.h" +#include "ext/track-info.h" #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/midi-mappings.h" -#include "ext/draft/param-indication.h" -#include "ext/draft/preset-load.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" -#include "ext/draft/configurable-audio-ports.h" -#include "ext/draft/extensible-audio-ports.h" diff --git a/include/clap/entry.h b/include/clap/entry.h index c1f9d9c3..abb92f30 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.2.0, 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.2.0 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.2.0. // // 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.2.0. + // + // 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/events.h b/include/clap/events.h index 3a587bec..5086b4df 100644 --- a/include/clap/events.h +++ b/include/clap/events.h @@ -117,19 +117,62 @@ enum { }; // Note on, off, end and choke events. +// +// Clap addresses notes and voices using the 4-value tuple +// (port, channel, key, note_id). Note on/off/end/choke +// events and parameter modulation messages are delivered with +// these values populated. +// +// Values in a note and voice address are either >= 0 if they +// are specified, or -1 to indicate a wildcard. A wildcard +// means a voice with any value in that part of the tuple +// matches the message. +// +// For instance, a (PCKN) of (0, 3, -1, -1) will match all voices +// on channel 3 of port 0. And a PCKN of (-1, 0, 60, -1) will match +// all channel 0 key 60 voices, independent of port or note id. +// +// Especially in the case of note-on note-off pairs, and in the +// absence of voice stacking or polyphonic modulation, a host may +// choose to issue a note id only at note on. So you may see a +// message stream like +// +// CLAP_EVENT_NOTE_ON [0,0,60,184] +// CLAP_EVENT_NOTE_OFF [0,0,60,-1] +// +// and the host will expect the first voice to be released. +// Well constructed plugins will search for voices and notes using +// the entire tuple. +// // In the case of note choke or end events: // - the velocity is ignored. -// - key and channel are used to match active notes, a value of -1 matches all. +// - key and channel are used to match active notes +// - note_id is optionally provided by the host typedef struct clap_event_note { clap_event_header_t header; - int32_t note_id; // -1 if unspecified, otherwise >=0 - int16_t port_index; - int16_t channel; // 0..15 - int16_t key; // 0..127 + int32_t note_id; // host provided note id >= 0, or -1 if unspecified or wildcard + int16_t port_index; // port index from ext/note-ports; -1 for wildcard + int16_t channel; // 0..15, same as MIDI1 Channel Number, -1 for wildcard + int16_t key; // 0..127, same as MIDI1 Key Number (60==Middle C), -1 for wildcard double velocity; // 0..1 } clap_event_note_t; +// Note Expressions are well named modifications of a voice targeted to +// voices using the same wildcard rules described above. Note Expressions are delivered +// as sample accurate events and should be applied at the sample when received. +// +// Note expressions are a statement of value, not cumulative. A PAN event of 0 followed by 1 +// followed by 0.5 would pan hard left, hard right, and center. They are intended as +// an offset from the non-note-expression voice default. A voice which had a volume of +// -20db absent note expressions which received a +4db note expression would move the +// voice to -16db. +// +// A plugin which receives a note expression at the same sample as a NOTE_ON event +// should apply that expression to all generated samples. A plugin which receives +// a note expression after a NOTE_ON event should initiate the voice with default +// values and then apply the note expression when received. A plugin may make a choice +// to smooth note expression streams. enum { // with 0 < x <= 4, plain = 20 * log(x) CLAP_NOTE_EXPRESSION_VOLUME = 0, @@ -137,7 +180,9 @@ enum { // pan, 0 left, 0.5 center, 1 right CLAP_NOTE_EXPRESSION_PAN = 1, - // relative tuning in semitone, from -120 to +120 + // Relative tuning in semitones, from -120 to +120. Semitones are in + // equal temperament and are doubles; the resulting note would be + // retuned by `100 * evt->value` cents. CLAP_NOTE_EXPRESSION_TUNING = 2, // 0..1 @@ -153,7 +198,8 @@ typedef struct clap_event_note_expression { clap_note_expression expression_id; - // target a specific note_id, port, key and channel, -1 for global + // target a specific note_id, port, key and channel, with + // -1 meaning wildcard, per the wildcard discussion above int32_t note_id; int16_t port_index; int16_t channel; @@ -169,7 +215,8 @@ typedef struct clap_event_param_value { clap_id param_id; // @ref clap_param_info.id void *cookie; // @ref clap_param_info.cookie - // target a specific note_id, port, key and channel, -1 for global + // target a specific note_id, port, key and channel, with + // -1 meaning wildcard, per the wildcard discussion above int32_t note_id; int16_t port_index; int16_t channel; @@ -185,7 +232,8 @@ typedef struct clap_event_param_mod { clap_id param_id; // @ref clap_param_info.id void *cookie; // @ref clap_param_info.cookie - // target a specific note_id, port, key and channel, -1 for global + // target a specific note_id, port, key and channel, with + // -1 meaning wildcard, per the wildcard discussion above int32_t note_id; int16_t port_index; int16_t channel; diff --git a/include/clap/ext/draft/ambisonic.h b/include/clap/ext/ambisonic.h similarity index 90% rename from include/clap/ext/draft/ambisonic.h rename to include/clap/ext/ambisonic.h index 556af9a6..c9775171 100644 --- a/include/clap/ext/draft/ambisonic.h +++ b/include/clap/ext/ambisonic.h @@ -1,10 +1,13 @@ #pragma once -#include "../../plugin.h" +#include "../plugin.h" // This extension can be used to specify the channel mapping used by the plugin. +static CLAP_CONSTEXPR const char CLAP_EXT_AMBISONIC[] = "clap.ambisonic/3"; -static CLAP_CONSTEXPR const char CLAP_EXT_AMBISONIC[] = "clap.ambisonic.draft/3"; +// The latest draft is 100% compatible. +// This compat ID may be removed in 2026. +static CLAP_CONSTEXPR const char CLAP_EXT_AMBISONIC_COMPAT[] = "clap.ambisonic.draft/3"; static CLAP_CONSTEXPR const char CLAP_PORT_AMBISONIC[] = "ambisonic"; diff --git a/include/clap/ext/draft/audio-ports-activation.h b/include/clap/ext/audio-ports-activation.h similarity index 91% rename from include/clap/ext/draft/audio-ports-activation.h rename to include/clap/ext/audio-ports-activation.h index f451c005..f63085c5 100644 --- a/include/clap/ext/draft/audio-ports-activation.h +++ b/include/clap/ext/audio-ports-activation.h @@ -1,6 +1,6 @@ #pragma once -#include "../../plugin.h" +#include "../plugin.h" /// @page Audio Ports Activation /// @@ -26,6 +26,11 @@ /// clap_host_audio_ports.rescan(CLAP_AUDIO_PORTS_RESCAN_LIST). static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_ACTIVATION[] = + "clap.audio-ports-activation/2"; + +// The latest draft is 100% compatible. +// This compat ID may be removed in 2026. +static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_ACTIVATION_COMPAT[] = "clap.audio-ports-activation/draft-2"; #ifdef __cplusplus diff --git a/include/clap/ext/audio-ports-config.h b/include/clap/ext/audio-ports-config.h index 4627154c..2ab86573 100644 --- a/include/clap/ext/audio-ports-config.h +++ b/include/clap/ext/audio-ports-config.h @@ -26,7 +26,13 @@ /// extension where all busses can be retrieved in the same way as in the audio-port extension. static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_CONFIG[] = "clap.audio-ports-config"; + static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_CONFIG_INFO[] = + "clap.audio-ports-config-info/1"; + +// The latest draft is 100% compatible. +// This compat ID may be removed in 2026. +static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_CONFIG_INFO_COMPAT[] = "clap.audio-ports-config-info/draft-0"; #ifdef __cplusplus diff --git a/include/clap/ext/audio-ports.h b/include/clap/ext/audio-ports.h index 74b94c32..39880493 100644 --- a/include/clap/ext/audio-ports.h +++ b/include/clap/ext/audio-ports.h @@ -54,7 +54,6 @@ typedef struct clap_audio_port_info { // - CLAP_PORT_STEREO // - CLAP_PORT_SURROUND (defined in the surround extension) // - CLAP_PORT_AMBISONIC (defined in the ambisonic extension) - // - CLAP_PORT_CV (defined in the cv extension) // // An extension can provide its own port type and way to inspect the channels. const char *port_type; diff --git a/include/clap/ext/draft/configurable-audio-ports.h b/include/clap/ext/configurable-audio-ports.h similarity index 90% rename from include/clap/ext/draft/configurable-audio-ports.h rename to include/clap/ext/configurable-audio-ports.h index 4fe60cd1..86688e71 100644 --- a/include/clap/ext/draft/configurable-audio-ports.h +++ b/include/clap/ext/configurable-audio-ports.h @@ -1,6 +1,6 @@ #pragma once -#include "../audio-ports.h" +#include "audio-ports.h" #ifdef __cplusplus extern "C" { @@ -8,7 +8,13 @@ extern "C" { // This extension lets the host configure the plugin's input and output audio ports. // This is a "push" approach to audio ports configuration. + static CLAP_CONSTEXPR const char CLAP_EXT_CONFIGURABLE_AUDIO_PORTS[] = + "clap.configurable-audio-ports/1"; + +// The latest draft is 100% compatible. +// This compat ID may be removed in 2026. +static CLAP_CONSTEXPR const char CLAP_EXT_CONFIGURABLE_AUDIO_PORTS_COMPAT[] = "clap.configurable-audio-ports.draft1"; typedef struct clap_audio_port_configuration_request { diff --git a/include/clap/ext/draft/context-menu.h b/include/clap/ext/context-menu.h similarity index 96% rename from include/clap/ext/draft/context-menu.h rename to include/clap/ext/context-menu.h index 85c3abbc..293f59a8 100644 --- a/include/clap/ext/draft/context-menu.h +++ b/include/clap/ext/context-menu.h @@ -1,11 +1,15 @@ #pragma once -#include "../../plugin.h" +#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"; +static CLAP_CONSTEXPR const char CLAP_EXT_CONTEXT_MENU[] = "clap.context-menu/1"; + +// The latest draft is 100% compatible. +// This compat ID may be removed in 2026. +static CLAP_CONSTEXPR const char CLAP_EXT_CONTEXT_MENU_COMPAT[] = "clap.context-menu.draft/0"; #ifdef __cplusplus extern "C" { @@ -15,7 +19,6 @@ extern "C" { 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 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/cv.h b/include/clap/ext/draft/cv.h deleted file mode 100644 index 343c836c..00000000 --- a/include/clap/ext/draft/cv.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "../../plugin.h" - -// This extension can be used to specify the cv channel type used by the plugin. -// Work in progress, suggestions are welcome - -static CLAP_CONSTEXPR const char CLAP_EXT_CV[] = "clap.cv.draft/0"; -static CLAP_CONSTEXPR const char CLAP_PORT_CV[] = "cv"; - -#ifdef __cplusplus -extern "C" { -#endif - -enum { - // TODO: standardize values? - CLAP_CV_VALUE = 0, - CLAP_CV_GATE = 1, - CLAP_CV_PITCH = 2, -}; - -// TODO: maybe we want a channel_info instead, where we could have more details about the supported -// ranges? - -typedef struct clap_plugin_cv { - // Returns true on success. - // [main-thread] - bool(CLAP_ABI *get_channel_type)(const clap_plugin_t *plugin, - bool is_input, - uint32_t port_index, - uint32_t channel_index, - uint32_t *channel_type); -} clap_plugin_cv_t; - -typedef struct clap_host_cv { - // Informs the host that the channels type have changed. - // The channels type can only change when the plugin is de-activated. - // [main-thread,!active] - void(CLAP_ABI *changed)(const clap_host_t *host); -} clap_host_cv_t; - -#ifdef __cplusplus -} -#endif diff --git a/include/clap/ext/draft/extensible-audio-ports.h b/include/clap/ext/draft/extensible-audio-ports.h index 278594b5..8f97b0c9 100644 --- a/include/clap/ext/draft/extensible-audio-ports.h +++ b/include/clap/ext/draft/extensible-audio-ports.h @@ -8,7 +8,7 @@ extern "C" { // This extension lets the host add and remove audio ports to the plugin. static CLAP_CONSTEXPR const char CLAP_EXT_EXTENSIBLE_AUDIO_PORTS[] = - "clap.extensible-audio-ports.draft0"; + "clap.extensible-audio-ports/1"; typedef struct clap_plugin_extensible_audio_ports { // Asks the plugin to add a new port (at the end of the list), with the following settings. 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/draft/resource-directory.h b/include/clap/ext/draft/resource-directory.h index fc65eaa8..6b8bd57e 100644 --- a/include/clap/ext/draft/resource-directory.h +++ b/include/clap/ext/draft/resource-directory.h @@ -2,7 +2,7 @@ #include "../../plugin.h" -static CLAP_CONSTEXPR const char CLAP_EXT_RESOURCE_DIRECTORY[] = "clap.resource-directory.draft/0"; +static CLAP_CONSTEXPR const char CLAP_EXT_RESOURCE_DIRECTORY[] = "clap.resource-directory/1"; #ifdef __cplusplus extern "C" { diff --git a/include/clap/ext/draft/transport-control.h b/include/clap/ext/draft/transport-control.h index 8a7e7b51..8085ceea 100644 --- a/include/clap/ext/draft/transport-control.h +++ b/include/clap/ext/draft/transport-control.h @@ -6,7 +6,7 @@ // The host has no obligation to execute these requests, so the interface may be // partially working. -static CLAP_CONSTEXPR const char CLAP_EXT_TRANSPORT_CONTROL[] = "clap.transport-control.draft/0"; +static CLAP_CONSTEXPR const char CLAP_EXT_TRANSPORT_CONTROL[] = "clap.transport-control/1"; #ifdef __cplusplus extern "C" { diff --git a/include/clap/ext/draft/triggers.h b/include/clap/ext/draft/triggers.h index fa1e83a9..bf85dee5 100644 --- a/include/clap/ext/draft/triggers.h +++ b/include/clap/ext/draft/triggers.h @@ -4,7 +4,7 @@ #include "../../events.h" #include "../../string-sizes.h" -static CLAP_CONSTEXPR const char CLAP_EXT_TRIGGERS[] = "clap.triggers.draft/0"; +static CLAP_CONSTEXPR const char CLAP_EXT_TRIGGERS[] = "clap.triggers/1"; #ifdef __cplusplus extern "C" { diff --git a/include/clap/ext/draft/tuning.h b/include/clap/ext/draft/tuning.h index db8d9764..ae668b13 100644 --- a/include/clap/ext/draft/tuning.h +++ b/include/clap/ext/draft/tuning.h @@ -4,7 +4,7 @@ #include "../../events.h" #include "../../string-sizes.h" -static CLAP_CONSTEXPR const char CLAP_EXT_TUNING[] = "clap.tuning.draft/2"; +static CLAP_CONSTEXPR const char CLAP_EXT_TUNING[] = "clap.tuning/2"; #ifdef __cplusplus extern "C" { diff --git a/include/clap/ext/latency.h b/include/clap/ext/latency.h index 862b3341..6c9df1b1 100644 --- a/include/clap/ext/latency.h +++ b/include/clap/ext/latency.h @@ -8,10 +8,9 @@ static CLAP_CONSTEXPR const char CLAP_EXT_LATENCY[] = "clap.latency"; extern "C" { #endif -// The audio ports scan has to be done while the plugin is deactivated. typedef struct clap_plugin_latency { // Returns the plugin latency in samples. - // [main-thread] + // [main-thread & active] uint32_t(CLAP_ABI *get)(const clap_plugin_t *plugin); } clap_plugin_latency_t; diff --git a/include/clap/ext/draft/param-indication.h b/include/clap/ext/param-indication.h similarity index 91% rename from include/clap/ext/draft/param-indication.h rename to include/clap/ext/param-indication.h index 5ec45f75..643a9534 100644 --- a/include/clap/ext/draft/param-indication.h +++ b/include/clap/ext/param-indication.h @@ -1,7 +1,7 @@ #pragma once -#include "../params.h" -#include "../../color.h" +#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: @@ -13,7 +13,11 @@ // 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"; +static CLAP_CONSTEXPR const char CLAP_EXT_PARAM_INDICATION[] = "clap.param-indication/4"; + +// The latest draft is 100% compatible. +// This compat ID may be removed in 2026. +static CLAP_CONSTEXPR const char CLAP_EXT_PARAM_INDICATION_COMPAT[] = "clap.param-indication.draft/4"; #ifdef __cplusplus extern "C" { diff --git a/include/clap/ext/params.h b/include/clap/ext/params.h index 2ec7e025..33602634 100644 --- a/include/clap/ext/params.h +++ b/include/clap/ext/params.h @@ -12,7 +12,7 @@ /// The plugin is responsible for keeping its audio processor and its GUI in sync. /// /// The host can at any time read parameters' value on the [main-thread] using -/// @ref clap_plugin_params.value(). +/// @ref clap_plugin_params.get_value(). /// /// There are two options to communicate parameter value changes, and they are not concurrent. /// - send automation points during clap_plugin.process() diff --git a/include/clap/ext/draft/preset-load.h b/include/clap/ext/preset-load.h similarity index 86% rename from include/clap/ext/draft/preset-load.h rename to include/clap/ext/preset-load.h index 92263f11..1e4122e2 100644 --- a/include/clap/ext/draft/preset-load.h +++ b/include/clap/ext/preset-load.h @@ -1,8 +1,12 @@ #pragma once -#include "../../plugin.h" +#include "../plugin.h" -static const char CLAP_EXT_PRESET_LOAD[] = "clap.preset-load.draft/2"; +static CLAP_CONSTEXPR const char CLAP_EXT_PRESET_LOAD[] = "clap.preset-load/2"; + +// The latest draft is 100% compatible. +// This compat ID may be removed in 2026. +static CLAP_CONSTEXPR const char CLAP_EXT_PRESET_LOAD_COMPAT[] = "clap.preset-load.draft/2"; #ifdef __cplusplus extern "C" { diff --git a/include/clap/ext/draft/remote-controls.h b/include/clap/ext/remote-controls.h similarity index 90% rename from include/clap/ext/draft/remote-controls.h rename to include/clap/ext/remote-controls.h index 07566d0a..d7bf4fc5 100644 --- a/include/clap/ext/draft/remote-controls.h +++ b/include/clap/ext/remote-controls.h @@ -1,7 +1,7 @@ #pragma once -#include "../../plugin.h" -#include "../../string-sizes.h" +#include "../plugin.h" +#include "../string-sizes.h" // This extension let the plugin provide a structured way of mapping parameters to an hardware // controller. @@ -31,7 +31,11 @@ // 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/2"; +static CLAP_CONSTEXPR const char CLAP_EXT_REMOTE_CONTROLS[] = "clap.remote-controls/2"; + +// The latest draft is 100% compatible +// This compat ID may be removed in 2026. +static CLAP_CONSTEXPR const char CLAP_EXT_REMOTE_CONTROLS_COMPAT[] = "clap.remote-controls.draft/2"; #ifdef __cplusplus extern "C" { diff --git a/include/clap/ext/draft/state-context.h b/include/clap/ext/state-context.h similarity index 75% rename from include/clap/ext/draft/state-context.h rename to include/clap/ext/state-context.h index 4912a6e0..20248b37 100644 --- a/include/clap/ext/draft/state-context.h +++ b/include/clap/ext/state-context.h @@ -1,7 +1,7 @@ #pragma once -#include "../../plugin.h" -#include "../../stream.h" +#include "../plugin.h" +#include "../stream.h" /// @page state-context extension /// @brief extended state handling @@ -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..540246b3 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 consider implementing CLAP_EXT_STATE_CONTEXT in addition to CLAP_EXT_STATE. static CLAP_CONSTEXPR const char CLAP_EXT_STATE[] = "clap.state"; diff --git a/include/clap/ext/draft/surround.h b/include/clap/ext/surround.h similarity index 94% rename from include/clap/ext/draft/surround.h rename to include/clap/ext/surround.h index 82f4e888..7e57c051 100644 --- a/include/clap/ext/draft/surround.h +++ b/include/clap/ext/surround.h @@ -1,6 +1,6 @@ #pragma once -#include "../../plugin.h" +#include "../plugin.h" // This extension can be used to specify the channel mapping used by the plugin. // @@ -24,7 +24,11 @@ // 3. host calls clap_plugin_surround->get_channel_map() // 4. host activates the plugin and can start processing audio -static CLAP_CONSTEXPR const char CLAP_EXT_SURROUND[] = "clap.surround.draft/4"; +static CLAP_CONSTEXPR const char CLAP_EXT_SURROUND[] = "clap.surround/4"; + +// The latest draft is 100% compatible. +// This compat ID may be removed in 2026. +static CLAP_CONSTEXPR const char CLAP_EXT_SURROUND_COMPAT[] = "clap.surround.draft/4"; static CLAP_CONSTEXPR const char CLAP_PORT_SURROUND[] = "surround"; diff --git a/include/clap/ext/draft/track-info.h b/include/clap/ext/track-info.h similarity index 88% rename from include/clap/ext/draft/track-info.h rename to include/clap/ext/track-info.h index a79e67c3..3e1a5559 100644 --- a/include/clap/ext/draft/track-info.h +++ b/include/clap/ext/track-info.h @@ -1,14 +1,18 @@ #pragma once -#include "../../plugin.h" -#include "../../color.h" -#include "../../string-sizes.h" +#include "../plugin.h" +#include "../color.h" +#include "../string-sizes.h" // This extension 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 configuration regarding audio port type and channel count. -static CLAP_CONSTEXPR const char CLAP_EXT_TRACK_INFO[] = "clap.track-info.draft/1"; +static CLAP_CONSTEXPR const char CLAP_EXT_TRACK_INFO[] = "clap.track-info/1"; + +// The latest draft is 100% compatible. +// This compat ID may be removed in 2026. +static CLAP_CONSTEXPR const char CLAP_EXT_TRACK_INFO_COMPAT[] = "clap.track-info.draft/1"; #ifdef __cplusplus extern "C" { diff --git a/include/clap/ext/voice-info.h b/include/clap/ext/voice-info.h index 344ed8ab..038baecb 100644 --- a/include/clap/ext/voice-info.h +++ b/include/clap/ext/voice-info.h @@ -9,7 +9,7 @@ // - make the host's voice pool coherent with what the plugin has // - turn the host's voice management to mono when the plugin is mono -static const char CLAP_EXT_VOICE_INFO[] = "clap.voice-info"; +static CLAP_CONSTEXPR const char CLAP_EXT_VOICE_INFO[] = "clap.voice-info"; #ifdef __cplusplus extern "C" { diff --git a/include/clap/factory/draft/plugin-invalidation.h b/include/clap/factory/draft/plugin-invalidation.h index ea698e55..7f65dd04 100644 --- a/include/clap/factory/draft/plugin-invalidation.h +++ b/include/clap/factory/draft/plugin-invalidation.h @@ -6,7 +6,7 @@ // Use it to retrieve const clap_plugin_invalidation_factory_t* from // clap_plugin_entry.get_factory() static const CLAP_CONSTEXPR char CLAP_PLUGIN_INVALIDATION_FACTORY_ID[] = - "clap.plugin-invalidation-factory/draft0"; + "clap.plugin-invalidation-factory/1"; #ifdef __cplusplus extern "C" { diff --git a/include/clap/factory/draft/plugin-state-converter.h b/include/clap/factory/draft/plugin-state-converter.h new file mode 100644 index 00000000..cfc9ad1d --- /dev/null +++ b/include/clap/factory/draft/plugin-state-converter.h @@ -0,0 +1,99 @@ +#pragma once + +#include "../../id.h" +#include "../../universal-plugin-id.h" +#include "../../stream.h" +#include "../../version.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct clap_plugin_state_converter_descriptor { + clap_version_t clap_version; + + clap_universal_plugin_id_t src_plugin_id; + clap_universal_plugin_id_t dst_plugin_id; + + const char *id; // eg: "com.u-he.diva-converter", mandatory + const char *name; // eg: "Diva Converter", mandatory + const char *vendor; // eg: "u-he" + const char *version; // eg: 1.1.5 + const char *description; // eg: "Official state converter for u-he Diva." +} clap_plugin_state_converter_descriptor_t; + +// This interface provides a mechanism for the host to convert a plugin state and its automation +// points to a new plugin. +// +// This is useful to convert from one plugin ABI to another one. +// This is also useful to offer an upgrade path: from EQ version 1 to EQ version 2. +// This can also be used to convert the state of a plugin that isn't maintained anymore into +// another plugin that would be similar. +typedef struct clap_plugin_state_converter { + const clap_plugin_state_converter_descriptor_t *desc; + + void *converter_data; + + // Destroy the converter. + void (*destroy)(struct clap_plugin_state_converter *converter); + + // Converts the input state to a state usable by the destination plugin. + // + // error_buffer is a place holder of error_buffer_size bytes for storing a null-terminated + // error message in case of failure, which can be displayed to the user. + // + // Returns true on success. + // [thread-safe] + bool (*convert_state)(struct clap_plugin_state_converter *converter, + const clap_istream_t *src, + const clap_ostream_t *dst, + char *error_buffer, + size_t error_buffer_size); + + // Converts a normalized value. + // Returns true on success. + // [thread-safe] + bool (*convert_normalized_value)(struct clap_plugin_state_converter *converter, + clap_id src_param_id, + double src_normalized_value, + clap_id *dst_param_id, + double *dst_normalized_value); + + // Converts a plain value. + // Returns true on success. + // [thread-safe] + bool (*convert_plain_value)(struct clap_plugin_state_converter *converter, + clap_id src_param_id, + double src_plain_value, + clap_id *dst_param_id, + double *dst_plain_value); +} clap_plugin_state_converter_t; + +// Factory identifier +static CLAP_CONSTEXPR const char CLAP_PLUGIN_STATE_CONVERTER_FACTORY_ID[] = + "clap.plugin-state-converter-factory/1"; + +// List all the plugin state converters available in the current DSO. +typedef struct clap_plugin_state_converter_factory { + // Get the number of converters. + // [thread-safe] + uint32_t (*count)(const struct clap_plugin_state_converter_factory *factory); + + // Retrieves a plugin state converter descriptor by its index. + // Returns null in case of error. + // The descriptor must not be freed. + // [thread-safe] + const clap_plugin_state_converter_descriptor_t *(*get_descriptor)( + const struct clap_plugin_state_converter_factory *factory, uint32_t index); + + // Create a plugin state converter by its converter_id. + // The returned pointer must be freed by calling converter->destroy(converter); + // Returns null in case of error. + // [thread-safe] + clap_plugin_state_converter_t *(*create)( + const struct clap_plugin_state_converter_factory *factory, const char *converter_id); +} clap_plugin_state_converter_factory_t; + +#ifdef __cplusplus +} +#endif diff --git a/include/clap/factory/draft/preset-discovery.h b/include/clap/factory/preset-discovery.h similarity index 89% rename from include/clap/factory/draft/preset-discovery.h rename to include/clap/factory/preset-discovery.h index 05223d9d..fbb0244a 100644 --- a/include/clap/factory/draft/preset-discovery.h +++ b/include/clap/factory/preset-discovery.h @@ -41,13 +41,20 @@ #pragma once -#include "../../private/std.h" -#include "../../private/macros.h" -#include "../../version.h" +#include "../private/std.h" +#include "../private/macros.h" +#include "../timestamp.h" +#include "../version.h" +#include "../universal-plugin-id.h" // Use it to retrieve const clap_preset_discovery_factory_t* from // clap_plugin_entry.get_factory() static const CLAP_CONSTEXPR char CLAP_PRESET_DISCOVERY_FACTORY_ID[] = + "clap.preset-discovery-factory/2"; + +// The latest draft is 100% compatible. +// This compat ID may be removed in 2026. +static const CLAP_CONSTEXPR char CLAP_PRESET_DISCOVERY_FACTORY_ID_COMPAT[] = "clap.preset-discovery-factory/draft-2"; #ifdef __cplusplus @@ -83,27 +90,6 @@ enum clap_preset_discovery_flags { CLAP_PRESET_DISCOVERY_IS_FAVORITE = 1 << 3, }; -// TODO: move clap_timestamp_t, CLAP_TIMESTAMP_UNKNOWN and clap_plugin_id_t to parent files once we -// settle with preset discovery - -// This type defines a timestamp: the number of seconds since UNIX EPOCH. -// See C's time_t time(time_t *). -typedef uint64_t clap_timestamp_t; - -// Value for unknown timestamp. -static const clap_timestamp_t CLAP_TIMESTAMP_UNKNOWN = 0; - -// Pair of plugin ABI and plugin identifier -typedef struct clap_plugin_id { - // The plugin ABI name, in lowercase. - // eg: "clap" - const char *abi; - - // The plugin ID, for example "com.u-he.Diva". - // If the ABI rely upon binary plugin ids, then they shall be hex encoded (lower case). - const char *id; -} clap_plugin_id_t; - // Receiver that receives the metadata for a single preset file. // The host would define the various callbacks in this interface and the preset parser function // would then call them. @@ -138,7 +124,7 @@ typedef struct clap_preset_discovery_metadata_receiver { // Adds a plug-in id that this preset can be used with. void(CLAP_ABI *add_plugin_id)(const struct clap_preset_discovery_metadata_receiver *receiver, - const clap_plugin_id_t *plugin_id); + const clap_universal_plugin_id_t *plugin_id); // Sets the sound pack to which the preset belongs to. void(CLAP_ABI *set_soundpack_id)(const struct clap_preset_discovery_metadata_receiver *receiver, @@ -162,8 +148,8 @@ typedef struct clap_preset_discovery_metadata_receiver { // If this function is not called, then the indexer may look at the file's creation and // modification time. void(CLAP_ABI *set_timestamps)(const struct clap_preset_discovery_metadata_receiver *receiver, - clap_timestamp_t creation_time, - clap_timestamp_t modification_time); + clap_timestamp creation_time, + clap_timestamp modification_time); // Adds a feature to the preset. // @@ -209,14 +195,14 @@ typedef struct clap_preset_discovery_location { // Describes an installed sound pack. typedef struct clap_preset_discovery_soundpack { - uint32_t flags; // see enum clap_preset_discovery_flags - const char *id; // sound pack identifier - const char *name; // name of this sound pack - const char *description; // optional, reasonably short description of the sound pack - const char *homepage_url; // optional, url to the pack's homepage - const char *vendor; // optional, sound pack's vendor - const char *image_path; // optional, an image on disk - clap_timestamp_t release_timestamp; // release date, CLAP_TIMESTAMP_UNKNOWN if unavailable + uint32_t flags; // see enum clap_preset_discovery_flags + const char *id; // sound pack identifier + const char *name; // name of this sound pack + const char *description; // optional, reasonably short description of the sound pack + const char *homepage_url; // optional, url to the pack's homepage + const char *vendor; // optional, sound pack's vendor + const char *image_path; // optional, an image on disk + clap_timestamp release_timestamp; // release date, CLAP_TIMESTAMP_UNKNOWN if unavailable } clap_preset_discovery_soundpack_t; // Describes a preset provider diff --git a/include/clap/plugin.h b/include/clap/plugin.h index 97ead816..7e83074b 100644 --- a/include/clap/plugin.h +++ b/include/clap/plugin.h @@ -46,6 +46,9 @@ typedef struct clap_plugin { // Must be called after creating the plugin. // If init returns false, the host must destroy the plugin instance. // If init returns true, then the plugin is initialized and in the deactivated state. + // Unlike in `plugin-factory::create_plugin`, in init you have complete access to the host + // and host extensions, so clap related setup activities should be done here rather than in + // create_plugin. // [main-thread] bool(CLAP_ABI *init)(const struct clap_plugin *plugin); @@ -60,21 +63,21 @@ typedef struct clap_plugin { // the [min, max] range, which is bounded by [1, INT32_MAX]. // Once activated the latency and port configuration must remain constant, until deactivation. // Returns true on success. - // [main-thread & !active_state] + // [main-thread & !active] bool(CLAP_ABI *activate)(const struct clap_plugin *plugin, double sample_rate, uint32_t min_frames_count, uint32_t max_frames_count); - // [main-thread & active_state] + // [main-thread & active] void(CLAP_ABI *deactivate)(const struct clap_plugin *plugin); // Call start processing before processing. // Returns true on success. - // [audio-thread & active_state & !processing_state] + // [audio-thread & active & !processing] bool(CLAP_ABI *start_processing)(const struct clap_plugin *plugin); // Call stop processing before sending the plugin to sleep. - // [audio-thread & active_state & processing_state] + // [audio-thread & active & processing] void(CLAP_ABI *stop_processing)(const struct clap_plugin *plugin); // - Clears all buffers, performs a full reset of the processing state (filters, oscillators, @@ -82,13 +85,13 @@ typedef struct clap_plugin { // - The parameter's value remain unchanged. // - clap_process.steady_time may jump backward. // - // [audio-thread & active_state] + // [audio-thread & active] 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] + // [audio-thread & active & processing] clap_process_status(CLAP_ABI *process)(const struct clap_plugin *plugin, const clap_process_t *process); diff --git a/include/clap/timestamp.h b/include/clap/timestamp.h new file mode 100644 index 00000000..63d2caba --- /dev/null +++ b/include/clap/timestamp.h @@ -0,0 +1,11 @@ +#pragma once + +#include "private/std.h" +#include "private/macros.h" + +// This type defines a timestamp: the number of seconds since UNIX EPOCH. +// See C's time_t time(time_t *). +typedef uint64_t clap_timestamp; + +// Value for unknown timestamp. +static const CLAP_CONSTEXPR clap_timestamp CLAP_TIMESTAMP_UNKNOWN = 0; diff --git a/include/clap/universal-plugin-id.h b/include/clap/universal-plugin-id.h new file mode 100644 index 00000000..b039d035 --- /dev/null +++ b/include/clap/universal-plugin-id.h @@ -0,0 +1,26 @@ +#pragma once + +// Pair of plugin ABI and plugin identifier. +// +// If you want to represent other formats please send us an update to the comment with the +// name of the abi and the representation of the id. +typedef struct clap_universal_plugin_id { + // The plugin ABI name, in lowercase and null-terminated. + // eg: "clap", "vst3", "vst2", "au", ... + const char *abi; + + // The plugin ID, null-terminated and formatted as follows: + // + // CLAP: use the plugin id + // eg: "com.u-he.diva" + // + // AU: format the string like "type:subt:manu" + // eg: "aumu:SgXT:VmbA" + // + // VST2: print the id as a signed 32-bits integer + // eg: "-4382976" + // + // VST3: print the id as a standard UUID + // eg: "123e4567-e89b-12d3-a456-426614174000" + const char *id; +} clap_universal_plugin_id_t; diff --git a/include/clap/version.h b/include/clap/version.h index 33072a2d..6a3e782c 100644 --- a/include/clap/version.h +++ b/include/clap/version.h @@ -21,8 +21,8 @@ typedef struct clap_version { #endif #define CLAP_VERSION_MAJOR 1 -#define CLAP_VERSION_MINOR 1 -#define CLAP_VERSION_REVISION 10 +#define CLAP_VERSION_MINOR 2 +#define CLAP_VERSION_REVISION 0 #define CLAP_VERSION_INIT \ { (uint32_t)CLAP_VERSION_MAJOR, (uint32_t)CLAP_VERSION_MINOR, (uint32_t)CLAP_VERSION_REVISION } diff --git a/src/main.c b/src/main.c index c1d6f593..eaeb5251 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,4 @@ -#include +#include // The purpose of this file is to check that all headers compile int main(int argc, char **argv) { diff --git a/src/main.cc b/src/main.cc index 80febfba..fcf64a69 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,4 +1,4 @@ -#include +#include // The purpose of this file is to check that all headers compile diff --git a/src/plugin-template.c b/src/plugin-template.c index 97f4b1ff..8436815c 100644 --- a/src/plugin-template.c +++ b/src/plugin-template.c @@ -6,6 +6,12 @@ #include #include #include +#include + +#if __STDC_VERSION__ >= 201112L && !defined (__STDC_NO_THREADS__) && defined (CLAP_HAS_THREADS_H) +# define CLAP_HAS_THREAD +# include +#endif #include @@ -357,15 +363,82 @@ static const clap_plugin_factory_t s_plugin_factory = { //////////////// static bool entry_init(const char *plugin_path) { - // called only once, and very first + // perform the plugin initialization return true; } static void entry_deinit(void) { - // called before unloading the DSO + // perform the plugin de-initialization +} + +#ifdef CLAP_HAS_THREAD +static mtx_t g_entry_lock; +static once_flag g_entry_once = ONCE_FLAG_INIT; +#endif + +static int g_entry_init_counter = 0; + +#ifdef CLAP_HAS_THREAD +// Initializes the necessary mutex for the entry guard +static void entry_init_guard_init(void) { + mtx_init(&g_entry_lock, mtx_plain); +} +#endif + +// Thread safe init counter +static bool entry_init_guard(const char *plugin_path) { +#ifdef CLAP_HAS_THREAD + call_once(&g_entry_once, entry_init_guard_init); + + mtx_lock(&g_entry_lock); +#endif + + const int cnt = ++g_entry_init_counter; + assert(cnt > 0); + + bool succeed = true; + if (cnt == 1) { + succeed = entry_init(plugin_path); + if (!succeed) + g_entry_init_counter = 0; + } + +#ifdef CLAP_HAS_THREAD + mtx_unlock(&g_entry_lock); +#endif + + return succeed; +} + +// Thread safe deinit counter +static void entry_deinit_guard(void) { +#ifdef CLAP_HAS_THREAD + call_once(&g_entry_once, entry_init_guard_init); + + mtx_lock(&g_entry_lock); +#endif + + const int cnt = --g_entry_init_counter; + assert(cnt > 0); + + bool succeed = true; + if (cnt == 0) + entry_deinit(); + +#ifdef CLAP_HAS_THREAD + mtx_unlock(&g_entry_lock); +#endif } static const void *entry_get_factory(const char *factory_id) { +#ifdef CLAP_HAS_THREAD + call_once(&g_entry_once, entry_init_guard_init); +#endif + + assert(g_entry_init_counter > 0); + if (g_entry_init_counter <= 0) + return NULL; + if (!strcmp(factory_id, CLAP_PLUGIN_FACTORY_ID)) return &s_plugin_factory; return NULL; @@ -374,7 +447,7 @@ static const void *entry_get_factory(const char *factory_id) { // This symbol will be resolved by the host CLAP_EXPORT const clap_plugin_entry_t clap_entry = { .clap_version = CLAP_VERSION_INIT, - .init = entry_init, - .deinit = entry_deinit, + .init = entry_init_guard, + .deinit = entry_deinit_guard, .get_factory = entry_get_factory, };