diff options
Diffstat (limited to 'plugins/eg-sampler.lv2/sampler.c')
-rw-r--r-- | plugins/eg-sampler.lv2/sampler.c | 239 |
1 files changed, 119 insertions, 120 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); } } |