Skip to content

Proposal for unique parameter identifiers #3

@micahrj

Description

@micahrj

Each of a plugin's parameters is associated with a unique, stable identifier. Currently, these identifiers are u32 values which are manually specified in the plugin source (via #[param(id = ...)] if using the Params derive macro). These u32 ID values are then used directly as the 32-bit parameter IDs expected by the VST 3 and CLAP APIs. This approach has several downsides:

  • The range of valid 32-bit identifiers is not the same for every plugin API. In particular, VST 3 only allows parameter IDs less than 2^31. With the current approach, plugin authors have to be aware of this restriction when choosing parameter IDs. If, in the future, Coupler added support for another plugin format with a different set of restrictions, plugin authors would have to be made aware of those restrictions as well.
  • Some features require exposing additional host-visible parameters as an implementation detail. For instance, in VST 3, MIDI CC input and program change commands are delivered as parameter change events for specially designated parameters. To support these features, there will need to be some solution for avoiding parameter ID collisions, such as reserving a block of IDs for framework-internal use (which exposes implementation details to plugin authors) or dynamically choosing parameter IDs which are not already in use by the plugin (which could present backward compatibility hazards).
  • Manually specifying parameter IDs is cumbersome and error-prone for plugin authors.
  • It is unclear how the current approach could be extended to support nested parameter structs with repetition. For instance, in a synthesizer plugin with multiple identical oscillator or filter sections, it would be useful to be able to define a reusable struct containing all of the parameters for a single oscillator or filter and then include multiple copies of that struct as fields in a larger parameter struct. If parameter IDs are simply plugin-global opaque u32 values, there is no obvious way to compositionally assign distinct IDs for each distinct copy of a nested parameter struct.

To solve these problems, Coupler will use sequences of string keys as unique, stable identifiers for plugin parameters. By default, the Params derive macro will use a parameter's field name as its string key; it will be possible to specify a custom key using #[param(key = "...")]. Nested parameter structs will also have associated string keys, which will be prepended to the key sequences of the parameters they contain. The full unique identifier for a given parameter will thus consist of the sequence of string keys along the path from the root struct down to that parameter.

For each plugin format, the framework will be responsible for mapping these key sequences to the space of host-visible identifiers. The exact mapping used can be considered an implementation detail, but it must always give consistent results for a given set of key sequences, and it should be independent of parameter order. To support the addition of new parameters while preserving mapping stability, each parameter will have an associated generation number (defaulting to zero). Modifications to the set of parameters with a higher generation number should have no effect on the identifier mapping for parameters with lower generation numbers. To support the removal of existing parameters while preserving mapping stability, plugins will also be able to specify a set of reserved key sequences, each with an associated generation number. Removing a parameter and adding a corresponding reserved key sequence (with the same generation number) should have no effect on the identifier mapping for the remaining parameters.

To avoid incurring the cost of string comparisons in situations where performance is critical, such as when dealing with parameter change events in Engine::process, Coupler will refer to parameters by their integer index in the linear parameter ordering. This should have no implications regarding the ability to add, remove, or reorder a plugin's parameter set, as integer indices will only be used at runtime for performance reasons and will not be treated as stable identifiers across multiple instantiations of a given plugin.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions