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:
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, ¶m))
+ 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, ¶m))
+ 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);