From 93850c202de8b073a1ce1dd8bd246d407bce4e2f Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 30 Sep 2008 16:50:21 +0000 Subject: Flatten ingen source directory heirarchy a bit. git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@1551 a436a847-0d15-0410-975c-d299462d15a1 --- src/engine/LV2Node.cpp | 305 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 src/engine/LV2Node.cpp (limited to 'src/engine/LV2Node.cpp') 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 + * + * 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 +#include +#include +#include +#include +#include +#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(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 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(num_ports, NULL); + _instances = new Raul::Array(_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 + -- cgit v1.2.1