summaryrefslogtreecommitdiffstats
path: root/src/engine/LV2Node.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/LV2Node.cpp')
-rw-r--r--src/engine/LV2Node.cpp305
1 files changed, 305 insertions, 0 deletions
diff --git a/src/engine/LV2Node.cpp b/src/engine/LV2Node.cpp
new file mode 100644
index 00000000..a06cc55a
--- /dev/null
+++ b/src/engine/LV2Node.cpp
@@ -0,0 +1,305 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Ingen 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <iostream>
+#include <cassert>
+#include <float.h>
+#include <stdint.h>
+#include <cmath>
+#include <raul/Maid.hpp>
+#include "AudioBuffer.hpp"
+#include "InputPort.hpp"
+#include "LV2Node.hpp"
+#include "LV2Plugin.hpp"
+#include "EventBuffer.hpp"
+#include "OutputPort.hpp"
+#include "ProcessContext.hpp"
+#include "lv2_contexts.h"
+
+using namespace std;
+
+namespace Ingen {
+
+
+/** Partially construct a LV2Node.
+ *
+ * Object is not usable until instantiate() is called with success.
+ * (It _will_ crash!)
+ */
+LV2Node::LV2Node(LV2Plugin* plugin,
+ const string& name,
+ bool polyphonic,
+ PatchImpl* parent,
+ SampleRate srate,
+ size_t buffer_size)
+ : NodeBase(plugin, name, polyphonic, parent, srate, buffer_size)
+ , _lv2_plugin(plugin)
+ , _instances(NULL)
+ , _prepared_instances(NULL)
+ , _message_run(NULL)
+{
+ assert(_lv2_plugin);
+}
+
+
+LV2Node::~LV2Node()
+{
+ for (uint32_t i=0; i < _polyphony; ++i)
+ slv2_instance_free((*_instances)[i]);
+
+ delete _instances;
+}
+
+
+bool
+LV2Node::prepare_poly(uint32_t poly)
+{
+ NodeBase::prepare_poly(poly);
+
+ if ( (!_polyphonic)
+ || (_prepared_instances && poly <= _prepared_instances->size()) ) {
+ return true;
+ }
+
+ _prepared_instances = new Raul::Array<SLV2Instance>(poly, *_instances);
+ for (uint32_t i = _polyphony; i < _prepared_instances->size(); ++i) {
+ // FIXME: features array (in NodeFactory) must be passed!
+ _prepared_instances->at(i) = slv2_plugin_instantiate(
+ _lv2_plugin->slv2_plugin(), _srate, NULL);
+
+ if (_prepared_instances->at(i) == NULL) {
+ cerr << "Failed to instantiate plugin!" << endl;
+ return false;
+ }
+
+ if (_activated)
+ slv2_instance_activate(_prepared_instances->at(i));
+ }
+
+ return true;
+}
+
+
+bool
+LV2Node::apply_poly(Raul::Maid& maid, uint32_t poly)
+{
+ if (!_polyphonic)
+ return true;
+
+ if (_prepared_instances) {
+ assert(poly <= _prepared_instances->size());
+ maid.push(_instances);
+ _instances = _prepared_instances;
+ _prepared_instances = NULL;
+ }
+
+ assert(poly <= _instances->size());
+ _polyphony = poly;
+
+ return NodeBase::apply_poly(maid, poly);
+}
+
+
+/** Instantiate self from LV2 plugin descriptor.
+ *
+ * Implemented as a seperate function (rather than in the constructor) to
+ * allow graceful error-catching of broken plugins.
+ *
+ * Returns whether or not plugin was successfully instantiated. If return
+ * value is false, this object may not be used.
+ */
+bool
+LV2Node::instantiate()
+{
+ SharedPtr<LV2Info> info = _lv2_plugin->lv2_info();
+ SLV2Plugin plug = _lv2_plugin->slv2_plugin();
+
+ uint32_t num_ports = slv2_plugin_get_num_ports(plug);
+ assert(num_ports > 0);
+
+ _ports = new Raul::Array<PortImpl*>(num_ports, NULL);
+ _instances = new Raul::Array<SLV2Instance>(_polyphony, NULL);
+
+ uint32_t port_buffer_size = 0;
+
+ for (uint32_t i=0; i < _polyphony; ++i) {
+ (*_instances)[i] = slv2_plugin_instantiate(plug, _srate, info->lv2_features());
+ if ((*_instances)[i] == NULL) {
+ cerr << "Failed to instantiate plugin!" << endl;
+ return false;
+ }
+
+ const void* ctx_ext = slv2_instance_get_extension_data(
+ (*_instances)[i], LV2_CONTEXT_MESSAGE);
+
+ if (ctx_ext) {
+ cerr << "HAS CONTEXT EXTENSION" << endl;
+ if (_message_run == NULL)
+ _message_run = new MessageRunFuncs(_polyphony, NULL);
+ LV2MessageContext* mc = (LV2MessageContext*)ctx_ext;
+ (*_message_run)[i] = mc->message_run;
+ }
+ }
+
+ string port_name;
+ string port_path;
+
+ PortImpl* port = NULL;
+
+ float* def_values = new float[num_ports];
+ slv2_plugin_get_port_ranges_float(plug, 0, 0, def_values);
+
+ for (uint32_t j=0; j < num_ports; ++j) {
+ SLV2Port id = slv2_plugin_get_port_by_index(plug, j);
+
+ // LV2 shortnames are guaranteed to be unique, valid C identifiers
+ port_name = slv2_value_as_string(slv2_port_get_symbol(plug, id));
+
+ assert(port_name.find("/") == string::npos);
+
+ port_path = path() + "/" + port_name;
+
+ DataType data_type = DataType::UNKNOWN;
+ if (slv2_port_is_a(plug, id, info->control_class)) {
+ data_type = DataType::CONTROL;
+ port_buffer_size = 1;
+ } else if (slv2_port_is_a(plug, id, info->audio_class)) {
+ data_type = DataType::AUDIO;
+ port_buffer_size = _buffer_size;
+ } else if (slv2_port_is_a(plug, id, info->event_class)) {
+ data_type = DataType::EVENT;
+ port_buffer_size = _buffer_size;
+ }
+
+ enum { UNKNOWN, INPUT, OUTPUT } direction = UNKNOWN;
+ if (slv2_port_is_a(plug, id, info->input_class)) {
+ direction = INPUT;
+ } else if (slv2_port_is_a(plug, id, info->output_class)) {
+ direction = OUTPUT;
+ }
+
+ if (data_type == DataType::UNKNOWN || direction == UNKNOWN) {
+ delete _ports;
+ _ports = NULL;
+ delete _instances;
+ _instances = NULL;
+ return false;
+ }
+
+ // FIXME: need nice type preserving SLV2Value -> Raul::Atom conversion
+ float def = isnan(def_values[j]) ? 0.0f : def_values[j];
+ Atom defatm = def;
+
+ if (direction == INPUT)
+ port = new InputPort(this, port_name, j, _polyphony, data_type, defatm, port_buffer_size);
+ else
+ port = new OutputPort(this, port_name, j, _polyphony, data_type, defatm, port_buffer_size);
+
+ if (direction == INPUT && data_type == DataType::CONTROL)
+ ((AudioBuffer*)port->buffer(0))->set_value(def, 0, 0);
+
+ SLV2Value pred = slv2_value_new_uri(info->lv2_world(),
+ "http://lv2plug.in/ns/dev/contexts#context");
+ SLV2Values contexts = slv2_port_get_value(plug, id, pred);
+ for (uint32_t i = 0; i < slv2_values_size(contexts); ++i) {
+ SLV2Value c = slv2_values_get_at(contexts, i);
+ const char* context = slv2_value_as_string(c);
+ if (!strcmp("http://lv2plug.in/ns/dev/contexts#MessageContext", context)) {
+ cout << "MESSAGE CONTEXT!" << endl;
+ port->set_context(Context::MESSAGE);
+ } else {
+ cout << "UNKNOWN CONTEXT: "
+ << slv2_value_as_string(slv2_values_get_at(contexts, i))
+ << endl;
+ }
+ }
+
+ _ports->at(j) = port;
+ }
+
+ delete [] def_values;
+
+ return true;
+}
+
+
+void
+LV2Node::activate()
+{
+ NodeBase::activate();
+
+ for (uint32_t i=0; i < _polyphony; ++i) {
+ for (unsigned long j=0; j < num_ports(); ++j) {
+ PortImpl* const port = _ports->at(j);
+
+ set_port_buffer(i, j, port->buffer(i));
+
+ if (port->type() == DataType::CONTROL) {
+ ((AudioBuffer*)port->buffer(i))->set_value(port->value().get_float(), 0, 0);
+ } else if (port->type() == DataType::AUDIO) {
+ ((AudioBuffer*)port->buffer(i))->set_value(0.0f, 0, 0);
+ }
+ }
+ slv2_instance_activate((*_instances)[i]);
+ }
+}
+
+
+void
+LV2Node::deactivate()
+{
+ NodeBase::deactivate();
+
+ for (uint32_t i=0; i < _polyphony; ++i)
+ slv2_instance_deactivate((*_instances)[i]);
+}
+
+
+void
+LV2Node::message_process(MessageContext& context, uint32_t* output)
+{
+ // FIXME: voice
+ if (_message_run)
+ (*_message_run)[0]((*_instances)[0], output);
+
+ /* MESSAGE PROCESS */
+}
+
+
+void
+LV2Node::process(ProcessContext& context)
+{
+ NodeBase::pre_process(context);
+
+ for (uint32_t i=0; i < _polyphony; ++i)
+ slv2_instance_run((*_instances)[i], context.nframes());
+
+ NodeBase::post_process(context);
+}
+
+
+void
+LV2Node::set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf)
+{
+ assert(voice < _polyphony);
+
+ slv2_instance_connect_port((*_instances)[voice], port_num, buf->raw_data());
+}
+
+
+} // namespace Ingen
+