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

CLAP 1.2.3 #432

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
17 changes: 17 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
# Changes in 1.2.3

## New draft extensions

* [scratch-memory](include/clap/ext/draft/scratch-memory.h): host provided scratch memory within the process call
* [location](include/clap/ext/draft/location.h): better info about the plugin location within the project

## Documention

* [events.h](include/clap/events.h): clarify sysex lifetime
* [host.h](include/clap/host.h): clarify `request_callback()`
* [ambisonic.h](include/clap/ext/ambisonic.h): remove bad comment

## Fixes

* [plugin-template.c](src/plugin-template.c): fix bad assertion

# Changes in 1.2.2

* [thread-check.h](include/clap/ext/thread-check.h): expand the thread-doc to clarify and expand realtime
Expand Down
2 changes: 2 additions & 0 deletions include/clap/all.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@
#include "ext/draft/resource-directory.h"
#include "ext/draft/transport-control.h"
#include "ext/draft/triggers.h"
#include "ext/draft/location.h"
#include "ext/draft/tuning.h"
#include "ext/draft/undo.h"
#include "ext/draft/scratch-memory.h"
2 changes: 2 additions & 0 deletions include/clap/color.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ typedef struct clap_color {
uint8_t blue;
} clap_color_t;

static const CLAP_CONSTEXPR clap_color_t CLAP_COLOR_TRANSPARENT = { 0, 0, 0, 0 };

