diff options
Diffstat (limited to 'plugins/eg-sampler.lv2/sampler.c')
-rw-r--r-- | plugins/eg-sampler.lv2/sampler.c | 146 |
1 files changed, 83 insertions, 63 deletions
diff --git a/plugins/eg-sampler.lv2/sampler.c b/plugins/eg-sampler.lv2/sampler.c index 1fd8c0e..5cc3c96 100644 --- a/plugins/eg-sampler.lv2/sampler.c +++ b/plugins/eg-sampler.lv2/sampler.c @@ -38,7 +38,8 @@ #include "lv2/lv2plug.in/ns/lv2core/lv2.h" #include "lv2/lv2plug.in/ns/lv2core/lv2_util.h" -#include "./uris.h" +#include "uris.h" +#include "atom_sink.h" enum { SAMPLER_CONTROL = 0, @@ -46,8 +47,6 @@ enum { SAMPLER_OUT = 2 }; -static const char* default_sample_file = "click.wav"; - typedef struct { SF_INFO info; // Info about sample from sndfile float* data; // Sample data in float @@ -64,10 +63,6 @@ typedef struct { // Forge for creating atoms LV2_Atom_Forge forge; - // Sample - Sample* sample; - bool sample_changed; - // Ports const LV2_Atom_Sequence* control_port; LV2_Atom_Sequence* notify_port; @@ -79,13 +74,14 @@ typedef struct { // URIs SamplerURIs uris; - // Current position in run() - uint32_t frame_offset; - // Playback state + Sample* sample; + uint32_t frame_offset; float gain; sf_count_t frame; bool play; + bool activated; + bool sample_changed; } Sampler; /** @@ -109,18 +105,17 @@ typedef struct { not modified. */ static Sample* -load_sample(Sampler* self, const char* path) +load_sample(LV2_Log_Logger* logger, const char* path) { - const size_t path_len = strlen(path); + lv2_log_trace(logger, "Loading %s\n", path); - lv2_log_trace(&self->logger, "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); + const size_t path_len = strlen(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)) { - lv2_log_error(&self->logger, "Failed to open sample '%s'\n", path); + lv2_log_error(logger, "Failed to open sample '%s'\n", path); free(sample); return NULL; } @@ -128,7 +123,7 @@ load_sample(Sampler* self, const char* path) // Read data float* const data = malloc(sizeof(float) * info->frames); if (!data) { - lv2_log_error(&self->logger, "Failed to allocate memory for sample\n"); + lv2_log_error(logger, "Failed to allocate memory for sample\n"); return NULL; } sf_seek(sndfile, 0ul, SEEK_SET); @@ -175,20 +170,19 @@ work(LV2_Handle instance, // Free old sample const SampleMessage* msg = (const SampleMessage*)data; free_sample(self, msg->sample); - } else { + } else if (atom->type == self->forge.Object) { // Handle set message (load sample). - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)data; - - // Get file path from message - const LV2_Atom* file_path = read_set_file(&self->uris, obj); - if (!file_path) { + const LV2_Atom_Object* obj = (const LV2_Atom_Object*)data; + const char* path = read_set_file(&self->uris, obj); + if (!path) { + lv2_log_error(&self->logger, "Malformed set file request\n"); return LV2_WORKER_ERR_UNKNOWN; } // Load sample. - Sample* sample = load_sample(self, LV2_ATOM_BODY_CONST(file_path)); + Sample* sample = load_sample(&self->logger, path); if (sample) { - // Loaded sample, send it to run() to be applied. + // Send new sample to run() to be applied respond(handle, sizeof(sample), &sample); } } @@ -208,22 +202,25 @@ work_response(LV2_Handle instance, uint32_t size, const void* data) { - Sampler* self = (Sampler*)instance; - - SampleMessage msg = { { sizeof(Sample*), self->uris.eg_freeSample }, - self->sample }; - - // Send a message to the worker to free the current sample - self->schedule->schedule_work(self->schedule->handle, sizeof(msg), &msg); + Sampler* self = (Sampler*)instance; + Sample* old_sample = self->sample; + Sample* new_sample = *(Sample*const*)data; // Install the new sample self->sample = *(Sample*const*)data; - // Send a notification that we're using a new sample. - lv2_atom_forge_frame_time(&self->forge, self->frame_offset); - write_set_file(&self->forge, &self->uris, - self->sample->path, - self->sample->path_len); + // Schedule work to free the old sample + SampleMessage msg = { { sizeof(Sample*), self->uris.eg_freeSample }, + old_sample }; + self->schedule->schedule_work(self->schedule->handle, sizeof(msg), &msg); + + if (strcmp(old_sample->path, new_sample->path)) { + // Send a notification that we're using a new sample + lv2_atom_forge_frame_time(&self->forge, self->frame_offset); + write_set_file(&self->forge, &self->uris, + new_sample->path, + new_sample->path_len); + } return LV2_WORKER_SUCCESS; } @@ -279,15 +276,6 @@ instantiate(const LV2_Descriptor* descriptor, map_sampler_uris(self->map, &self->uris); lv2_atom_forge_init(&self->forge, self->map); - // Load the default sample file - const size_t path_len = strlen(path); - const size_t file_len = strlen(default_sample_file); - const size_t len = path_len + file_len; - char* sample_path = (char*)malloc(len + 1); - snprintf(sample_path, len + 1, "%s%s", path, default_sample_file); - self->sample = load_sample(self, sample_path); - free(sample_path); - return (LV2_Handle)self; } @@ -299,6 +287,18 @@ cleanup(LV2_Handle instance) free(self); } +static void +activate(LV2_Handle instance) +{ + ((Sampler*)instance)->activated = true; +} + +static void +deactivate(LV2_Handle instance) +{ + ((Sampler*)instance)->activated = false; +} + /** Define a macro for converting a gain in dB to a coefficient. */ #define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g) * 0.05f) : 0.0f) @@ -367,7 +367,7 @@ run(LV2_Handle instance, const uint32_t key = ((const LV2_Atom_URID*)property)->body; if (key == uris->eg_sample) { // Sample change, send it to the worker. - lv2_log_trace(&self->logger, "Queueing set message\n"); + lv2_log_trace(&self->logger, "Scheduling sample change\n"); self->schedule->schedule_work(self->schedule->handle, lv2_atom_total_size(&ev->body), &ev->body); @@ -379,7 +379,6 @@ run(LV2_Handle instance, } } else if (obj->body.otype == uris->patch_Get) { // Received a get message, emit our state (probably to UI) - lv2_log_trace(&self->logger, "Responding to get request\n"); lv2_atom_forge_frame_time(&self->forge, self->frame_offset); write_set_file(&self->forge, &self->uris, self->sample->path, @@ -462,6 +461,19 @@ restore(LV2_Handle instance, { Sampler* self = (Sampler*)instance; + // Get host features + LV2_Worker_Schedule* schedule = NULL; + LV2_State_Map_Path* paths = NULL; + const char* missing = lv2_features_query( + features, + LV2_STATE__mapPath, &paths, true, + LV2_WORKER__schedule, &schedule, false, + NULL); + if (missing) { + lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); + return LV2_STATE_ERR_NO_FEATURE; + } + // Get eg:sample from state size_t size; uint32_t type; @@ -476,21 +488,29 @@ restore(LV2_Handle instance, return LV2_STATE_ERR_BAD_TYPE; } - LV2_State_Map_Path* map_path = (LV2_State_Map_Path*)lv2_features_data( - features, LV2_STATE__mapPath); - if (!map_path) { - return LV2_STATE_ERR_NO_FEATURE; - } - // Map abstract state path to absolute path const char* apath = (const char*)value; - char* path = map_path->absolute_path(map_path->handle, apath); + char* path = paths->absolute_path(paths->handle, apath); // Replace current sample with the new one - lv2_log_trace(&self->logger, "Restoring file %s\n", path); - free_sample(self, self->sample); - self->sample = load_sample(self, path); - self->sample_changed = true; + if (!self->activated || !schedule) { + // No scheduling available, load sample immediately + lv2_log_trace(&self->logger, "Synchronous restore\n"); + free_sample(self, self->sample); + self->sample = load_sample(&self->logger, path); + self->sample_changed = true; + } else { + // Schedule sample to be loaded by the provided worker + lv2_log_trace(&self->logger, "Scheduling restore\n"); + LV2_Atom_Forge forge; + LV2_Atom* buf = calloc(1, sizeof(LV2_Atom) * 32 + strlen(path)); + lv2_atom_forge_init(&forge, self->map); + lv2_atom_forge_set_sink(&forge, atom_sink, atom_sink_deref, buf); + write_set_file(&forge, &self->uris, path, strlen(path)); + + const uint32_t msg_size = lv2_atom_pad_size(buf->size); + schedule->schedule_work(self->schedule->handle, msg_size, buf + 1); + } return LV2_STATE_SUCCESS; } @@ -512,9 +532,9 @@ static const LV2_Descriptor descriptor = { EG_SAMPLER_URI, instantiate, connect_port, - NULL, // activate, + activate, run, - NULL, // deactivate, + deactivate, cleanup, extension_data }; |