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

Plugin State Converter rework #454

Open
wants to merge 3 commits into
base: next
Choose a base branch
from
Open
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
108 changes: 77 additions & 31 deletions include/clap/factory/draft/plugin-state-converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,75 +22,121 @@ typedef struct clap_plugin_state_converter_descriptor {
const char *description; // eg: "Official state converter for u-he Diva."
} clap_plugin_state_converter_descriptor_t;

typedef struct clap_plugin_state_convert_param_value {
clap_id param_id;
double value;
} clap_plugin_state_converter_param_value_t;

enum clap_plugin_state_convert_flags {
// When this flag is set, then we are converting an automation point.
CLAP_PLUGIN_STATE_CONVERTER_FOR_AUTOMATION = 1 << 0,

// When this flag is set, then we are converting a modulation mapping.
// For example we can ask the target value for 0.5 and then 0.6 and
// calculate the new modulation mapping range.
CLAP_PLUGIN_STATE_CONVERTER_FOR_MODULATION_MAPPING = 1 << 1,

// When set, input and output values must be normalized.
CLAP_PLUGIN_STATE_CONVERTER_VALUE_IS_NORMALIZED = 1 << 2,
};

// 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.
//
// Note:
// We recommend to first convert the state and then the automation points.
// Because the points convertion may depend on the plugin state, and the converter is
// stateful so it needs to know the state in order to achieve optimal automation conversion.
// The converter may also assume that every call to convert_value where
// CLAP_PLUGIN_STATE_CONVERTER_FOR_AUTOMATION is set happens in chronological order.
//
// Note:
// Sometimes, there's a many to many relationship between source parameters and target
// parameters. For example in plugin A, you have two parameters Phase, Amplitude which describes
// some polar coordinates. In plugin B, you have X, Y parameters which are euclidian coordinates.
// It is not possible to perfectly convert from A to B or B to A, because of the automation
// curves, ... Though, for a best effort approach we can support one parameter that translates to
// many. For example, in A you have a 3 state enum parameter, that is translated into two bool
// parameters in plugin B, or you have a parameter that has been duplicated in B.
//
// That is why, when we convert a parameter value, it can produce 0 to many target parameter
// value.
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);
void(CLAP_ABI *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.
bool(CLAP_ABI *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);

// Get the parameter mapping.
//
// 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);
// Note: one source parameter can map to many destination parameters, see the note above.
int32_t(CLAP_ABI *get_mapping)(struct clap_plugin_state_converter *converter,
clap_id src_param_id,
clap_id *dst_param_ids,
uint32_t dst_param_ids_size);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_mapping is reduntant I think? convert_value can do this already.

// Converts a value.
// flags is a bitmask, see clap_plugin_state_convert_flags
// src is a single parameter value.
// dsts is an array of parameter values.
// dsts_size is the size of the dsts array.
// See get_max_target_parameters() and the struct's note.
//
// Returns the number of converted parameters, or -1 on error.
//
// Note:
// convert_value assumes the state of the plugin to the one from the most recent call to
// convert_state.
int32_t(CLAP_ABI *convert_value)(struct clap_plugin_state_converter *converter,
uint32_t flags,
clap_plugin_state_converter_param_value_t *src,
clap_plugin_state_converter_param_value_t *dsts,
uint32_t dsts_size);
} clap_plugin_state_converter_t;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to turn convert_state, get_mapping and convert_value into a single function:

int32_t(CLAP_ABI *convert)(struct clap_plugin_state_converter *converter,
uint32_t flags,
const clap_istream_t *src,
const clap_ostream_t *dst,
clap_plugin_state_converter_param_value_t *srcs,
uint32_t srcs_count,
clap_plugin_state_converter_param_value_t *dsts,
uint32_t dsts_size);

This avoids the rather ugly "convert_value assumes the state of the plugin to the one from the most recent call to". It also gives plugin developers the whole shebang in one go, so we don't have to change it again when a developer needs to see more than one automation point to be able to do the right thing.

// Factory identifier
static CLAP_CONSTEXPR const char CLAP_PLUGIN_STATE_CONVERTER_FACTORY_ID[] =
"clap.plugin-state-converter-factory/1";
"clap.plugin-state-converter-factory/2";

// 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);
uint32_t(CLAP_ABI *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 clap_plugin_state_converter_descriptor_t *(CLAP_ABI *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.
//
// Returns a stateful converter on success, or null on error.
//
// Note: the returned converter isn't thread-safe.
//
// [thread-safe]
clap_plugin_state_converter_t *(*create)(
clap_plugin_state_converter_t *(CLAP_ABI *create)(
const struct clap_plugin_state_converter_factory *factory, const char *converter_id);
} clap_plugin_state_converter_factory_t;

Expand Down
Loading