#ifdef __cplusplus
}
#endif
31 changes: 29 additions & 2 deletions include/clap/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ extern "C" {
#endif

// event header
// must be the first attribute of the event
// All clap events start with an event header to determine the overall
// size of the event and its type and space (a namespacing for types).
// clap_event objects are contiguous regions of memory which can be copied
// with a memcpy of `size` bytes starting at the top of the header. As
// such, be very careful when desiginig clap events with internal pointers
// and other non-value-types to consider the lifetime of those members.
typedef struct clap_event_header {
uint32_t size; // event size including this header, eg: sizeof (clap_event_note)
uint32_t time; // sample offset within the buffer for this event
Expand Down Expand Up @@ -266,6 +271,12 @@ enum clap_transport_flags {
CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL = 1 << 7,
};

// clap_event_transport provides song position, tempo, and similar information
// from the host to the plugin. There are two ways a host communicates these values.
// In the `clap_process` structure sent to each processing block, the host must
// provide a transport structure which indicates the available information at the
// start of the block. If the host provides sample-accurate tempo or transport changes,
// it can also provide subsequent inter-block transport updates by delivering a new event.
typedef struct clap_event_transport {
clap_event_header_t header;

Expand Down Expand Up @@ -297,11 +308,27 @@ typedef struct clap_event_midi {
uint8_t data[3];
} clap_event_midi_t;

// clap_event_midi_sysex contains a pointer to a sysex contents buffer.
// The lifetime of this buffer is (from host->plugin) only the process
// call in which the event is delivered or (from plugin->host) only the
// duration of a try_push call.
//
// Since `clap_output_events.try_push` requires hosts to make a copy of
// an event, host implementers receiving sysex messages from plugins need
// to take care to both copy the event (so header, size, etc...) but
// also memcpy the contents of the sysex pointer to host-owned memory, and
// not just copy the data pointer.
//
// Similarly plugins retaining the sysex outside the lifetime of a single
// process call must copy the sysex buffer to plugin-owned memory.
//
// As a consequence, the data structure pointed to by the sysex buffer
// must be contiguous and copyable with `memcpy` of `size` bytes.
typedef struct clap_event_midi_sysex {
clap_event_header_t header;

uint16_t port_index;
const uint8_t *buffer; // midi buffer
const uint8_t *buffer; // midi buffer. See lifetime comment above.
uint32_t size;
} clap_event_midi_sysex_t;

Expand Down
3 changes: 0 additions & 3 deletions include/clap/ext/ambisonic.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ typedef struct clap_plugin_ambisonic {
const clap_ambisonic_config_t *config);

// Returns true on success
//
// config_id: the configuration id, see clap_plugin_audio_ports_config.
// If config_id is CLAP_INVALID_ID, then this function queries the current port info.
// [main-thread]
bool(CLAP_ABI *get_config)(const clap_plugin_t *plugin,
bool is_input,
Expand Down
73 changes: 73 additions & 0 deletions include/clap/ext/draft/location.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#pragma once

#include "../../color.h"
#include "../../plugin.h"
#include "../../string-sizes.h"

// This extension allows a host to tell the plugin more about its position
// within a project or session.

static CLAP_CONSTEXPR const char CLAP_EXT_LOCATION[] = "clap.location/1";

#ifdef __cplusplus
extern "C" {
#endif

enum {
// Represents a document/project/session.
CLAP_PLUGIN_LOCATION_PROJECT = 1,
Comment on lines +17 to +18
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be required to be the first element in the array passed to set_location, if it is provided?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think it should, because if your host is just a device chain, it doesn't have a track or a project.


// Represents a group of tracks.
// It can contain both track groups, tracks and devices (post processing).
abique marked this conversation as resolved.
Show resolved Hide resolved
// The first device within a track group has the index of
// the last track or track group within this group + 1.
Comment on lines +22 to +23
Copy link
Contributor

Choose a reason for hiding this comment

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

Specifically the first device, or should this be the first child (whether that is track group, track, or device?).

What is the purpose of setting the index to that value? Is it to tell the plugin the range of location elements that are children (direct or not) of the parent element? That should probably be made clear.

Is the first child required to immediately follow its parent element in the clap_plugin_location_element_t *path array in set_location?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, it could be simplified.
We could say that index starts at 0 for tracks and then 0 again for devices.

This is the model:

track_group
 `- track0 -|
 `- track1 -| -> (+) -> device0 -> device1 -> ... 
 `- ...    -|

CLAP_PLUGIN_LOCATION_TRACK_GROUP = 2,

// Represents a single tracks. It contains devices (serial).
abique marked this conversation as resolved.
Show resolved Hide resolved
CLAP_PLUGIN_LOCATION_TRACK = 3,

// Represents a single device.
// It contains other nested device chains.
abique marked this conversation as resolved.
Show resolved Hide resolved
CLAP_PLUGIN_LOCATION_DEVICE = 4,

// Represents a nested device chain (serial).
// Its parent must be a device.
// It contains other devices.
CLAP_PLUGIN_LOCATION_NESTED_DEVICE_CHAIN = 5,
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason why nested device chains & devices function differently from how track groups & tracks function? That is, why do device chains need to be a child of a device rather than function as a "device group" which can have child devices?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A device chain is a serial list of devices.
A device can contain multiple device chains, which maybe use for parallel processing.
There are similarities between tracks and device chain. Though there's a different semantic, which the plugin can use for display. Think Fabfilter Pro Q3, with sidechain.

};

typedef struct clap_plugin_location_element {
// Kind of the element, must be one of the CLAP_PLUGIN_LOCATION_* values.
uint32_t kind;

// Index within the parent element.
// Set to 0 if irrelevant.
uint32_t index;
Comment on lines +44 to +46
Copy link
Contributor

@messmerd messmerd Nov 24, 2024

Choose a reason for hiding this comment

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

So index does not refer to the array index of this element's parent element within the array passed to set_location?

I could be completely wrong, but it seems like a simpler way of flattening the tree would be to specify that the order of elements in set_location's path array is the same order that elements would be visited in a pre-order depth-first traversal of the tree. Then each index would be the array index of the element's direct parent. And since the root element does not have a parent, its index could be the index of the plugin device itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The index here, refers to the position within the track/device_chain/...


// Internal ID of the element.
// This is not intended for display to the user,
// but rather to give the host a potential quick way for lookups.
char id[CLAP_PATH_SIZE];

// User friendly name of the element.
char name[CLAP_NAME_SIZE];

// Color for this element, should be CLAP_COLOR_TRANSPARENT if no color is
// used for this element.
clap_color_t color;
} clap_plugin_location_element_t;

typedef struct clap_plugin_location {
// Called by the host when the location of the plugin instance changes.
//
// The last item in this array always refers to the device itself, and as
// such is expected to be of kind CLAP_PLUGIN_LOCATION_DEVICE.
// [main-thread]
void(CLAP_ABI *set_location)(clap_plugin_t *plugin,
clap_plugin_location_element_t *path,
Copy link
Contributor

Choose a reason for hiding this comment

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

If I understand correctly, this array is a flattened tree structure?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes it is an array, which describes the path toward the current plugin instance.

uint32_t num_elements);
} clap_plugin_location_t;

#ifdef __cplusplus
}
#endif
91 changes: 91 additions & 0 deletions include/clap/ext/draft/scratch-memory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#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 its content 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.
//
// 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";

#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".
//
// 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 `reserve()` multiple times, then the
// final call determines the actual amount of scratch memory
// 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()`.
//
// 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
// 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 *reserve)(const clap_host_t *host, uint32_t scratch_size_bytes);
Copy link

@p--b p--b Nov 19, 2024

Choose a reason for hiding this comment

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

question: Would you accept a PR that added a concurrent_buffers parameter as discussed in #432?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think so.

It'd need to explain what happens if the plugins performs more access to the buffer than initially requested.
It'd need to state that even if concurrent_buffers == 1, you may still get a thread local pointer, so if you want the same pointer on all threads, you need to fetch it before calling into the thread pool.

Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@p--b ETA?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@p--b will you make that PR?

Copy link

Choose a reason for hiding this comment

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

Hey, apologies for the delay. Yes, I can have something to you in the next 24 hours.

Copy link

Choose a reason for hiding this comment

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

#436 :)


// 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 reserved size. If the host returned "false"
// when scratch memory was requested, then this method must not
// 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
// 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.
//
// The provided memory is owned by the host, so the plugin must not
// free the memory.
//
// [audio-thread]
void*(CLAP_ABI *access)(const clap_host_t *host);
} clap_host_scratch_memory_t;

#ifdef __cplusplus
}
#endif
11 changes: 9 additions & 2 deletions include/clap/ext/gui.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
/// Embedding the window gives more control to the host, and feels more integrated.
/// Floating window are sometimes the only option due to technical limitations.
///
/// The Embedding protocol is by far the most common, supported by all hosts to date,
/// and a plugin author should support at least that case.
///
/// Showing the GUI works as follow:
/// 1. clap_plugin_gui->is_api_supported(), check what can work
/// 2. clap_plugin_gui->create(), allocates gui resources
Expand Down Expand Up @@ -85,7 +88,10 @@ typedef struct clap_gui_resize_hints {
bool can_resize_horizontally;
bool can_resize_vertically;

// only if can resize horizontally and vertically
// if both horizontal and vertical resize are available, do we preserve the
// aspect ratio, and if so, what is the width x height aspect ratio to preserve.
// These flags are unused if can_resize_horizontally or vertically are false,
// and ratios are unused if preserve is false.
bool preserve_aspect_ratio;
uint32_t aspect_ratio_width;
uint32_t aspect_ratio_height;
Expand All @@ -94,7 +100,8 @@ typedef struct clap_gui_resize_hints {
// Size (width, height) is in pixels; the corresponding windowing system extension is
// responsible for defining if it is physical pixels or logical pixels.
typedef struct clap_plugin_gui {
// Returns true if the requested gui api is supported
// Returns true if the requested gui api is supported, either in floating (plugin-created)
// or non-floating (embedded) mode.
// [main-thread]
bool(CLAP_ABI *is_api_supported)(const clap_plugin_t *plugin, const char *api, bool is_floating);

Expand Down
6 changes: 3 additions & 3 deletions include/clap/ext/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,9 @@ typedef struct clap_param_info {
// '/' will be used as a separator to show a tree-like structure.
char module[CLAP_PATH_SIZE];

double min_value; // Minimum plain value
double max_value; // Maximum plain value
double default_value; // Default plain value
double min_value; // Minimum plain value. Must be finite (`std::isfinite` true)
double max_value; // Maximum plain value. Must be finite
double default_value; // Default plain value. Must be in [min, max] range.
} clap_param_info_t;

typedef struct clap_plugin_params {
Expand Down
7 changes: 7 additions & 0 deletions include/clap/host.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ 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 within 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;
Expand Down
1 change: 1 addition & 0 deletions include/clap/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion include/clap/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ typedef struct clap_version {

#define CLAP_VERSION_MAJOR 1
#define CLAP_VERSION_MINOR 2
#define CLAP_VERSION_REVISION 2
#define CLAP_VERSION_REVISION 3

#define CLAP_VERSION_INIT \
{ (uint32_t)CLAP_VERSION_MAJOR, (uint32_t)CLAP_VERSION_MINOR, (uint32_t)CLAP_VERSION_REVISION }
Expand Down
2 changes: 1 addition & 1 deletion src/plugin-template.c
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ static void entry_deinit_guard(void) {
#endif

const int cnt = --g_entry_init_counter;
assert(cnt > 0);
assert(cnt >= 0);

bool succeed = true;
if (cnt == 0)
Expand Down
Loading