diff options
author | Bent Bisballe Nyeng <deva@aasimon.org> | 2016-03-11 12:28:44 +0100 |
---|---|---|
committer | Bent Bisballe Nyeng <deva@aasimon.org> | 2016-03-11 12:28:44 +0100 |
commit | a27d71e88bd8f950e11e7a1a2ba0a4dd88f541f4 (patch) | |
tree | 688bbe1368d31bb7a67f29795a040c1102c34a6e /pluginlv2.cc |
Initial import from DrumGizmo branch.
Diffstat (limited to 'pluginlv2.cc')
-rw-r--r-- | pluginlv2.cc | 637 |
1 files changed, 637 insertions, 0 deletions
diff --git a/pluginlv2.cc b/pluginlv2.cc new file mode 100644 index 0000000..d351104 --- /dev/null +++ b/pluginlv2.cc @@ -0,0 +1,637 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * pluginlv2.cc + * + * Sun Feb 7 15:15:24 CET 2016 + * Copyright 2016 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of PluginGizmo. + * + * PluginGizmo is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * PluginGizmo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PluginGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "pluginlv2.h" + +#include <iostream> +#include <assert.h> + +#include "midievent.h" + +#define LV2_PLUGIN_URI__atom LV2_PLUGIN_URI "/atom#" +#define LV2_PLUGIN_URI__instance LV2_PLUGIN_URI "#plugin-instance" +#define LV2_PLUGIN_URI__ui LV2_PLUGIN_URI "#ui" + +#include "lv2/lv2plug.in/ns/ext/atom/util.h" +#include <lv2/lv2plug.in/ns/ext/midi/midi.h> +#include <lv2/lv2plug.in/ns/ext/instance-access/instance-access.h> + +bool PluginLV2::getFreeWheel() const +{ + return free_wheel; +} + +float PluginLV2::getSamplerate() +{ + return sample_rate; +} + +std::size_t PluginLV2::getFramesize() +{ + return frame_size; +} + +bool PluginLV2::getActive() +{ + return active; +} + +float PluginLV2::getLatency() +{ + if(latency_port) + { + return *latency_port; + } + + return 0.0f; +} + +void PluginLV2::setLatency(float latency) +{ + if(latency_port) + { + *latency_port = latency; + } +} + +LV2_Handle PluginLV2::instantiate(const struct _LV2_Descriptor* descriptor, + double sample_rate, + const char* bundle_path, + const LV2_Feature *const *features) +{ + PluginLV2* plugin_lv2 = createEffectInstance(); + + plugin_lv2->sample_rate = sample_rate; + + plugin_lv2->input_event_ports.resize(plugin_lv2->getNumberOfMidiInputs(), + nullptr); + plugin_lv2->output_event_ports.resize(plugin_lv2->getNumberOfMidiOutputs(), + nullptr); + + plugin_lv2->input_audio_ports.resize(plugin_lv2->getNumberOfAudioInputs()); + plugin_lv2->output_audio_ports.resize(plugin_lv2->getNumberOfAudioOutputs()); + + for(auto& port : plugin_lv2->output_audio_ports) + { + port = nullptr; + } + + for(auto& port : plugin_lv2->input_audio_ports) + { + port = nullptr; + } + + while(*features != nullptr) + { + std::string uri = (*features)->URI; + void* data = (*features)->data; + + if(uri == LV2_URID__map) + { + plugin_lv2->map = (LV2_URID_Map*)data; + } + + ++features; + } + + // Only reported on creation. + plugin_lv2->onSamplerateChange(sample_rate); + + return (LV2_Handle)plugin_lv2; +} + +void PluginLV2::connectPort(LV2_Handle instance, uint32_t port, + void *data_location) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + + if(port == static_cast<uint32_t>(LV2Ports::FreeWheel)) + { + plugin_lv2->free_wheel_port = (float*)data_location; + if(plugin_lv2->free_wheel_port) + { + plugin_lv2->free_wheel = (*plugin_lv2->free_wheel_port != 0.0f); + // Signal first time. + plugin_lv2->onFreeWheelChange(plugin_lv2->free_wheel); + } + } + + if(port == static_cast<uint32_t>(LV2Ports::Latency)) + { + plugin_lv2->latency_port = (float*)data_location; + } + + uint32_t port_offset = static_cast<uint32_t>(LV2Ports::PortOffset); + + if((port >= port_offset) && + (port < (port_offset + plugin_lv2->getNumberOfMidiInputs()))) + { + int port_index = port - port_offset; + plugin_lv2->input_event_ports[port_index] = + (LV2_Atom_Sequence*)data_location; + } + + port_offset += plugin_lv2->getNumberOfMidiInputs(); + + if((port >= port_offset) && + (port < (port_offset + plugin_lv2->getNumberOfMidiOutputs()))) + { + int port_index = port - port_offset; + plugin_lv2->output_event_ports[port_index] = + (LV2_Atom_Sequence*)data_location; + } + + port_offset += plugin_lv2->getNumberOfMidiOutputs(); + + if((port >= port_offset) && + (port < (port_offset + plugin_lv2->getNumberOfAudioInputs()))) + { + int port_index = port - port_offset; + plugin_lv2->input_audio_ports[port_index] = (float*)data_location; + } + + port_offset += plugin_lv2->getNumberOfAudioInputs(); + + if((port >= port_offset) && + (port < (port_offset + plugin_lv2->getNumberOfAudioOutputs()))) + { + int port_index = port - port_offset; + plugin_lv2->output_audio_ports[port_index] = (float*)data_location; + } +} + +class Sequence { +public: + Sequence(LV2_URID_Map& map, void* buffer, std::size_t buffer_size); + void clear(); + void addMidiEvent(std::size_t pos, const char* data, std::size_t size); + void* data(); + +private: + void *buffer; + std::size_t buffer_size; + LV2_Atom_Sequence *seq; + LV2_URID_Map& map; +}; + +Sequence::Sequence(LV2_URID_Map& map, void* buffer, std::size_t buffer_size) + : map(map) +{ + this->buffer = buffer; + this->buffer_size = buffer_size; + + seq = (LV2_Atom_Sequence*)buffer; + + seq->atom.size = sizeof(LV2_Atom_Sequence_Body); + seq->atom.type = map.map(map.handle, LV2_ATOM__Sequence); + seq->body.unit = 0; + seq->body.pad = 0; +} + +// Keep this to support atom extension from lv2 < 1.10 +static inline void _lv2_atom_sequence_clear(LV2_Atom_Sequence* seq) +{ + seq->atom.size = sizeof(LV2_Atom_Sequence_Body); +} + +void Sequence::clear() +{ + _lv2_atom_sequence_clear(seq); +} + +// Keep this to support atom extension from lv2 < 1.10 +static inline LV2_Atom_Event* +_lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq, + uint32_t capacity, + const LV2_Atom_Event* event) +{ + const uint32_t total_size = (uint32_t)sizeof(*event) + event->body.size; + + if(capacity - seq->atom.size < total_size) + { + return nullptr; + } + + LV2_Atom_Event* e = lv2_atom_sequence_end(&seq->body, seq->atom.size); + memcpy(e, event, total_size); + + seq->atom.size += lv2_atom_pad_size(total_size); + + return e; +} + +void Sequence::addMidiEvent(std::size_t pos, const char* data, std::size_t size) +{ + typedef struct { + LV2_Atom_Event event; + uint8_t msg[6]; + } MIDINoteEvent; + + + MIDINoteEvent ev; + ev.event.time.frames = pos; + ev.event.body.type = map.map(map.handle, LV2_MIDI__MidiEvent); + ev.event.body.size = size;//sizeof(LV2_Atom_Event) + size; + + assert(size <= sizeof(ev.msg)); // Assert that we have room for the message + + memcpy(ev.msg, data, size + sizeof(LV2_Atom_Event)); + + _lv2_atom_sequence_append_event(seq, this->buffer_size, &ev.event); +} + +void* Sequence::data() +{ + return buffer; +} + +void PluginLV2::run(LV2_Handle instance, uint32_t sample_count) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + + // Handle free-wheel state + if(plugin_lv2->free_wheel_port != nullptr) + { + bool new_free_wheel = *plugin_lv2->free_wheel_port != 0.0f; + if(new_free_wheel != plugin_lv2->free_wheel) + { + plugin_lv2->free_wheel = new_free_wheel; + plugin_lv2->onFreeWheelChange(plugin_lv2->free_wheel); + } + } + + // Handle frame size + if(plugin_lv2->frame_size != sample_count) + { + plugin_lv2->frame_size = sample_count; + plugin_lv2->onFramesizeChange(plugin_lv2->frame_size); + } + + // Convert input lv2 events to input events. + std::vector<MidiEvent> input_events; + for(std::size_t port = 0; port < plugin_lv2->getNumberOfMidiInputs(); ++port) + { + if(plugin_lv2->input_event_ports[port] == nullptr) + { + continue; // Not yet connected. + } + + auto& event_port = plugin_lv2->input_event_ports[port]; + + LV2_Atom_Event* ev = lv2_atom_sequence_begin(&event_port->body); + while(!lv2_atom_sequence_is_end(&event_port->body, + event_port->atom.size, + ev)) + { + if(ev->body.type != plugin_lv2->map->map(plugin_lv2->map->handle, + LV2_MIDI__MidiEvent)) + { + continue; // not a midi event. + } + + const char* data = (char*)(ev + 1); + input_events.emplace_back(ev->time.frames, data, ev->body.size); + + ev = lv2_atom_sequence_next(ev); + } + } + + std::vector<MidiEvent> output_events; + + // Process events and audio + plugin_lv2->process(plugin_lv2->pos, + input_events, + output_events, + plugin_lv2->input_audio_ports, + plugin_lv2->output_audio_ports, + sample_count); + + // Convert output events to lv2 events + if(plugin_lv2->getNumberOfMidiOutputs() > 0) + { + if(plugin_lv2->map != nullptr) + { + if(plugin_lv2->output_event_ports[0] != nullptr) // Not yet connected? + { + auto& event_port = plugin_lv2->output_event_ports[0]; // TODO: Split? + Sequence seq(*plugin_lv2->map, + &event_port->body + 1, + event_port->atom.size); + for(auto midi_event : output_events) + { + seq.addMidiEvent(midi_event.getTime(), + midi_event.getData(), + midi_event.getSize()); + } + } + } + } + + plugin_lv2->pos += sample_count; +} + +void PluginLV2::activate(LV2_Handle instance) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + plugin_lv2->active = true; + plugin_lv2->onActiveChange(plugin_lv2->active); +} + +void PluginLV2::deactivate(LV2_Handle instance) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + plugin_lv2->active = false; + plugin_lv2->onActiveChange(plugin_lv2->active); +} + +void PluginLV2::cleanup(LV2_Handle instance) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + delete plugin_lv2; +} + +// +// State handling +// + +LV2_State_Status PluginLV2::save(LV2_Handle instance, + LV2_State_Store_Function store, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + + if(plugin_lv2->map == nullptr) + { + // Missing urid feature? + return LV2_STATE_ERR_NO_FEATURE; + } + + std::string config = plugin_lv2->onStateSave(); + + store(handle, + plugin_lv2->map->map(plugin_lv2->map->handle, + LV2_PLUGIN_URI__atom "config"), + config.data(), + config.length(), + plugin_lv2->map->map(plugin_lv2->map->handle, LV2_ATOM__Chunk), + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + + return LV2_STATE_SUCCESS; +} + +LV2_State_Status PluginLV2::restore(LV2_Handle instance, + LV2_State_Retrieve_Function retrieve, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + + if(plugin_lv2->map == nullptr) + { + // Missing urid feature? + return LV2_STATE_ERR_NO_FEATURE; + } + + std::size_t size; + uint32_t type; + + const char* data = + (const char*)retrieve(handle, + plugin_lv2->map->map(plugin_lv2->map->handle, + LV2_PLUGIN_URI__atom "config"), + &size, &type, &flags); + + if(data && size) + { + std::string config; + + config.append(data, size); + plugin_lv2->onStateRestore(config); + } + + return LV2_STATE_SUCCESS; +} + +static LV2_State_Interface persist = { + PluginLV2::save, + PluginLV2::restore +}; + +const void* PluginLV2::extensionData(const char *uri) +{ + if(!strcmp(uri, LV2_STATE__interface)) + { + return &persist; + } + + return nullptr; +} + + +// +// GUI +// +LV2UI_Handle PluginLV2::uiInstantiate(const struct _LV2UI_Descriptor*descriptor, + const char * plugin_uri, + const char * bundle_path, + LV2UI_Write_Function write_function, + LV2UI_Controller controller, + LV2UI_Widget* widget, + const LV2_Feature * const * features) +{ + LV2_Handle instance = nullptr; + void* parent = nullptr; + LV2UI_Resize* resize = nullptr; + + while(*features != nullptr) { + std::string uri = (*features)->URI; + void *data = (*features)->data; + + if(uri == LV2_INSTANCE_ACCESS_URI) + { + instance = (LV2_Handle)data; + } + + if(uri == LV2_UI__parent) + { + parent = data; + } + + if(uri == LV2_UI__resize) + { + resize = (LV2UI_Resize*)data; + } + + features++; + } + + if(instance == nullptr) + { + return nullptr; + } + + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + + // Do we have a GUI? + if(plugin_lv2->hasGUI() == false) + { + return nullptr; + } + + plugin_lv2->resize = resize; + + plugin_lv2->createWindow(parent); + plugin_lv2->resize->ui_resize(plugin_lv2->resize->handle, 100, 100); + + return plugin_lv2; +} + +void PluginLV2::resizeWindow(std::size_t width, std::size_t height) +{ + if(resize) + { + resize->ui_resize(resize->handle, width, height); + } +} + +void PluginLV2::closeWindow() +{ +} + +static const LV2_Descriptor descriptor = { + LV2_PLUGIN_URI, + PluginLV2::instantiate, + PluginLV2::connectPort, + PluginLV2::activate, + PluginLV2::run, + PluginLV2::deactivate, + PluginLV2::cleanup, + PluginLV2::extensionData +}; + +void PluginLV2::uiCleanup(LV2UI_Handle handle) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)handle; + plugin_lv2->onDestroyWindow () ; + +} + +int PluginLV2::uiIdle(LV2UI_Handle handle) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)handle; + plugin_lv2->onIdle(); + return 0; +} + +static const LV2UI_Idle_Interface idle_iface = { + PluginLV2::uiIdle +}; + +const void* PluginLV2::uiExtensionData(const char* uri) +{ + if(!strcmp(uri, LV2_UI__idleInterface)) + { + return &idle_iface; + } + + return NULL; +} + +static LV2UI_Descriptor ui_descriptor = { + LV2_PLUGIN_URI__ui, + PluginLV2::uiInstantiate, + PluginLV2::uiCleanup, + nullptr,//PluginLV2::ui_port_event, + PluginLV2::uiExtensionData +}; + +#ifdef __cplusplus +extern "C" { +#endif + +LV2_SYMBOL_EXPORT +const LV2_Descriptor* lv2_descriptor(uint32_t index) +{ + switch (index) { + case 0: + return &descriptor; + default: + return nullptr; + } +} + +LV2_SYMBOL_EXPORT +const LV2UI_Descriptor *lv2ui_descriptor(uint32_t index) +{ + switch(index) { + case 0: + return &ui_descriptor; + default: + return nullptr; + } +} + +// +// DynManifest experiments failed, but the code is kept here for nostalgic +// reasons :-) +// +// +//int lv2_dyn_manifest_open(LV2_Dyn_Manifest_Handle* handle, +// const LV2_Feature* const* features) +//{ +// *handle = createEffectInstance(); +// return 0; +//} +// +//int lv2_dyn_manifest_get_subjects(LV2_Dyn_Manifest_Handle handle, FILE* fp) +//{ +//// PluginLV2* plugin_lv2 = (PluginLV2*)handle; +// +// fprintf(fp, "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"); +// fprintf(fp, "<" LV2_PLUGIN_URI "> a lv2:Plugin .\n"); +// return 0; +//} +// +//int lv2_dyn_manifest_get_data(LV2_Dyn_Manifest_Handle handle, FILE* fp, +// const char* uri) +//{ +// //PluginLV2* plugin_lv2 = (PluginLV2*)handle; +// printf("%s '%s'\n", __PRETTY_FUNCTION__, uri); +// return 0; +//} +// +//void lv2_dyn_manifest_close(LV2_Dyn_Manifest_Handle handle) +//{ +// PluginLV2* plugin_lv2 = (PluginLV2*)handle; +// delete plugin_lv2; +//} + +#ifdef __cplusplus +} +#endif |