clap

CLAP Audio Plugin API
Log | Files | Refs | README | LICENSE

commit c28994f6959313606897c104e78cef8e653855e0
parent 5d480de905a988f29860556b361d941d724de97a
Author: Alexandre Bique <bique.alexandre@gmail.com>
Date:   Wed,  7 Jan 2015 17:42:24 +0100

thyns: add state serialization

Diffstat:
Mexamples/thyns/plugin.c | 26++++++++++++++++++++++++++
Ainclude/clap/ext/params.c | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/clap/ext/params.h | 18++++++++++++++++++
Ainclude/clap/serialize/serialize.c | 157+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/clap/serialize/serialize.h | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtools/clap-jack-host/clap-jack-host.c | 103++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
6 files changed, 523 insertions(+), 3 deletions(-)

diff --git a/examples/thyns/plugin.c b/examples/thyns/plugin.c @@ -2,6 +2,7 @@ #include <stdio.h> #include <clap/clap.h> #include <clap/ext/params.h> +#include <clap/ext/state.h> #include "thyns.h" @@ -11,6 +12,10 @@ struct thyns_plugin struct clap_plugin plugin; struct clap_host *host; struct clap_plugin_params params; + struct clap_plugin_state state; + + // buffer to save the synthesizer state + uint8_t state_buffer[2048]; }; void @@ -70,6 +75,8 @@ thyns_plugin_extension(struct clap_plugin *plugin, const char *extension) if (!strcmp(extension, CLAP_EXT_PARAMS)) return &p->params; + if (!strcmp(extension, CLAP_EXT_STATE)) + return &p->state; return NULL; } @@ -149,6 +156,23 @@ thyns_params_get(struct clap_plugin *plugin, return false; } +bool +thyns_state_save(struct clap_plugin *plugin, void **buffer, uint32_t *size) +{ + struct thyns_plugin *p = plugin->plugin_data; + + *buffer = p->state_buffer; + *size = sizeof (p->state_buffer); + return clap_plugin_params_save(plugin, *buffer, size); +} + +bool +thyns_state_restore(struct clap_plugin *plugin, const void *buffer, uint32_t size) +{ + clap_plugin_params_restore(plugin, buffer, size); + return true; +} + struct thyns_plugin * thyns_plugin_create(struct clap_host *host, uint32_t sample_rate) @@ -174,6 +198,8 @@ thyns_plugin_create(struct clap_host *host, p->plugin.deactivate = thyns_plugin_deactivate; p->params.count = thyns_params_count; p->params.get = thyns_params_get; + p->state.save = thyns_state_save; + p->state.restore = thyns_state_restore; return p; } diff --git a/include/clap/ext/params.c b/include/clap/ext/params.c @@ -0,0 +1,140 @@ +static inline bool +clap_plugin_params_save(struct clap_plugin *plugin, + uint8_t *buffer, + uint32_t *size) +{ + struct clap_plugin_params *params = plugin->extension(plugin, CLAP_EXT_PARAMS); + if (!params) { + *size = 0; + return true; + } + + struct clap_serializer s; + s.out = buffer; + s.out_end = buffer + *size; + + if (!clap_serializer_dict(&s)) + return false; + + uint32_t count = params->count(plugin); + for (uint32_t i = 0; i < count; ++i) { + struct clap_param param; + + if (!params->get(plugin, i, &param)) + continue; + + if (param.type == CLAP_PARAM_GROUP) + continue; + + clap_serializer_str(&s, param.id, strlen(param.id)); + switch (param.type) { + case CLAP_PARAM_BOOL: + if (!clap_serializer_bool(&s, param.value.b)) + return false; + break; + + case CLAP_PARAM_INT: + case CLAP_PARAM_ENUM: + if (!clap_serializer_int32(&s, param.value.i)) + return false; + break; + + case CLAP_PARAM_FLOAT: + if (!clap_serializer_float(&s, param.value.f)) + return false; + break; + + default: + return false; + } + } + + if (!clap_serializer_end(&s)) + return false; + + *size = s.out - buffer; + return true; +} + +static inline void +clap_plugin_params_restore(struct clap_plugin *plugin, + const uint8_t *buffer, + uint32_t size) +{ + struct clap_plugin_params *params = plugin->extension(plugin, CLAP_EXT_PARAMS); + if (!params) + return; + + // allocate the ids + uint32_t count = params->count(plugin); + char ids[count][CLAP_ID_SIZE]; + memset(ids, 0, sizeof (ids)); + + // fill ids + for (uint32_t i = 0; i < count; ++i) { + struct clap_param param; + if (params->get(plugin, i, &param)) + memcpy(ids[i], param.id, sizeof (param.id)); + } + + struct clap_deserializer d; + d.in = buffer; + d.in_end = buffer + size; + + if (!clap_deserialize(&d) || d.type != CLAP_SERIALIZE_DICT) + return; + + while (true) { + // get param id + if (!clap_deserialize(&d) || d.type != CLAP_SERIALIZE_STR) + return; + + // find param index + uint32_t index; + for (index = 0; index < count; ++index) + if (!strncmp(ids[index], d.s, d.slen)) + break; + + if (index == count) { + // index not found, pull value and continue + clap_deserialize(&d); + continue; + } + + struct clap_event ev; + ev.next = NULL; + ev.type = CLAP_EVENT_PARAM_SET; + ev.steady_time = 0; + ev.param.is_global = true; + ev.param.key = 0; + ev.param.index = index; + + struct clap_process process; + process.inputs = NULL; + process.outputs = NULL; + process.samples_count = 0; + process.steady_time = 0; + process.events = &ev; + + switch (d.type) { + case CLAP_SERIALIZE_BOOL: + ev.param.value.b = d.b; + break; + + case CLAP_SERIALIZE_INT32: + ev.param.value.i = d.i; + break; + + case CLAP_SERIALIZE_FLOAT: + ev.param.value.f = d.f; + break; + + default: + return; + } + + plugin->process(plugin, &process); + } + + return; +} diff --git a/include/clap/ext/params.h b/include/clap/ext/params.h @@ -2,6 +2,7 @@ # define CLAP_EXT_PARAMS_H # include "../clap.h" +# include "../serialize/serialize.h" # define CLAP_EXT_PARAMS "clap/params" @@ -53,4 +54,21 @@ struct clap_plugin_params struct clap_param *param); }; +/* Helper that will serialize the plugin's parameters value into the buffer. + * (*size) must be set to the size of the buffer. + * At return, (*size) contains the number of bytes used and returns true on + * success, the only possible error is that the buffer is too small. + */ +static inline bool +clap_plugin_params_save(struct clap_plugin *plugin, + uint8_t *buffer, + uint32_t *size); + +static inline void +clap_plugin_params_restore(struct clap_plugin *plugin, + const uint8_t *buffer, + uint32_t size); + +# include "params.c" + #endif /* !CLAP_EXT_PARAMS_H */ diff --git a/include/clap/serialize/serialize.c b/include/clap/serialize/serialize.c @@ -0,0 +1,157 @@ +#include <string.h> + +static inline bool +clap_serializer_dict(struct clap_serializer *s) +{ + if (!s->out || s->out + 1 > s->out_end) + return false; + *s->out = CLAP_SERIALIZE_DICT; + ++s->out; + return true; +} + +static inline bool +clap_serializer_array(struct clap_serializer *s) +{ + if (!s->out || s->out + 1 > s->out_end) + return false; + *s->out = CLAP_SERIALIZE_ARRAY; + ++s->out; + return true; +} + +static inline bool +clap_serializer_end(struct clap_serializer *s) +{ + if (!s->out || s->out + 1 > s->out_end) + return false; + *s->out = CLAP_SERIALIZE_END; + ++s->out; + return true; +} + +static inline bool +clap_serializer_bool(struct clap_serializer *s, bool value) +{ + if (!s->out || s->out + 2 > s->out_end) + return false; + s->out[0] = CLAP_SERIALIZE_BOOL; + s->out[1] = value; + s->out += 2; + return true; +} + +static inline void +clap_serializer_int32_priv(struct clap_serializer *s, int32_t value) +{ + s->out[1] = value >> 24; + s->out[2] = value >> 16; + s->out[3] = value >> 8; + s->out[4] = value; +} + +static inline bool +clap_serializer_int32(struct clap_serializer *s, int32_t value) +{ + if (!s->out || s->out + 5 >= s->out_end) + return false; + + *s->out = CLAP_SERIALIZE_INT32; + clap_serializer_int32_priv(s, value); + s->out += 5; + return true; +} + +static inline bool +clap_serializer_float(struct clap_serializer *s, float value) +{ + if (!s->out || s->out + 5 >= s->out_end) + return false; + + union { + float f; + int32_t i; + } u; + + u.f = value; + *s->out = CLAP_SERIALIZE_FLOAT; + clap_serializer_int32_priv(s, u.i); + s->out += 5; + return true; +} + +static inline bool +clap_serializer_str(struct clap_serializer *s, + const char *str, + uint32_t len) +{ + if (!s->out || s->out + 5 + len >= s->out_end) + return false; + + *s->out = CLAP_SERIALIZE_STR; + clap_serializer_int32_priv(s, len); + memcpy(s->out + 5, str, len); + s->out += 5 + len; + return true; +} + +static inline bool +clap_deserialize(struct clap_deserializer *d) +{ + if (!d->in || !d->in_end || d->in >= d->in_end) { + d->type = CLAP_SERIALIZE_EOB; + return false; + } + + d->type = *d->in; + switch (d->type) { + case CLAP_SERIALIZE_DICT: + ++d->in; + return true; + + case CLAP_SERIALIZE_ARRAY: + ++d->in; + return true; + + case CLAP_SERIALIZE_END: + ++d->in; + return true; + + case CLAP_SERIALIZE_BOOL: + if (d->in + 5 > d->in_end) { + d->type = CLAP_SERIALIZE_EOB; + return false; + } + d->b = d->in[1]; + d->in += 2; + return true; + + case CLAP_SERIALIZE_INT32: + case CLAP_SERIALIZE_FLOAT: + if (d->in + 5 > d->in_end) { + d->type = CLAP_SERIALIZE_EOB; + return false; + } + d->i = (d->in[1] << 24) | (d->in[2] << 16) | (d->in[3] << 8) | d->in[4]; + d->in += 5; + return true; + + case CLAP_SERIALIZE_STR: + if (d->in + 5 > d->in_end) { + d->type = CLAP_SERIALIZE_EOB; + return false; + } + d->slen = (d->in[1] << 24) | (d->in[2] << 16) | (d->in[3] << 8) | d->in[4]; + if (d->in + 5 + d->slen > d->in_end) { + d->type = CLAP_SERIALIZE_EOB; + return false; + } + d->s = (const char *)d->in + 5; + d->in = d->in + 5 + d->slen; + return true; + + default: + d->type = CLAP_SERIALIZE_ERROR; + return false; + } +} diff --git a/include/clap/serialize/serialize.h b/include/clap/serialize/serialize.h @@ -0,0 +1,82 @@ +#ifndef CLAP_SERIALIZE_H +# define CLAP_SERIALIZE_H + +# include <stdint.h> + +enum clap_serialize_type +{ + CLAP_SERIALIZE_EOB = 'B', + CLAP_SERIALIZE_ERROR = 'E', + + CLAP_SERIALIZE_DICT = 'd', + CLAP_SERIALIZE_ARRAY = 'a', + CLAP_SERIALIZE_END = 'e', + CLAP_SERIALIZE_STR = 's', + CLAP_SERIALIZE_BOOL = 'b', + CLAP_SERIALIZE_INT32 = 'i', + CLAP_SERIALIZE_FLOAT = 'f', +}; + +struct clap_serializer +{ + /* output buffer */ + uint8_t *out; + uint8_t *out_end; +}; + +/* Begins a dictionary. Returns false on EOB. */ +static inline bool +clap_serializer_dict(struct clap_serializer *s); + +/* Begins an array. Returns false on EOB. */ +static inline bool +clap_serializer_array(struct clap_serializer *s); + +/* Ends an array or dictionary. Returns false on EOB. */ +static inline bool +clap_serializer_end(struct clap_serializer *s); + +/* Pushes a string. Returns false on EOB. */ +static inline bool +clap_serializer_str(struct clap_serializer *s, + const char *str, + uint32_t len); + +/* Pushes an 32 bits integer. Returns false on EOB. */ +static inline bool +clap_serializer_bool(struct clap_serializer *s, bool value); + +/* Pushes an 32 bits integer. Returns false on EOB. */ +static inline bool +clap_serializer_int32(struct clap_serializer *s, int32_t value); + +/* Pushes a float. Returns false on EOB. */ +static inline bool +clap_serializer_float(struct clap_serializer *s, float value); + +struct clap_deserializer +{ + /* input buffer */ + const uint8_t *in; + const uint8_t *in_end; + + /* decoded value */ + enum clap_serialize_type type; + union { + bool b; + int i; + float f; + const char *s; + }; + uint32_t slen; +}; + +/* Decodes one token into *d. + * Returns false if the type is: CLAP_SERIALIZE_{ERROR,EOB}. + */ +static inline bool +clap_deserialize(struct clap_deserializer *d); + +# include "serialize.c" + +#endif /* !CLAP_SERIALIZE_H */ diff --git a/tools/clap-jack-host/clap-jack-host.c b/tools/clap-jack-host/clap-jack-host.c @@ -1,11 +1,17 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> #include <stdlib.h> #include <stdio.h> #include <dlfcn.h> #include <unistd.h> +#include <fcntl.h> +#include <signal.h> #include <clap/clap.h> #include <clap/midi/parser.h> #include <clap/ext/gui.h> +#include <clap/ext/state.h> #include <jack/jack.h> #include <jack/midiport.h> @@ -26,8 +32,16 @@ struct clap_jack_host jack_port_t *input_ports[2]; jack_port_t *output_ports[2]; jack_port_t *midi_in; + + /* config */ + const char *state_path; + + /* state */ + bool quit; }; +struct clap_jack_host *g_app = NULL; + static void deinitialize(struct clap_jack_host *app); static void host_events(struct clap_host *host, @@ -126,13 +140,14 @@ int process(jack_nframes_t nframes, void *arg) void shutdown(void *arg) { - deinitialize(arg); } static bool initialize(struct clap_jack_host *app, const char *plugin_path, uint32_t plugin_index) { + app->quit = false; + /* jack */ jack_status_t jack_status; app->client = jack_client_open("clap-host", JackNullOption, &jack_status, NULL); @@ -204,9 +219,76 @@ fail_jack: return false; } +static void load_state(struct clap_jack_host *app) +{ + struct stat st; + struct clap_plugin_state *state = app->plugin->extension( + app->plugin, CLAP_EXT_STATE); + + if (!state) + return; + + if (!app->state_path) + return; + + int fd = open(app->state_path, O_RDONLY); + if (fd < 0) + return; + + if (fstat(fd, &st)) { + close(fd); + return; + } + + void *mem = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (mem == MAP_FAILED) { + close(fd); + return; + } + + state->restore(app->plugin, mem, st.st_size); + + munmap(mem, st.st_size); + close(fd); +} + +static void save_state(struct clap_jack_host *app) +{ + struct clap_plugin_state *state = app->plugin->extension( + app->plugin, CLAP_EXT_STATE); + + if (!state) { + fprintf(stdout, "no state extension\n"); + return; + } + + if (!app->state_path) { + fprintf(stdout, "no state path\n"); + return; + } + + void *buffer = NULL; + uint32_t size = 0; + if (!state->save(app->plugin, &buffer, &size)) { + fprintf(stdout, "failed to save the state\n"); + return; + } + + int fd = open(app->state_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + fprintf(stdout, "open(%s): %m\n", app->state_path); + return; + } + + write(fd, buffer, size); + close(fd); +} + static void deinitialize(struct clap_jack_host *app) { /* clap */ + save_state(app); + app->plugin->deactivate(app->plugin); app->plugin->destroy(app->plugin); dlclose(app->library_handle); @@ -214,12 +296,21 @@ static void deinitialize(struct clap_jack_host *app) jack_client_close(app->client); } +void sig_int(int sig) +{ + g_app->quit = true; +} + int main(int argc, char **argv) { struct clap_jack_host app; - if (argc != 3) { - fprintf(stderr, "usage: %s plugin.so index\n", argv[0]); + g_app = &app; + + signal(SIGINT, sig_int); + + if (argc < 3) { + fprintf(stderr, "usage: %s plugin.so index [state]\n", argv[0]); return 2; } @@ -231,6 +322,12 @@ int main(int argc, char **argv) return 1; } + if (argc == 4) { + app.state_path = argv[3]; + load_state(&app); + } else + app.state_path = NULL; + struct clap_plugin_gui *gui = app.plugin->extension(app.plugin, CLAP_EXT_GUI); if (gui) gui->open(app.plugin);