Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

param-resolutions extension #428

Draft
wants to merge 9 commits into
base: next
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,5 @@ cmake-build*/

# A place to store stuff and get it git ignored
ignore/*

.DS_Store
9 changes: 9 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
114 changes: 114 additions & 0 deletions include/clap/ext/draft/param-resolutions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#pragma once

#include "../../plugin.h"

static CLAP_CONSTEXPR const char CLAP_EXT_PARAM_RESOLUTIONS[] = "clap.param-resolutions/1";

#ifdef __cplusplus
extern "C" {
#endif

/// This extension allows a plugin to provides discrete value resolutions for parameters.
///
/// The host can use the parameter resolution for example for
/// - incrementing/decrementing the parameter value in discrete steps
/// (via mousewheel, up/down keys or relative hardware encoders, ...)
/// - drawing a grid for a generic parameter gui control
///
/// The plugin can provide additional resolution levels (fine/coarse)
/// which the host can use e.g. to provide more fine/coarse control over a parameter if the user
/// holds a key modifier while incrementing/decrementing its value.
///
/// The term "resolution" in this context describes the discrete number of segments in which the
/// value range from param_info.min_value to param_info.max_value is split into.
/// The host could, for example, calculate a discrete step delta for a given `resolution`
/// using `(param_info.max_value - param_info.min_value) / resolution`.
///
/// Note: For the following examples, the term "resulting display value" means the resulting text
/// after using the plugin's value-to-text method on the actual (double) parameter value.
///
/// Resolution examples:
/// - Simple parameter with a value range from param_info.min_value=0.0 to param_info.max_value=1.0
/// - Resulting values:
/// | resolution | values |
/// |============|==========================|
/// | 1 | 0.0 1.0 |
/// | 2 | 0.0 0.5 1.0 |
/// | 4 | 0.0 0.25 0.5 0.75 1.0 |
/// | 10 | 0.0 0.1 0.2 ... 0.9 1.0 |
/// - Bipolar parameter with
/// - A value range from param_info.min_value=0.0 to param_info.max_value=1.0
/// - A display value range from -1.0 to 1.0 (after using value-to-text)
/// - resulting values:
/// | resolution | values | resulting display values |
/// |============|==========================|============================|
/// | 1 | 0.0 1.0 | -1.0 1.0 |
/// | 2 | 0.0 0.5 1.0 | -1.0 0.0 1.0 |
/// | 4 | 0.0 0.25 0.5 0.75 1.0 | -1.0 -0.5 0.5 0.75 1.0 |
/// | 20 | 0.0 0.1 0.2 ... 0.9 1.0 | -1.0 -0.9 -0.8 ... 0.9 1.0 |
///
/// Use case example:
/// - A 'Transpose' parameter with a display value range from -24st to +24st
/// - clap_param_resolutions:
/// - base_resolution: 48
/// - fine_resolution_factor: 100 (resulting fine resolution: 48 * 100 = 4800)
/// - coarse_resolution_divisor: 12 (resulting coarse resolution: 48 / 12 = 4)
/// - Resulting display values:
/// | resolution | resulting display values (in st) |
/// |===============|=============================================|
/// | (fine) 4800 | -24 -24.01 ... 0 ... +23.99 +24 (cents) |
/// | (base) 48 | -24 -23 ... 0 ... +23 +24 (semitones) |
/// | (coarse) 4 | -24 -12 0 +12 +24 (octaves) |

typedef struct clap_param_resolutions {
// Base resolution of the parameter.
//
// Constraint: has to be >= 1
uint16_t base_resolution;
// Factor to multiply base_resolution with for a finer resolution
// Resulting fine resolution: base_resolution * fine_resolution_factor
// Set to 1 if there is no special fine resolution present
//
// Constraints:
// - has to be >= 1
// - for parameters with CLAP_PARAM_IS_STEPPED flag set, the resulting fine resolution must be
// equal to `param_info.max_value - param_info.min_value`
uint16_t fine_resolution_factor;
// Divisor which to divide base_resolution by for a more coarse resolution
// Resulting coarse resolution: base_resolution / fine_resolution_factor
// Set to 1 if there is no special coarse resolution present
//
// Constraints:
// - has to be >= 1
// - base_resolution has to be divisible by coarse_resolution_divisor
uint16_t coarse_resolution_divisor;
} clap_param_resolutions_t;

typedef struct clap_plugin_param_resolutions {
// Get the resolutions for a parameter.
// Returns false if there is no discrete resolution available for the parameter with the given
// id.
// The host must not call this for parameters with CLAP_PARAM_IS_ENUM flag set.
//
// Note: If this returns false for a parameter with CLAP_PARAM_IS_STEPPED flag set, the parameter
// has only the implicit base_resolution of `param_info.max_value - param_info.min_value`.
// [main-thread]
bool(CLAP_ABI *get)(const clap_plugin_t *plugin,
clap_id param_id,
clap_param_resolutions *resolutions);

} clap_plugin_param_resolutions_t;

typedef struct clap_host_param_resolutions {
// Informs the host that param resolutions have changed.
//
// Note: If the plugin calls params.rescan with CLAP_PARAM_RESCAN_ALL, all previously scanned
// parameter resolutions must be considered invalid. It is thus not necessary for the plugin to
// call param_resolution.changed in this case.
//
// [main-thread]
void(CLAP_ABI *changed)(const clap_host_t *host);
} clap_host_param_resolutions_t;
#ifdef __cplusplus
}
#endif
142 changes: 90 additions & 52 deletions include/clap/ext/draft/undo.h
Original file line number Diff line number Diff line change
@@ -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" {
Expand All @@ -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.
///
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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.
//
Expand All @@ -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
Expand Down
6 changes: 3 additions & 3 deletions include/clap/ext/latency.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Loading
Loading