summaryrefslogtreecommitdiff
path: root/pluginlv2.cc
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2016-03-11 12:28:44 +0100
committerBent Bisballe Nyeng <deva@aasimon.org>2016-03-11 12:28:44 +0100
commita27d71e88bd8f950e11e7a1a2ba0a4dd88f541f4 (patch)
tree688bbe1368d31bb7a67f29795a040c1102c34a6e /pluginlv2.cc
Initial import from DrumGizmo branch.
Diffstat (limited to 'pluginlv2.cc')
-rw-r--r--pluginlv2.cc637
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