diff options
Diffstat (limited to 'plugins/eg-sampler.lv2/sampler.c')
| -rw-r--r-- | plugins/eg-sampler.lv2/sampler.c | 552 |
1 files changed, 0 insertions, 552 deletions
diff --git a/plugins/eg-sampler.lv2/sampler.c b/plugins/eg-sampler.lv2/sampler.c deleted file mode 100644 index 36acb2f..0000000 --- a/plugins/eg-sampler.lv2/sampler.c +++ /dev/null @@ -1,552 +0,0 @@ -/* - LV2 Sampler Example Plugin - Copyright 2011-2016 David Robillard <d@drobilla.net> - Copyright 2011 Gabriel M. Beddingfield <gabriel@teuton.org> - Copyright 2011 James Morris <jwm.art.net@gmail.com> - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#include <math.h> -#include <stdlib.h> -#include <string.h> -#ifndef __cplusplus -# include <stdbool.h> -#endif - -#include <sndfile.h> - -#include "lv2/lv2plug.in/ns/ext/atom/forge.h" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" -#include "lv2/lv2plug.in/ns/ext/log/log.h" -#include "lv2/lv2plug.in/ns/ext/log/logger.h" -#include "lv2/lv2plug.in/ns/ext/midi/midi.h" -#include "lv2/lv2plug.in/ns/ext/patch/patch.h" -#include "lv2/lv2plug.in/ns/ext/state/state.h" -#include "lv2/lv2plug.in/ns/ext/urid/urid.h" -#include "lv2/lv2plug.in/ns/ext/worker/worker.h" -#include "lv2/lv2plug.in/ns/lv2core/lv2.h" -#include "lv2/lv2plug.in/ns/lv2core/lv2_util.h" - -#include "uris.h" -#include "atom_sink.h" - -enum { - SAMPLER_CONTROL = 0, - SAMPLER_NOTIFY = 1, - SAMPLER_OUT = 2 -}; - -typedef struct { - SF_INFO info; // Info about sample from sndfile - float* data; // Sample data in float - char* path; // Path of file - uint32_t path_len; // Length of path -} Sample; - -typedef struct { - // Features - LV2_URID_Map* map; - LV2_Worker_Schedule* schedule; - LV2_Log_Logger logger; - - // Forge for creating atoms - LV2_Atom_Forge forge; - - // Ports - const LV2_Atom_Sequence* control_port; - LV2_Atom_Sequence* notify_port; - float* output_port; - - // Forge frame for notify port (for writing worker replies) - LV2_Atom_Forge_Frame notify_frame; - - // URIs - SamplerURIs uris; - - // Playback state - Sample* sample; - uint32_t frame_offset; - float gain; - sf_count_t frame; - bool play; - bool activated; - bool sample_changed; -} Sampler; - -/** - An atom-like message used internally to apply/free samples. - - This is only used internally to communicate with the worker, it is never - sent to the outside world via a port since it is not POD. It is convenient - to use an Atom header so actual atoms can be easily sent through the same - ringbuffer. -*/ -typedef struct { - LV2_Atom atom; - Sample* sample; -} SampleMessage; - -/** - Load a new sample and return it. - - Since this is of course not a real-time safe action, this is called in the - worker thread only. The sample is loaded and returned only, plugin state is - not modified. -*/ -static Sample* -load_sample(LV2_Log_Logger* logger, const char* path) -{ - lv2_log_trace(logger, "Loading %s\n", path); - - 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(logger, "Failed to open sample '%s'\n", path); - free(sample); - return NULL; - } - - // Read data - float* const data = (float*)malloc(sizeof(float) * info->frames); - if (!data) { - lv2_log_error(logger, "Failed to allocate memory for sample\n"); - return NULL; - } - sf_seek(sndfile, 0ul, SEEK_SET); - sf_read_float(sndfile, data, info->frames); - sf_close(sndfile); - - // Fill sample struct and return it - sample->data = data; - sample->path = (char*)malloc(path_len + 1); - sample->path_len = (uint32_t)path_len; - memcpy(sample->path, path, path_len + 1); - - return sample; -} - -static void -free_sample(Sampler* self, Sample* sample) -{ - if (sample) { - lv2_log_trace(&self->logger, "Freeing %s\n", sample->path); - free(sample->path); - free(sample->data); - free(sample); - } -} - -/** - Do work in a non-realtime thread. - - This is called for every piece of work scheduled in the audio thread using - self->schedule->schedule_work(). A reply can be sent back to the audio - thread using the provided respond function. -*/ -static LV2_Worker_Status -work(LV2_Handle instance, - LV2_Worker_Respond_Function respond, - LV2_Worker_Respond_Handle handle, - uint32_t size, - const void* data) -{ - Sampler* self = (Sampler*)instance; - const LV2_Atom* atom = (const LV2_Atom*)data; - if (atom->type == self->uris.eg_freeSample) { - // Free old sample - const SampleMessage* msg = (const SampleMessage*)data; - free_sample(self, msg->sample); - } else if (atom->type == self->forge.Object) { - // Handle set message (load sample). - 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->logger, path); - if (sample) { - // Send new sample to run() to be applied - respond(handle, sizeof(sample), &sample); - } - } - - return LV2_WORKER_SUCCESS; -} - -/** - Handle a response from work() in the audio thread. - - When running normally, this will be called by the host after run(). When - freewheeling, this will be called immediately at the point the work was - scheduled. -*/ -static LV2_Worker_Status -work_response(LV2_Handle instance, - uint32_t size, - const void* data) -{ - Sampler* self = (Sampler*)instance; - Sample* old_sample = self->sample; - Sample* new_sample = *(Sample*const*)data; - - // Install the new sample - self->sample = *(Sample*const*)data; - - // 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; -} - -static void -connect_port(LV2_Handle instance, - uint32_t port, - void* data) -{ - Sampler* self = (Sampler*)instance; - switch (port) { - case SAMPLER_CONTROL: - self->control_port = (const LV2_Atom_Sequence*)data; - break; - case SAMPLER_NOTIFY: - self->notify_port = (LV2_Atom_Sequence*)data; - break; - case SAMPLER_OUT: - self->output_port = (float*)data; - break; - default: - break; - } -} - -static LV2_Handle -instantiate(const LV2_Descriptor* descriptor, - double rate, - const char* path, - const LV2_Feature* const* features) -{ - // Allocate and initialise instance structure. - Sampler* self = (Sampler*)calloc(1, sizeof(Sampler)); - if (!self) { - return NULL; - } - - // Get host features - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &self->logger.log, false, - LV2_URID__map, &self->map, true, - LV2_WORKER__schedule, &self->schedule, true, - NULL); - lv2_log_logger_set_map(&self->logger, self->map); - if (missing) { - lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); - free(self); - return NULL; - } - - // Map URIs and initialise forge - map_sampler_uris(self->map, &self->uris); - lv2_atom_forge_init(&self->forge, self->map); - - return (LV2_Handle)self; -} - -static void -cleanup(LV2_Handle instance) -{ - Sampler* self = (Sampler*)instance; - free_sample(self, self->sample); - 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) - -static void -run(LV2_Handle instance, - uint32_t sample_count) -{ - Sampler* self = (Sampler*)instance; - SamplerURIs* uris = &self->uris; - sf_count_t start_frame = 0; - sf_count_t pos = 0; - float* output = self->output_port; - - // Set up forge to write directly to notify output port. - const uint32_t notify_capacity = self->notify_port->atom.size; - lv2_atom_forge_set_buffer(&self->forge, - (uint8_t*)self->notify_port, - notify_capacity); - - // Start a sequence in the notify output port. - lv2_atom_forge_sequence_head(&self->forge, &self->notify_frame, 0); - - // Send update to UI if sample has changed due to state restore - if (self->sample_changed) { - lv2_atom_forge_frame_time(&self->forge, 0); - write_set_file(&self->forge, &self->uris, - self->sample->path, - self->sample->path_len); - self->sample_changed = false; - } - - // Read incoming events - LV2_ATOM_SEQUENCE_FOREACH(self->control_port, ev) { - self->frame_offset = ev->time.frames; - if (ev->body.type == uris->midi_Event) { - const uint8_t* const msg = (const uint8_t*)(ev + 1); - switch (lv2_midi_message_type(msg)) { - case LV2_MIDI_MSG_NOTE_ON: - start_frame = ev->time.frames; - self->frame = 0; - self->play = true; - break; - default: - break; - } - } else if (lv2_atom_forge_is_object_type(&self->forge, ev->body.type)) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; - if (obj->body.otype == uris->patch_Set) { - // Get the property and value of the set message - const LV2_Atom* property = NULL; - const LV2_Atom* value = NULL; - lv2_atom_object_get(obj, - uris->patch_property, &property, - uris->patch_value, &value, - 0); - if (!property) { - lv2_log_error(&self->logger, - "patch:Set message with no property\n"); - continue; - } else if (property->type != uris->atom_URID) { - lv2_log_error(&self->logger, - "patch:Set property is not a URID\n"); - continue; - } - - 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, "Scheduling sample change\n"); - self->schedule->schedule_work(self->schedule->handle, - lv2_atom_total_size(&ev->body), - &ev->body); - } else if (key == uris->param_gain) { - // Gain change - if (value->type == uris->atom_Float) { - self->gain = DB_CO(((LV2_Atom_Float*)value)->body); - } - } - } else if (obj->body.otype == uris->patch_Get) { - // Received a get message, emit our state (probably to UI) - lv2_atom_forge_frame_time(&self->forge, self->frame_offset); - write_set_file(&self->forge, &self->uris, - self->sample->path, - self->sample->path_len); - } else { - lv2_log_trace(&self->logger, - "Unknown object type %d\n", obj->body.otype); - } - } else { - lv2_log_trace(&self->logger, - "Unknown event type %d\n", ev->body.type); - } - } - - // Render the sample (possibly already in progress) - if (self->play) { - uint32_t f = self->frame; - const uint32_t lf = self->sample->info.frames; - - for (pos = 0; pos < start_frame; ++pos) { - output[pos] = 0; - } - - for (; pos < sample_count && f < lf; ++pos, ++f) { - output[pos] = self->sample->data[f] * self->gain; - } - - self->frame = f; - - if (f == lf) { - self->play = false; - } - } - - // Add zeros to end if sample not long enough (or not playing) - for (; pos < sample_count; ++pos) { - output[pos] = 0.0f; - } -} - -static LV2_State_Status -save(LV2_Handle instance, - LV2_State_Store_Function store, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature* const* features) -{ - Sampler* self = (Sampler*)instance; - if (!self->sample) { - return LV2_STATE_SUCCESS; - } - - 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 absolute sample path to an abstract state path - char* apath = map_path->abstract_path(map_path->handle, self->sample->path); - - // Store eg:sample = abstract path - store(handle, - self->uris.eg_sample, - apath, - strlen(apath) + 1, - self->uris.atom_Path, - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - - free(apath); - return LV2_STATE_SUCCESS; -} - -static LV2_State_Status -restore(LV2_Handle instance, - LV2_State_Retrieve_Function retrieve, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature* const* features) -{ - 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; - uint32_t valflags; - const void* value = retrieve(handle, self->uris.eg_sample, - &size, &type, &valflags); - if (!value) { - lv2_log_error(&self->logger, "Missing eg:sample\n"); - return LV2_STATE_ERR_NO_PROPERTY; - } else if (type != self->uris.atom_Path) { - lv2_log_error(&self->logger, "Non-path eg:sample\n"); - return LV2_STATE_ERR_BAD_TYPE; - } - - // Map abstract state path to absolute path - const char* apath = (const char*)value; - char* path = paths->absolute_path(paths->handle, apath); - - // Replace current sample with the new one - 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 = (LV2_Atom*)calloc(1, strlen(path) + 128); - 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); - free(buf); - } - - return LV2_STATE_SUCCESS; -} - -static const void* -extension_data(const char* uri) -{ - static const LV2_State_Interface state = { save, restore }; - static const LV2_Worker_Interface worker = { work, work_response, NULL }; - if (!strcmp(uri, LV2_STATE__interface)) { - return &state; - } else if (!strcmp(uri, LV2_WORKER__interface)) { - return &worker; - } - return NULL; -} - -static const LV2_Descriptor descriptor = { - EG_SAMPLER_URI, - instantiate, - connect_port, - activate, - run, - deactivate, - cleanup, - extension_data -}; - -LV2_SYMBOL_EXPORT -const LV2_Descriptor* lv2_descriptor(uint32_t index) -{ - switch (index) { - case 0: - return &descriptor; - default: - return NULL; - } -} |