From 757a92752f6371fbb05d7195bad483175a7e5071 Mon Sep 17 00:00:00 2001 From: Nature Is Frequency Date: Tue, 6 Feb 2024 14:44:21 +0000 Subject: [PATCH 01/19] Fix: for issue: #390 Add .DS_Store to .gitignore Add .DS_Store to .gitignore (MacOS specific) Fixes: https://github.com/free-audio/clap/issues/390 --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index dc8faa65..ba473737 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,5 @@ cmake-build*/ # A place to store stuff and get it git ignored ignore/* + +.DS_Store From ebfe2623270cb237901f90e634824f016f24b6bb Mon Sep 17 00:00:00 2001 From: Paul Walker Date: Wed, 17 Jul 2024 09:24:40 -0400 Subject: [PATCH 02/19] Expand the thread-doc to clarify and expand realtime Summarizing the conversation in discussion #414, this PR adds a more complete description of when the realtime constraint must be met, by which party, and how it interacts with the thread safe tag. --- include/clap/ext/thread-check.h | 47 ++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/include/clap/ext/thread-check.h b/include/clap/ext/thread-check.h index 7b68e1b0..493e192d 100644 --- a/include/clap/ext/thread-check.h +++ b/include/clap/ext/thread-check.h @@ -17,23 +17,44 @@ extern "C" { /// This will be the same OS thread throughout the lifetime of the plug-in. /// On macOS and Windows, this must be the thread on which gui and timer events are received /// (i.e., the main thread of the program). -/// It isn't a realtime thread, yet this thread needs to respond fast enough to user interaction, -/// so it is recommended to run long and expensive tasks such as preset indexing or asset loading -/// in dedicated background threads. +/// It isn't a realtime thread, yet this thread needs to respond fast enough to allow responsive +/// user interaction, so it is strongly recommended plugins run long,and expensive or blocking +/// tasks such as preset indexing or asset loading in dedicated background threads started by the +/// plugin. /// /// audio-thread: -/// This thread is used for realtime audio processing. Its execution should be as deterministic -/// as possible to meet the audio interface's deadline (can be <1ms). In other words, there is a -/// known set of operations that should be avoided: malloc() and free(), mutexes (spin mutexes -/// are worse), I/O, waiting, ... -/// The audio-thread is something symbolic, there isn't one OS thread that remains the -/// audio-thread for the plugin lifetime. As you may guess, the host is likely to have a +/// This thread can be used for realtime audio processing. Its execution should be as +/// deterministic as possible to meet the audio interface's deadline (can be <1ms). There are a +/// known set of operations that should be avoided: malloc() and free(), contended locks and +/// mutexes, I/O, waiting, and so forth. +/// +/// The audio-thread is symbolic, there isn't one OS thread that remains the +/// audio-thread for the plugin lifetime. A host is may opt to have a /// thread pool and the plugin.process() call may be scheduled on different OS threads over time. -/// The most important thing is that there can't be two audio-threads at the same time. All the -/// functions marked with [audio-thread] **ARE NOT CONCURRENT**. The host may mark any OS thread, +/// However, the host must guarantee that single plugin instance will not be two audio-threads +/// at the same time. +/// +/// Functions marked with [audio-thread] **ARE NOT CONCURRENT**. The host may mark any OS thread, /// including the main-thread as the audio-thread, as long as it can guarantee that only one OS -/// thread is the audio-thread at a time. The audio-thread can be seen as a concurrency guard for -/// all functions marked with [audio-thread]. +/// thread is the audio-thread at a time in a plugin instance. The audio-thread can be seen as a +/// concurrency guard for all functions marked with [audio-thread]. +/// +/// The real-time constraint on the [audio-thread] interacts closely with the render extension. +/// If a plugin doesn't implement render, then that plugin must have all [audio-thread] functions +/// meet the real time standard. If the plugin does implement render, and returns true when +/// render mode is set to real-time or if the plugin advertises a hard realtime requirement, it +/// must implement realtime constraints. Hosts also provide functions marked [audio-thread]. +/// These can be safely called by a plugin in the audio thread. Therefore hosts must either (1) +/// implement those functions meeting the real-time constraints or (2) not process plugins which +/// advertise a hard realtime constraint or don't implement the render extension. Hosts which +/// provide [audio-thread] functions outside these conditions may experience inconsistent or +/// inaccurate rendering. +/// +/// Clap also tags some functions as [thread-safe]. Functions tagged as [thread-safe] can be called +/// from any thread unless explicitly counter-indicated (for instance [thread-safe, !audio-thread]) +/// and may be called concurrently. Since a [thread-safe] function may be called from the +/// [audio-thread] unless explicitly counter-indicated, it must also meet the realtime constraints +/// as describes above. // This interface is useful to do runtime checks and make // sure that the functions are called on the correct threads. From 3527890bcb9d514b9d028a0f9db91ebd433f36d8 Mon Sep 17 00:00:00 2001 From: trinitou Date: Wed, 7 Feb 2024 23:31:09 +0100 Subject: [PATCH 03/19] Adjust latency extension requirements - allow plugin_latency->get to be called during plugin->activate - require host_latency->changed to be called during plugin->activate --- include/clap/ext/latency.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/clap/ext/latency.h b/include/clap/ext/latency.h index 6c9df1b1..d10a1326 100644 --- a/include/clap/ext/latency.h +++ b/include/clap/ext/latency.h @@ -10,15 +10,15 @@ extern "C" { typedef struct clap_plugin_latency { // Returns the plugin latency in samples. - // [main-thread & active] + // [main-thread & (being-activated | active)] uint32_t(CLAP_ABI *get)(const clap_plugin_t *plugin); } clap_plugin_latency_t; typedef struct clap_host_latency { // Tell the host that the latency changed. - // The latency is only allowed to change if the plugin is deactivated. + // The latency is only allowed to change during plugin->activate. // If the plugin is activated, call host->request_restart() - // [main-thread] + // [main-thread & being-activated] void(CLAP_ABI *changed)(const clap_host_t *host); } clap_host_latency_t; From 5e2bd97cc341129840c6fd55f55eb55e3cce0488 Mon Sep 17 00:00:00 2001 From: Alexandre Bique Date: Mon, 2 Sep 2024 14:46:32 +0200 Subject: [PATCH 04/19] undo: redesign the interface - the plugin interfaces have been separated into 2 independent ones - the plugin interfaces are optional - simplification of the design --- include/clap/ext/draft/undo.h | 142 +++++++++++++++++++++------------- 1 file changed, 90 insertions(+), 52 deletions(-) diff --git a/include/clap/ext/draft/undo.h b/include/clap/ext/draft/undo.h index 365780e6..76070c38 100644 --- a/include/clap/ext/draft/undo.h +++ b/include/clap/ext/draft/undo.h @@ -1,8 +1,11 @@ #pragma once #include "../../plugin.h" +#include "../../stream.h" -static CLAP_CONSTEXPR const char CLAP_EXT_UNDO[] = "clap.undo/2"; +static CLAP_CONSTEXPR const char CLAP_EXT_UNDO[] = "clap.undo/4"; +static CLAP_CONSTEXPR const char CLAP_EXT_UNDO_CONTEXT[] = "clap.undo_context/4"; +static CLAP_CONSTEXPR const char CLAP_EXT_UNDO_DELTA[] = "clap.undo_delta/4"; #ifdef __cplusplus extern "C" { @@ -15,7 +18,7 @@ extern "C" { /// /// Calling host->undo() or host->redo() is equivalent to clicking undo/redo within the host's GUI. /// -/// If the plugin implements this interface then its undo and redo should be entirely delegated to +/// If the plugin uses this interface then its undo and redo should be entirely delegated to /// the host; clicking in the plugin's UI undo or redo is equivalent to clicking undo or redo in the /// host's UI. /// @@ -40,31 +43,32 @@ extern "C" { /// history. This simplifies the host implementation, leading to less bugs, a more robust design /// and maybe an easier experience for the user because there's a single undo context versus one /// for the host and one for each plugin instance. - -enum clap_undo_context_flags { - // While the host is within a change, it is impossible to perform undo or redo. - CLAP_UNDO_IS_WITHIN_CHANGE = 1 << 0, -}; - -enum clap_undo_delta_properties_flags { - // If not set, then all clap_undo_delta_properties's attributes become irrelevant. - // If set, then the plugin will provide deltas in host->change_made(). - CLAP_UNDO_DELTA_PROPERTIES_HAS_DELTA = 1 << 0, - - // If set, then the delta will be reusable in the future as long as the plugin is - // compatible with the given format_version. - CLAP_UNDO_DELTA_PROPERTIES_IS_PERSISTENT = 1 << 1, -}; +/// +/// This extension tries to make it as easy as possible for the plugin to hook into the host undo +/// and make it efficient when possible by using deltas. The plugin interfaces are all optional, and +/// the plugin can for a minimal implementation, just use the host interface and call +/// host->change_made() without providing a delta. This is enough for the host to know that it can +/// capture a plugin state for the undo step. typedef struct clap_undo_delta_properties { - // Bitmask of clap_undo_delta_properties_flags - uint64_t flags; + // If true, then the plugin will provide deltas in host->change_made(). + // If false, then all clap_undo_delta_properties's attributes become irrelevant. + bool has_delta; + + // If true, then the deltas can be stored on disk and re-used in the future as long as the plugin + // is compatible with the given format_version. + // + // If false, then format_version must be set to CLAP_INVALID_ID. + bool are_deltas_persistent; - // This represents the delta format version that the plugin is using. - uint32_t format_version; + // This represents the delta format version that the plugin is currently using. + // Use CLAP_INVALID_ID for invalid value. + clap_id format_version; } clap_undo_delta_properties_t; -typedef struct clap_plugin_undo { +// Use CLAP_EXT_UNDO_DELTA. +// This is an optional interface, using deltas is an optimization versus making a state snapshot. +typedef struct clap_plugin_undo_delta { // Asks the plugin the delta properties. // [main-thread] void(CLAP_ABI *get_delta_properties)(const clap_plugin_t *plugin, @@ -76,25 +80,42 @@ typedef struct clap_plugin_undo { bool(CLAP_ABI *can_use_delta_format_version)(const clap_plugin_t *plugin, clap_id format_version); - // Applies synchronously a delta. + // Undo using the delta. // Returns true on success. // // [main-thread] - bool(CLAP_ABI *apply_delta)(const clap_plugin_t *plugin, - clap_id format_version, - const void *delta, - size_t delta_size); - - // Sets the undo context. - // flags: bitmask of clap_undo_context_flags values - // names: null terminated string if an redo/undo step exists, null otherwise. - // [main-thread] - void(CLAP_ABI *set_context_info)(const clap_plugin_t *plugin, - uint64_t flags, - const char *undo_name, - const char *redo_name); -} clap_plugin_undo_t; + bool(CLAP_ABI *undo)(const clap_plugin_t *plugin, + clap_id format_version, + const void *delta, + size_t delta_size); + // Redo using the delta. + // Returns true on success. + // + // [main-thread] + bool(CLAP_ABI *redo)(const clap_plugin_t *plugin, + clap_id format_version, + const void *delta, + size_t delta_size); +} clap_plugin_undo_delta_t; + +// Use CLAP_EXT_UNDO_CONTEXT. +// This is an optional interface, that the plugin can implement in order to know about +// the current undo context. +typedef struct clap_plugin_undo_context { + // Indicate if it is currently possible to perform an undo or redo operation. + // [main-thread & plugin-subscribed-to-undo-context] + void(CLAP_ABI *set_can_undo)(const clap_plugin_t *plugin, bool can_undo); + void(CLAP_ABI *set_can_redo)(const clap_plugin_t *plugin, bool can_redo); + + // Sets the name of the next undo or redo step. + // name: null terminated string. + // [main-thread & plugin-subscribed-to-undo-context] + void(CLAP_ABI *set_undo_name)(const clap_plugin_t *plugin, const char *name); + void(CLAP_ABI *set_redo_name)(const clap_plugin_t *plugin, const char *name); +} clap_plugin_undo_context_t; + +// Use CLAP_EXT_UNDO. typedef struct clap_host_undo { // Begins a long running change. // The plugin must not call this twice: there must be either a call to cancel_change() or @@ -112,36 +133,51 @@ typedef struct clap_host_undo { // // name: mandatory null terminated string describing the change, this is displayed to the user // - // deltas: optional, they are binary blobs used to perform the undo and redo. When not available + // delta: optional, it is a binary blobs used to perform the undo and redo. When not available // the host will save the plugin state and use state->load() to perform undo and redo. + // The plugin must be able to perform a redo operation using the delta, though the undo operation + // is only possible if delta_can_undo is true. // // Note: the provided delta may be used for incremental state saving and crash recovery. The // plugin can indicate a format version id and the validity lifetime for the binary blobs. // The host can use these to verify the compatibility before applying the delta. // If the plugin is unable to use a delta, a notification should be provided to the user and - // the crash recovery should perform a best effort job, at least restoring the latest saved state. + // the crash recovery should perform a best effort job, at least restoring the latest saved + // state. // // Special case: for objects with shared and synchronized state, changes shouldn't be reported // as the host already knows about it. // For example, plugin parameter changes shouldn't produce a call to change_made(). // + // Note: if the plugin asked for this interface, then host_state->mark_dirty() will not create an + // implicit undo step. + // + // Note: if the plugin did load a preset or did something that leads to a large delta, + // it may consider not producing a delta (pass null) and let the host make a state snapshot + // instead. + // + // Note: if a plugin is producing a lot of changes within a small amount of time, the host + // may merge them into a single undo step. + // // [main-thread] void(CLAP_ABI *change_made)(const clap_host_t *host, const char *name, - const void *redo_delta, - size_t redo_delta_size, - const void *undo_delta, - size_t undo_delta_size); - - // Asks the host to perform the next undo step. - // This operation may be asynchronous. - // [main-thread] - void(CLAP_ABI *undo)(const clap_host_t *host); + const void *delta, + size_t delta_size, + bool delta_can_undo); - // Asks the host to perform the next redo step. - // This operation may be asynchronous. + // Asks the host to perform the next undo or redo step. + // + // Note: this maybe a complex and asynchronous operation, which may complete after + // this function returns. + // + // Note: the host may ignore this request if there is no undo/redo step to perform, + // or if the host is unable to perform undo/redo at the time (eg: a long running + // change is going on). + // // [main-thread] - void(CLAP_ABI *redo)(const clap_host_t *host); + void(CLAP_ABI *request_undo)(const clap_host_t *host); + void(CLAP_ABI *request_redo)(const clap_host_t *host); // Subscribes to or unsubscribes from undo context info. // @@ -154,8 +190,10 @@ typedef struct clap_host_undo { // // is_subscribed: set to true to receive context info // + // It is mandatory for the plugin to implement CLAP_EXT_UNDO_CONTEXT when using this method. + // // [main-thread] - void(CLAP_ABI *set_context_info_subscription)(const clap_host_t *host, bool is_subscribed); + void(CLAP_ABI *set_wants_context_updates)(const clap_host_t *host, bool is_subscribed); } clap_host_undo_t; #ifdef __cplusplus From 86c168189686732f07599f72789298d28e5147f9 Mon Sep 17 00:00:00 2001 From: Alexandre Bique Date: Mon, 2 Sep 2024 16:16:25 +0200 Subject: [PATCH 05/19] Update ChangeLog --- ChangeLog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index f9f9e351..c47a37dd 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,12 @@ +# Changes in 1.2.2 + +* [thread-check.h](include/clap/ext/thread-check.h): expand the thread-doc to clarify and expand realtime +* [latency.h](include/clap/ext/latency.h): adjust latency extension requirements +* [undo.h](include/clap/ext/draft/undo.h): re-design the interface + * the plugin interfaces have been separated into 2 independent ones + * the plugin interfaces are optional + * simplification of the design + # Changes in 1.2.1 ## New draft extensions From 961be0b9a140d20161b700a53588c94cb7272a3e Mon Sep 17 00:00:00 2001 From: Alexandre Bique Date: Mon, 2 Sep 2024 16:16:52 +0200 Subject: [PATCH 06/19] CLAP 1.2.2 --- include/clap/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clap/version.h b/include/clap/version.h index fdbd03a7..d11069c1 100644 --- a/include/clap/version.h +++ b/include/clap/version.h @@ -22,7 +22,7 @@ typedef struct clap_version { #define CLAP_VERSION_MAJOR 1 #define CLAP_VERSION_MINOR 2 -#define CLAP_VERSION_REVISION 1 +#define CLAP_VERSION_REVISION 2 #define CLAP_VERSION_INIT \ { (uint32_t)CLAP_VERSION_MAJOR, (uint32_t)CLAP_VERSION_MINOR, (uint32_t)CLAP_VERSION_REVISION } From d0f0373228199fb792b9828abee8f04995858a9a Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 30 Sep 2024 03:09:28 -0400 Subject: [PATCH 07/19] Add a description of the expectation of request_callback timing (#422) * Add a description of the expectation of request_callback timing Without making a requirement, indicate the intent of the timing. * Add an apostrophe * Add host can starve feedback from alex * more review feedback * notjusthosts --- include/clap/host.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/clap/host.h b/include/clap/host.h index ddfeb8e7..c647c198 100644 --- a/include/clap/host.h +++ b/include/clap/host.h @@ -35,6 +35,12 @@ typedef struct clap_host { void(CLAP_ABI *request_process)(const struct clap_host *host); // Request the host to schedule a call to plugin->on_main_thread(plugin) on the main thread. + // This callback should be called as soon as practicable, usually in the host application's next + // available main thread time slice. Typically callbacks occur withink 33ms / 30hz. + // Despite this guidance, plugins should not make assumptions about the exactness of timing for + // a main thread callback, but hosts should endeavour to be prompt. For example, in high load situations + // the environment may starve the gui/main thread in favor of audio processing, leading to substantially + // longer latencies for the callback than the indicative times given here. // [thread-safe] void(CLAP_ABI *request_callback)(const struct clap_host *host); } clap_host_t; From a5790e1b3538331b777a605fcb12be1071d9647b Mon Sep 17 00:00:00 2001 From: jatin Date: Tue, 8 Oct 2024 11:52:53 -0700 Subject: [PATCH 08/19] Very rough draft scratch-memory extension --- include/clap/ext/draft/scratch-memory.h | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 include/clap/ext/draft/scratch-memory.h diff --git a/include/clap/ext/draft/scratch-memory.h b/include/clap/ext/draft/scratch-memory.h new file mode 100644 index 00000000..e151533c --- /dev/null +++ b/include/clap/ext/draft/scratch-memory.h @@ -0,0 +1,38 @@ +#pragma once + +#include "../../plugin.h" + +// This extension lets the plugin request "scratch" memory +// from the host. Scratch memory can be accessed during the +// `process()` callback, but is not persistent between callbacks. + +static CLAP_CONSTEXPR const char CLAP_EXT_SCRATCH_MEMORY[] = "clap.scratch-memory/1"; + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct clap_host_scratch_memory { + // Asks the host for certain amount of scratch memory. + // If the host is unable to provide the memory, it should + // return "false". + // [main-thread & being-activated] + bool(CLAP_ABI *request_scratch_size)(const clap_host_t *host, size_t scratch_size_bytes); + + // Asks the host for the previously requested scratch memory. + // If the host returned "true" when scratch memory was requested, + // then this method must return a pointer to a memory block at least + // as large as the requested size. If the host returned "false" + // when scratch memory was requested then this method should return + // null. + // + // Note that this memory may be uninitialized, so the plugin may + // want to "zero" the memory before using it. + // + // [audio-thread] + void*(CLAP_ABI *access_scratch)(const clap_host_t *host); +}; + +#ifdef __cplusplus +} +#endif From efa821a9e905f6db0fed2cf5f1b83afb2b82ac36 Mon Sep 17 00:00:00 2001 From: jatin Date: Tue, 8 Oct 2024 12:07:31 -0700 Subject: [PATCH 09/19] Name changes and documenting de-allocation pattern --- include/clap/ext/draft/scratch-memory.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/include/clap/ext/draft/scratch-memory.h b/include/clap/ext/draft/scratch-memory.h index e151533c..e787d4ee 100644 --- a/include/clap/ext/draft/scratch-memory.h +++ b/include/clap/ext/draft/scratch-memory.h @@ -16,8 +16,13 @@ typedef struct clap_host_scratch_memory { // Asks the host for certain amount of scratch memory. // If the host is unable to provide the memory, it should // return "false". + // + // Note that any memory the host allocates to satisfy + // the requested scratch size can be de-allocated + // when the plugin is de-activated. + // // [main-thread & being-activated] - bool(CLAP_ABI *request_scratch_size)(const clap_host_t *host, size_t scratch_size_bytes); + bool(CLAP_ABI *request_size)(const clap_host_t *host, size_t scratch_size_bytes); // Asks the host for the previously requested scratch memory. // If the host returned "true" when scratch memory was requested, @@ -30,7 +35,7 @@ typedef struct clap_host_scratch_memory { // want to "zero" the memory before using it. // // [audio-thread] - void*(CLAP_ABI *access_scratch)(const clap_host_t *host); + void*(CLAP_ABI *access)(const clap_host_t *host); }; #ifdef __cplusplus From 48c1ba804aa5faba05de076205c6582566caf460 Mon Sep 17 00:00:00 2001 From: jatin Date: Sat, 12 Oct 2024 17:12:56 -0700 Subject: [PATCH 10/19] Fixing typedef declaration --- include/clap/ext/draft/scratch-memory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clap/ext/draft/scratch-memory.h b/include/clap/ext/draft/scratch-memory.h index e787d4ee..a79532d5 100644 --- a/include/clap/ext/draft/scratch-memory.h +++ b/include/clap/ext/draft/scratch-memory.h @@ -36,7 +36,7 @@ typedef struct clap_host_scratch_memory { // // [audio-thread] void*(CLAP_ABI *access)(const clap_host_t *host); -}; +} clap_host_scratch_memory_t; #ifdef __cplusplus } From a65ba5728456bb97bce096cc16d1349f4e284d6f Mon Sep 17 00:00:00 2001 From: jatin Date: Tue, 15 Oct 2024 14:17:11 -0700 Subject: [PATCH 11/19] Some changes based on PR feedback --- include/clap/ext/draft/scratch-memory.h | 30 ++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/include/clap/ext/draft/scratch-memory.h b/include/clap/ext/draft/scratch-memory.h index a79532d5..4ab51a67 100644 --- a/include/clap/ext/draft/scratch-memory.h +++ b/include/clap/ext/draft/scratch-memory.h @@ -5,6 +5,22 @@ // This extension lets the plugin request "scratch" memory // from the host. Scratch memory can be accessed during the // `process()` callback, but is not persistent between callbacks. +// +// The motivation for this extension is to allow the plugin host +// to "share" a single scratch buffer across multiple plugin +// instances. +// +// For example, imagine the host needs to process three plugins +// in sequence, and each plugin requires 10K of scratch memory. +// If each plugin pre-allocates its own scratch memory, then 30K +// of memory is being allocated in total. However, if each plugin +// requests 10K of scratch memory from the host, then the host can +// allocate a single 10K scratch buffer, and make it available to all +// three plugins. +// +// On memory-constrained platforms, this optimization may allow for +// more plugins to be used simultaneously. On platforms with lots +// of memory, this optimization may improve CPU cache usage. static CLAP_CONSTEXPR const char CLAP_EXT_SCRATCH_MEMORY[] = "clap.scratch-memory/1"; @@ -18,11 +34,11 @@ typedef struct clap_host_scratch_memory { // return "false". // // Note that any memory the host allocates to satisfy - // the requested scratch size can be de-allocated - // when the plugin is de-activated. + // the requested scratch size can be de-allocated by the + // host when the plugin is de-activated. // // [main-thread & being-activated] - bool(CLAP_ABI *request_size)(const clap_host_t *host, size_t scratch_size_bytes); + bool(CLAP_ABI *pre_reserve)(const clap_host_t *host, size_t scratch_size_bytes); // Asks the host for the previously requested scratch memory. // If the host returned "true" when scratch memory was requested, @@ -31,8 +47,12 @@ typedef struct clap_host_scratch_memory { // when scratch memory was requested then this method should return // null. // - // Note that this memory may be uninitialized, so the plugin may - // want to "zero" the memory before using it. + // The provided memory is not initialized, and may have been used + // by other plugin instances, so the plugin must correctly initialize + // the memory when using it. + // + // The provided memory is owned by the host, so the plugin should not + // attempt to free the memory. // // [audio-thread] void*(CLAP_ABI *access)(const clap_host_t *host); From 1b1a68e2e3fef04dcf369ca3568037b97bd26fb3 Mon Sep 17 00:00:00 2001 From: jatin Date: Wed, 16 Oct 2024 21:21:50 -0700 Subject: [PATCH 12/19] Attempt to clarify usage with thread-pool extension and use uint32_t over size_t --- include/clap/ext/draft/scratch-memory.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/include/clap/ext/draft/scratch-memory.h b/include/clap/ext/draft/scratch-memory.h index 4ab51a67..60860527 100644 --- a/include/clap/ext/draft/scratch-memory.h +++ b/include/clap/ext/draft/scratch-memory.h @@ -37,8 +37,16 @@ typedef struct clap_host_scratch_memory { // the requested scratch size can be de-allocated by the // host when the plugin is de-activated. // + // In the context of plugins and hosts that implement + // the "thread-pool" extension, scratch memory is assumed + // to be "thread-local". The plugin should request the maximum + // amount of scratch memory that it will need on a single + // thread. Accordingly, the host must ensure that each + // thread can independently provide the requested amount + // of scratch memory. + // // [main-thread & being-activated] - bool(CLAP_ABI *pre_reserve)(const clap_host_t *host, size_t scratch_size_bytes); + bool(CLAP_ABI *reserve)(const clap_host_t *host, uint32_t scratch_size_bytes); // Asks the host for the previously requested scratch memory. // If the host returned "true" when scratch memory was requested, From f2a84b959275bb6b7ae6d09b315b52a34dd6a204 Mon Sep 17 00:00:00 2001 From: jatin Date: Wed, 16 Oct 2024 21:32:09 -0700 Subject: [PATCH 13/19] Clarify access pattern from the plugin side --- include/clap/ext/draft/scratch-memory.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/clap/ext/draft/scratch-memory.h b/include/clap/ext/draft/scratch-memory.h index 60860527..2d959343 100644 --- a/include/clap/ext/draft/scratch-memory.h +++ b/include/clap/ext/draft/scratch-memory.h @@ -55,6 +55,14 @@ typedef struct clap_host_scratch_memory { // when scratch memory was requested then this method should return // null. // + // This method may only be called by the plugin from the audio thread, + // (i.e. during the process() or thread_pool.exec() callback), and + // the provided memory is only valid until the plugin returns from + // that callback. The plugin must not hold any references to data + // that lives in the scratch memory after returning from the callback, + // as that data will likely be over-written by another plugin using + // the same scratch memory. + // // The provided memory is not initialized, and may have been used // by other plugin instances, so the plugin must correctly initialize // the memory when using it. From 06ccabdc1b7691a56874c8eb836cbc3efe91397e Mon Sep 17 00:00:00 2001 From: jatinchowdhury18 Date: Fri, 18 Oct 2024 18:05:49 -0700 Subject: [PATCH 14/19] Update include/clap/ext/draft/scratch-memory.h Co-authored-by: Trinitou <106991375+Trinitou@users.noreply.github.com> --- include/clap/ext/draft/scratch-memory.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/clap/ext/draft/scratch-memory.h b/include/clap/ext/draft/scratch-memory.h index 2d959343..4ac950c4 100644 --- a/include/clap/ext/draft/scratch-memory.h +++ b/include/clap/ext/draft/scratch-memory.h @@ -48,12 +48,12 @@ typedef struct clap_host_scratch_memory { // [main-thread & being-activated] bool(CLAP_ABI *reserve)(const clap_host_t *host, uint32_t scratch_size_bytes); - // Asks the host for the previously requested scratch memory. + // Asks the host for the previously reserved scratch memory. // If the host returned "true" when scratch memory was requested, // then this method must return a pointer to a memory block at least - // as large as the requested size. If the host returned "false" - // when scratch memory was requested then this method should return - // null. + // as large as the reserved size. If the host returned "false" + // when scratch memory was requested, then this method must not + // be called. // // This method may only be called by the plugin from the audio thread, // (i.e. during the process() or thread_pool.exec() callback), and From 5bf4e59d770250da5dc29df1d80c525391054eba Mon Sep 17 00:00:00 2001 From: jatin Date: Fri, 18 Oct 2024 18:10:40 -0700 Subject: [PATCH 15/19] Clarify plugins calling reserve() multiple times --- include/clap/ext/draft/scratch-memory.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/clap/ext/draft/scratch-memory.h b/include/clap/ext/draft/scratch-memory.h index 4ac950c4..b98d6bde 100644 --- a/include/clap/ext/draft/scratch-memory.h +++ b/include/clap/ext/draft/scratch-memory.h @@ -33,6 +33,15 @@ typedef struct clap_host_scratch_memory { // If the host is unable to provide the memory, it should // return "false". // + // The plugin may call this method multiple times (for + // example, gradually decreasing the amount of scratch + // being asked for until the host returns true), however, + // the plugin should avoid calling this method un-neccesarily + // since the host implementation may be relatively expensive. + // If the plugin calls this method multiple times, then the + // final call determines the actual amount of scratch memory + // that will be available to the plugin. + // // Note that any memory the host allocates to satisfy // the requested scratch size can be de-allocated by the // host when the plugin is de-activated. From 635045cb5dbcf71fd0fa00d42277c3656470853f Mon Sep 17 00:00:00 2001 From: jatin Date: Sun, 20 Oct 2024 19:33:45 -0700 Subject: [PATCH 16/19] Clarify: if final reserve() call returns false, then no scratch memory --- include/clap/ext/draft/scratch-memory.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/clap/ext/draft/scratch-memory.h b/include/clap/ext/draft/scratch-memory.h index b98d6bde..cc2764c0 100644 --- a/include/clap/ext/draft/scratch-memory.h +++ b/include/clap/ext/draft/scratch-memory.h @@ -38,9 +38,11 @@ typedef struct clap_host_scratch_memory { // being asked for until the host returns true), however, // the plugin should avoid calling this method un-neccesarily // since the host implementation may be relatively expensive. - // If the plugin calls this method multiple times, then the + // If the plugin calls `reserve()` multiple times, then the // final call determines the actual amount of scratch memory - // that will be available to the plugin. + // that will be available to the plugin. If the final call + // returns false then no scratch memory will be provided, + // regardless of any previous calls to `reserve()`. // // Note that any memory the host allocates to satisfy // the requested scratch size can be de-allocated by the From 5006ea2ea71931493d2f8b99ec2a8f70f0238be3 Mon Sep 17 00:00:00 2001 From: jatin Date: Wed, 23 Oct 2024 01:53:16 -0700 Subject: [PATCH 17/19] Tweaks based on PR feedback --- include/clap/ext/draft/scratch-memory.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/clap/ext/draft/scratch-memory.h b/include/clap/ext/draft/scratch-memory.h index cc2764c0..7902a07c 100644 --- a/include/clap/ext/draft/scratch-memory.h +++ b/include/clap/ext/draft/scratch-memory.h @@ -4,7 +4,8 @@ // This extension lets the plugin request "scratch" memory // from the host. Scratch memory can be accessed during the -// `process()` callback, but is not persistent between callbacks. +// `process()` callback, but its content not persistent +// between callbacks. // // The motivation for this extension is to allow the plugin host // to "share" a single scratch buffer across multiple plugin @@ -18,9 +19,8 @@ // allocate a single 10K scratch buffer, and make it available to all // three plugins. // -// On memory-constrained platforms, this optimization may allow for -// more plugins to be used simultaneously. On platforms with lots -// of memory, this optimization may improve CPU cache usage. +// This optimization may allow for reduced memory usage and improved +// CPU cache usage. static CLAP_CONSTEXPR const char CLAP_EXT_SCRATCH_MEMORY[] = "clap.scratch-memory/1"; @@ -64,7 +64,7 @@ typedef struct clap_host_scratch_memory { // then this method must return a pointer to a memory block at least // as large as the reserved size. If the host returned "false" // when scratch memory was requested, then this method must not - // be called. + // be called, and will return NULL. // // This method may only be called by the plugin from the audio thread, // (i.e. during the process() or thread_pool.exec() callback), and From 8df28a370d1f6d1006386c140632820f8d2443d1 Mon Sep 17 00:00:00 2001 From: jatin Date: Thu, 24 Oct 2024 22:00:16 -0700 Subject: [PATCH 18/19] Document being-activated --- include/clap/plugin.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/clap/plugin.h b/include/clap/plugin.h index 7e83074b..0a239457 100644 --- a/include/clap/plugin.h +++ b/include/clap/plugin.h @@ -61,6 +61,7 @@ typedef struct clap_plugin { // In this call the plugin may allocate memory and prepare everything needed for the process // call. The process's sample rate will be constant and process's frame count will included in // the [min, max] range, which is bounded by [1, INT32_MAX]. + // In this call the plugin may call host-provided methods marked [being-activated]. // Once activated the latency and port configuration must remain constant, until deactivation. // Returns true on success. // [main-thread & !active] From b7968b2fa62b172c970d5a3c6955471e22fdb4ea Mon Sep 17 00:00:00 2001 From: jatin Date: Thu, 24 Oct 2024 22:05:12 -0700 Subject: [PATCH 19/19] Plugin most reserve again on re-activation --- include/clap/ext/draft/scratch-memory.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/clap/ext/draft/scratch-memory.h b/include/clap/ext/draft/scratch-memory.h index 7902a07c..556e5eab 100644 --- a/include/clap/ext/draft/scratch-memory.h +++ b/include/clap/ext/draft/scratch-memory.h @@ -44,9 +44,10 @@ typedef struct clap_host_scratch_memory { // returns false then no scratch memory will be provided, // regardless of any previous calls to `reserve()`. // - // Note that any memory the host allocates to satisfy - // the requested scratch size can be de-allocated by the - // host when the plugin is de-activated. + // When the plugin is de-activated, the scratch memory + // is invalidated, and the host may free the memory if + // appropriate. The plugin will need to reserve scratch + // memory again the next time it is activated. // // In the context of plugins and hosts that implement // the "thread-pool" extension, scratch memory is assumed