aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2012-02-18 01:15:07 +0000
committerDavid Robillard <d@drobilla.net>2012-02-18 01:15:07 +0000
commitc6a68dde29f61d6f67439635a33413a8c63f3091 (patch)
tree8839bd00914972168292746204bab1498ab910bc
parent9db204f703d338e7b6434fccdedb1050c9124bf6 (diff)
downloadlv2-c6a68dde29f61d6f67439635a33413a8c63f3091.tar.xz
Send notifications to the UI and display loaded sample path.
-rw-r--r--plugins/eg-sampler.lv2/sampler.c239
-rw-r--r--plugins/eg-sampler.lv2/sampler_ui.c40
-rw-r--r--plugins/eg-sampler.lv2/uris.h70
3 files changed, 209 insertions, 140 deletions
diff --git a/plugins/eg-sampler.lv2/sampler.c b/plugins/eg-sampler.lv2/sampler.c
index 82a374c..bf288d7 100644
--- a/plugins/eg-sampler.lv2/sampler.c
+++ b/plugins/eg-sampler.lv2/sampler.c
@@ -40,6 +40,7 @@
#include <sndfile.h>
#include "lv2/lv2plug.in/ns/ext/atom/atom-helpers.h"
+#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
#include "lv2/lv2plug.in/ns/ext/message/message.h"
#include "lv2/lv2plug.in/ns/ext/state/state.h"
#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
@@ -62,15 +63,20 @@ enum {
static const char* default_sample_file = "monosample.wav";
typedef struct {
- SF_INFO info;
- float* data;
- char* path;
+ SF_INFO info; /**< Info about sample from sndfile */
+ float* data; /**< Sample data in float */
+ char* uri; /**< URI of file */
+ const char* path; /**< Path of file (pointer into uri) */
+ size_t uri_len; /**< Length of uri. */
} Sample;
typedef struct {
/* Features */
LV2_URID_Map* map;
+ /* Forge for creating atoms */
+ LV2_Atom_Forge forge;
+
/* Worker thread, communication, and sync */
ZixThread worker_thread;
ZixSem signal;
@@ -99,20 +105,52 @@ typedef struct {
* This is only used internally via ringbuffers, since it is not POD and
* therefore not strictly an Atom.
*/
-typedef struct
-{
+typedef struct {
LV2_Atom atom;
Sample* sample;
} SampleMessage;
+static bool
+parse_file_uri(const char* uri,
+ const char** host, size_t* host_len,
+ const char** path, size_t* path_len)
+{
+ if (strncmp(uri, "file://", strlen("file://"))) {
+ return false;
+ }
+
+ *host = uri + strlen("file://");
+ const char* host_end = *host;
+ for (; *host_end && *host_end != '/'; ++host_end) {}
+
+ *host_len = host_end - *host;
+ *path = host_end;
+ *path_len = (uri + strlen(uri)) - host_end;
+
+ return true;
+}
+
static Sample*
-load_sample(Sampler* plugin, const char* path, uint32_t path_len)
+load_sample(Sampler* plugin, const char* uri)
{
+ const size_t uri_len = strlen(uri);
+ const char* host = NULL;
+ const char* path = NULL;
+ size_t host_len = 0;
+ size_t path_len = 0;
+ if (!parse_file_uri(uri, &host, &host_len, &path, &path_len)) {
+ fprintf(stderr, "Request to load bad file URI %s\n", uri);
+ return NULL;
+ }
+
+ /* Probably should check if the host is local here, but we'll just
+ blissfully attempt to load the path on this machine... */
+
printf("Loading sample %s\n", path);
Sample* const sample = (Sample*)malloc(sizeof(Sample));
SF_INFO* const info = &sample->info;
SNDFILE* const sndfile = sf_open(path, SFM_READ, info);
-
+
if (!sndfile || !info->frames || (info->channels != 1)) {
fprintf(stderr, "failed to open sample '%s'.\n", path);
free(sample);
@@ -130,94 +168,27 @@ load_sample(Sampler* plugin, const char* path, uint32_t path_len)
sf_close(sndfile);
/* Fill sample struct and return it. */
- sample->data = data;
- sample->path = (char*)malloc(path_len + 1);
- memcpy(sample->path, path, path_len + 1);
+ sample->data = data;
+ sample->uri = (char*)malloc(uri_len + 1);
+ sample->path = sample->uri + (path - uri);
+ sample->uri_len = uri_len;
+ memcpy(sample->uri, uri, uri_len + 1);
return sample;
}
static bool
-is_object_type(Sampler* plugin, LV2_URID type)
-{
- return type == plugin->uris.atom_Resource
- || type == plugin->uris.atom_Blank;
-}
-
-static bool
-parse_file_uri(const char* uri,
- const char** host, size_t* host_len,
- const char** path, size_t* path_len)
-{
- if (strncmp(uri, "file://", strlen("file://"))) {
- return false;
- }
-
- *host = uri + strlen("file://");
- const char* host_end = *host;
- for (; *host_end && *host_end != '/'; ++host_end) {}
-
- *host_len = host_end - *host;
- *path = host_end;
- *path_len = (uri + strlen(uri)) - host_end;
-
- return true;
-}
-
-static bool
handle_set_message(Sampler* plugin,
const LV2_Atom_Object* obj)
{
- if (obj->type != plugin->uris.msg_Set) {
- fprintf(stderr, "Ignoring unknown message type %d\n", obj->type);
- return false;
- }
-
- /* Message should look like this:
- * [
- * a msg:Set ;
- * msg:body [
- * eg-sampler:file <file://hal/home/me/foo.wav> ;
- * ] ;
- * ]
- */
-
- /* Get body of message. */
- const LV2_Atom_Object* body = NULL;
- lv2_object_getv(obj, plugin->uris.msg_body, &body, 0);
- if (!body) {
- fprintf(stderr, "Malformed set message has no body.\n");
- return false;
- }
- if (!is_object_type(plugin, body->atom.type)) {
- fprintf(stderr, "Malformed set message has non-object body.\n");
- return false;
- }
-
- /* Get file URI from body. */
- const LV2_Atom* file_uri = NULL;
- lv2_object_getv(body, plugin->uris.eg_file, &file_uri, 0);
+ /* Get file URI from message */
+ const LV2_Atom* file_uri = get_msg_file_uri(&plugin->uris, obj);
if (!file_uri) {
- fprintf(stderr, "Ignored set message with no file URI.\n");
return false;
}
/* Load sample. */
- const char* uri = (const char*)LV2_ATOM_BODY(file_uri);
- const char* host = NULL;
- const char* path = NULL;
- size_t host_len = 0;
- size_t path_len = 0;
- if (!parse_file_uri(uri, &host, &host_len, &path, &path_len)) {
- fprintf(stderr, "Request to load bad file URI %s\n", uri);
- return false;
- }
-
- /* Probably should check if the host is local here, but we'll just
- blissfully attempt to load the path on this machine... */
-
- Sample* sample = load_sample(plugin, path, path_len);
-
+ Sample* sample = load_sample(plugin, LV2_ATOM_BODY(file_uri));
if (sample) {
/* Loaded sample, send it to run() to be applied. */
const SampleMessage msg = {
@@ -231,7 +202,16 @@ handle_set_message(Sampler* plugin,
return true;
}
-
+
+void
+free_sample(Sample* sample)
+{
+ fprintf(stderr, "Freeing %s\n", sample->uri);
+ free(sample->uri);
+ free(sample->data);
+ free(sample);
+}
+
void*
worker_thread_main(void* arg)
{
@@ -251,10 +231,7 @@ worker_thread_main(void* arg)
if (obj->type == plugin->uris.eg_freeSample) {
/* Free old sample */
SampleMessage* msg = (SampleMessage*)obj;
- fprintf(stderr, "Freeing %s\n", msg->sample->path);
- free(msg->sample->path);
- free(msg->sample->data);
- free(msg->sample);
+ free_sample(msg->sample);
} else {
/* Handle set message (load sample). */
handle_set_message(plugin, (LV2_Atom_Object*)obj);
@@ -305,6 +282,23 @@ instantiate(const LV2_Descriptor* descriptor,
memset(plugin->sample, 0, sizeof(Sample));
memset(&plugin->uris, 0, sizeof(plugin->uris));
+ /* Scan host features for URID map */
+ LV2_URID_Map* map = NULL;
+ for (int i = 0; features[i]; ++i) {
+ if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) {
+ map = (LV2_URID_Map*)features[i]->data;
+ }
+ }
+ if (!map) {
+ fprintf(stderr, "Host does not support urid:map.\n");
+ goto fail;
+ }
+
+ /* Map URIS and initialise forge */
+ plugin->map = map;
+ map_sampler_uris(plugin->map, &plugin->uris);
+ lv2_atom_forge_init(&plugin->forge, plugin->map);
+
/* Create signal for waking up worker thread */
if (zix_sem_init(&plugin->signal, 0)) {
fprintf(stderr, "Could not initialize semaphore.\n");
@@ -326,30 +320,13 @@ instantiate(const LV2_Descriptor* descriptor,
zix_ring_mlock(plugin->to_worker);
zix_ring_mlock(plugin->from_worker);
- /* Scan host features for URID map */
- LV2_URID_Map* map = NULL;
- for (int i = 0; features[i]; ++i) {
- if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) {
- map = (LV2_URID_Map*)features[i]->data;
- }
- }
-
- if (!map) {
- fprintf(stderr, "Host does not support urid:map.\n");
- goto fail;
- }
-
- plugin->map = map;
- map_sampler_uris(plugin->map, &plugin->uris);
-
/* Load the default sample file */
- const size_t path_len = strlen(path);
- const size_t sample_file_len = strlen(default_sample_file);
- const size_t len = path_len + sample_file_len;
- char* sample_path = (char*)malloc(len + 1);
- memcpy(sample_path, path, path_len);
- memcpy(sample_path + path_len, default_sample_file, sample_file_len + 1);
- plugin->sample = load_sample(plugin, sample_path, len);
+ const size_t path_len = strlen(path);
+ const size_t file_len = strlen(default_sample_file);
+ const size_t len = strlen("file://") + path_len + file_len;
+ char* sample_uri = (char*)malloc(len + 1);
+ snprintf(sample_uri, len + 1, "file://%s%s", path, default_sample_file);
+ plugin->sample = load_sample(plugin, sample_uri);
return (LV2_Handle)plugin;
@@ -369,11 +346,8 @@ cleanup(LV2_Handle instance)
zix_sem_destroy(&plugin->signal);
zix_ring_free(plugin->to_worker);
zix_ring_free(plugin->from_worker);
-
- free(plugin->sample->data);
- free(plugin->sample->path);
- free(plugin->sample);
- free(instance);
+ free_sample(plugin->sample);
+ free(plugin);
}
static void
@@ -395,7 +369,7 @@ run(LV2_Handle instance,
plugin->frame = 0;
plugin->play = true;
}
- } else if (is_object_type(plugin, ev->body.type)) {
+ } else if (is_object_type(&plugin->uris, ev->body.type)) {
const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;
if (obj->type == plugin->uris.msg_Set) {
/* Received a set message, send it to the worker thread. */
@@ -438,12 +412,19 @@ run(LV2_Handle instance,
output[pos] = 0.0f;
}
+ /* Set up forge to write directly to notify output port buffer */
+ LV2_Atom* seq = plugin->notify_port->data;
+ lv2_atom_forge_set_buffer(
+ &plugin->forge,
+ LV2_ATOM_CONTENTS(LV2_Atom_Sequence, seq),
+ plugin->notify_port->capacity);
+
/* Read messages from worker thread */
SampleMessage m;
const uint32_t msize = lv2_atom_pad_size(sizeof(m));
while (zix_ring_read(plugin->from_worker, &m, msize) == msize) {
if (m.atom.type == plugin->uris.eg_applySample) {
- /** Send a message to the worker to free the current sample */
+ /* Send a message to the worker to free the current sample */
SampleMessage free_msg = {
{ plugin->uris.eg_freeSample, sizeof(plugin->sample) },
plugin->sample
@@ -453,8 +434,26 @@ run(LV2_Handle instance,
lv2_atom_pad_size(sizeof(free_msg)));
zix_sem_post(&plugin->signal);
- /** Install the new sample */
+ /* Install the new sample */
plugin->sample = m.sample;
+
+ /* Send a notification that we're using a new sample. */
+
+ lv2_atom_forge_audio_time(&plugin->forge, seq, 0, 0);
+
+ LV2_Atom* set = (LV2_Atom*)lv2_atom_forge_blank(
+ &plugin->forge, NULL, 0, plugin->uris.msg_Set);
+
+ lv2_atom_forge_property_head(&plugin->forge, set, plugin->uris.msg_body, 0);
+ LV2_Atom* body = (LV2_Atom*)lv2_atom_forge_blank(&plugin->forge, set, 0, 0);
+
+ lv2_atom_forge_property_head(&plugin->forge, body, plugin->uris.eg_file, 0);
+ lv2_atom_forge_uri(&plugin->forge, set,
+ (const uint8_t*)plugin->sample->uri,
+ plugin->sample->uri_len);
+
+ set->size += body->size;
+ seq->size += lv2_atom_total_size(set);
} else {
fprintf(stderr, "Unknown message from worker\n");
}
@@ -510,8 +509,8 @@ restore(LV2_Handle instance,
if (value) {
const char* path = (const char*)value;
printf("Restoring file %s\n", path);
- // FIXME: leak?
- plugin->sample = load_sample(plugin, path, size - 1);
+ free_sample(plugin->sample);
+ plugin->sample = load_sample(plugin, path);
}
}
diff --git a/plugins/eg-sampler.lv2/sampler_ui.c b/plugins/eg-sampler.lv2/sampler_ui.c
index ad28a0f..179c588 100644
--- a/plugins/eg-sampler.lv2/sampler_ui.c
+++ b/plugins/eg-sampler.lv2/sampler_ui.c
@@ -43,15 +43,11 @@ typedef struct {
LV2UI_Write_Function write;
LV2UI_Controller controller;
- GtkWidget* button;
+ GtkWidget* box;
+ GtkWidget* button;
+ GtkWidget* label;
} SamplerUI;
-static LV2_URID
-uri_to_id(SamplerUI* ui, const char* uri)
-{
- return ui->map->map(ui->map->handle, uri);
-}
-
static void
on_load_clicked(GtkWidget* widget,
void* handle)
@@ -108,11 +104,10 @@ on_load_clicked(GtkWidget* widget,
lv2_atom_forge_property_head(&ui->forge, body, ui->uris.eg_file, 0);
lv2_atom_forge_uri(&ui->forge, set, (const uint8_t*)file_uri, file_uri_len);
- lv2_atom_forge_property_head(&ui->forge, body, ui->uris.msg_body, 0);
set->size += body->size;
ui->write(ui->controller, 0, sizeof(LV2_Atom) + set->size,
- uri_to_id(ui, NS_ATOM "atomTransfer"),
+ ui->uris.atom_eventTransfer,
set);
g_free(filename);
@@ -132,7 +127,9 @@ instantiate(const LV2UI_Descriptor* descriptor,
ui->map = NULL;
ui->write = write_function;
ui->controller = controller;
+ ui->box = NULL;
ui->button = NULL;
+ ui->label = NULL;
*widget = NULL;
@@ -152,12 +149,16 @@ instantiate(const LV2UI_Descriptor* descriptor,
lv2_atom_forge_init(&ui->forge, ui->map);
+ ui->box = gtk_hbox_new(FALSE, 4);
+ ui->label = gtk_label_new("?");
ui->button = gtk_button_new_with_label("Load Sample");
+ gtk_box_pack_start(GTK_BOX(ui->box), ui->label, TRUE, TRUE, 4);
+ gtk_box_pack_start(GTK_BOX(ui->box), ui->button, FALSE, TRUE, 4);
g_signal_connect(ui->button, "clicked",
G_CALLBACK(on_load_clicked),
ui);
- *widget = ui->button;
+ *widget = ui->box;
return ui;
}
@@ -177,6 +178,25 @@ port_event(LV2UI_Handle handle,
uint32_t format,
const void* buffer)
{
+ SamplerUI* ui = (SamplerUI*)handle;
+ if (format == ui->uris.atom_eventTransfer) {
+ LV2_Atom* atom = (LV2_Atom*)buffer;
+ if (atom->type == ui->uris.atom_Blank) {
+ LV2_Atom_Object* obj = (LV2_Atom_Object*)atom;
+ const LV2_Atom* file_uri = get_msg_file_uri(&ui->uris, obj);
+ if (!file_uri) {
+ fprintf(stderr, "Unknown message sent to UI.\n");
+ return;
+ }
+
+ const char* uri = (const char*)LV2_ATOM_BODY(file_uri);
+ gtk_label_set_text(GTK_LABEL(ui->label), uri);
+ } else {
+ fprintf(stderr, "Unknown message type.\n");
+ }
+ } else {
+ fprintf(stderr, "Unknown format.\n");
+ }
}
const void*
diff --git a/plugins/eg-sampler.lv2/uris.h b/plugins/eg-sampler.lv2/uris.h
index 8c8801a..6584c13 100644
--- a/plugins/eg-sampler.lv2/uris.h
+++ b/plugins/eg-sampler.lv2/uris.h
@@ -32,9 +32,10 @@
typedef struct {
LV2_URID atom_Blank;
LV2_URID atom_Resource;
+ LV2_URID atom_eventTransfer;
LV2_URID eg_applySample;
- LV2_URID eg_freeSample;
LV2_URID eg_file;
+ LV2_URID eg_freeSample;
LV2_URID midi_Event;
LV2_URID msg_Set;
LV2_URID msg_body;
@@ -44,15 +45,64 @@ typedef struct {
static inline void
map_sampler_uris(LV2_URID_Map* map, SamplerURIs* uris)
{
- uris->atom_Blank = map->map(map->handle, NS_ATOM "Blank");
- uris->atom_Resource = map->map(map->handle, NS_ATOM "Resource");
- uris->eg_applySample = map->map(map->handle, APPLY_SAMPLE_URI);
- uris->eg_file = map->map(map->handle, FILE_URI);
- uris->eg_freeSample = map->map(map->handle, FREE_SAMPLE_URI);
- uris->midi_Event = map->map(map->handle, MIDI_EVENT_URI);
- uris->msg_Set = map->map(map->handle, LV2_MESSAGE_Set);
- uris->msg_body = map->map(map->handle, LV2_MESSAGE_body);
- uris->state_Path = map->map(map->handle, LV2_STATE_PATH_URI);
+ uris->atom_Blank = map->map(map->handle, NS_ATOM "Blank");
+ uris->atom_Resource = map->map(map->handle, NS_ATOM "Resource");
+ uris->atom_eventTransfer = map->map(map->handle, NS_ATOM "eventTransfer");
+ uris->eg_applySample = map->map(map->handle, APPLY_SAMPLE_URI);
+ uris->eg_file = map->map(map->handle, FILE_URI);
+ uris->eg_freeSample = map->map(map->handle, FREE_SAMPLE_URI);
+ uris->midi_Event = map->map(map->handle, MIDI_EVENT_URI);
+ uris->msg_Set = map->map(map->handle, LV2_MESSAGE_Set);
+ uris->msg_body = map->map(map->handle, LV2_MESSAGE_body);
+ uris->state_Path = map->map(map->handle, LV2_STATE_PATH_URI);
+}
+
+static inline bool
+is_object_type(const SamplerURIs* uris, LV2_URID type)
+{
+ return type == uris->atom_Resource
+ || type == uris->atom_Blank;
+}
+
+static inline const LV2_Atom*
+get_msg_file_uri(const SamplerURIs* uris,
+ const LV2_Atom_Object* obj)
+{
+ /* Message should look like this:
+ * [
+ * a msg:Set ;
+ * msg:body [
+ * eg-sampler:file <file://hal/home/me/foo.wav> ;
+ * ] ;
+ * ]
+ */
+
+ if (obj->type != uris->msg_Set) {
+ fprintf(stderr, "Ignoring unknown message type %d\n", obj->type);
+ return NULL;
+ }
+
+ /* Get body of message. */
+ const LV2_Atom_Object* body = NULL;
+ lv2_object_getv(obj, uris->msg_body, &body, 0);
+ if (!body) {
+ fprintf(stderr, "Malformed set message has no body.\n");
+ return NULL;
+ }
+ if (!is_object_type(uris, body->atom.type)) {
+ fprintf(stderr, "Malformed set message has non-object body.\n");
+ return NULL;
+ }
+
+ /* Get file URI from body. */
+ const LV2_Atom* file_uri = NULL;
+ lv2_object_getv(body, uris->eg_file, &file_uri, 0);
+ if (!file_uri) {
+ fprintf(stderr, "Ignored set message with no file URI.\n");
+ return NULL;
+ }
+
+ return file_uri;
}
#endif /* SAMPLER_URIS_H */