/* This file is part of Ingen. * Copyright (C) 2007-2009 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 "raul/Maid.hpp" #include #include "LADSPANode.hpp" #include "AudioBuffer.hpp" #include "InputPort.hpp" #include "OutputPort.hpp" #include "PluginImpl.hpp" #include "ProcessContext.hpp" using namespace std; using namespace Raul; namespace Ingen { using namespace Shared; /** Partially construct a LADSPANode. * * Object is not usable until instantiate() is called with success. * (It _will_ crash!) */ LADSPANode::LADSPANode(PluginImpl* plugin, const string& path, bool polyphonic, PatchImpl* parent, const LADSPA_Descriptor* descriptor, SampleRate srate, size_t buffer_size) : NodeBase(plugin, path, polyphonic, parent, srate, buffer_size) , _descriptor(descriptor) , _instances(NULL) , _prepared_instances(NULL) { assert(_descriptor != NULL); } LADSPANode::~LADSPANode() { for (uint32_t i=0; i < _polyphony; ++i) _descriptor->cleanup((*_instances)[i]); delete _instances; } bool LADSPANode::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) { _prepared_instances->at(i) = _descriptor->instantiate(_descriptor, _srate); if (_prepared_instances->at(i) == NULL) { cerr << "Failed to instantiate plugin!" << endl; return false; } // Initialize the values of new ports for (unsigned long j=0; j < num_ports(); ++j) { PortImpl* const port = _ports->at(j); Buffer *buffer = port->prepared_buffer(i); // FIXME: Preserve individual voice values if (port->type() == DataType::CONTROL) { ((AudioBuffer*)buffer)->set_value(port->value().get_float(), 0, 0); } else if (port->type() == DataType::AUDIO) { ((AudioBuffer*)buffer)->set_value(0.0f, 0, 0); } } if (_activated && _descriptor->activate) _descriptor->activate(_prepared_instances->at(i)); } return true; } bool LADSPANode::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); } static string nameify_if_invalid(const string& name) { if (Path::is_valid_name(name)) { return name; } else { const string new_name = Path::nameify(name); assert(Path::is_valid_name(new_name)); return new_name; } } /** Instantiate self from LADSPA 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 LADSPANode::instantiate() { if (!_ports) _ports = new Raul::Array(_descriptor->PortCount); _instances = new Raul::Array(_polyphony, NULL); size_t port_buffer_size = 0; for (uint32_t i=0; i < _polyphony; ++i) { (*_instances)[i] = _descriptor->instantiate(_descriptor, _srate); if ((*_instances)[i] == NULL) { cerr << "Failed to instantiate plugin!" << endl; return false; } } PortImpl* port = NULL; std::map names; for (uint32_t j=0; j < _descriptor->PortCount; ++j) { string port_name = nameify_if_invalid(_descriptor->PortNames[j]); string name = port_name; std::map::iterator existing = names.find(port_name); uint32_t offset = 2; bool type_clash = false; while (existing != names.end()) { const uint32_t e = existing->second; if (!type_clash && LADSPA_IS_PORT_CONTROL(_descriptor->PortDescriptors[j]) && LADSPA_IS_PORT_AUDIO(_descriptor->PortDescriptors[e])) { name = port_name + "_CR"; type_clash = true; } else if (!type_clash && LADSPA_IS_PORT_AUDIO(_descriptor->PortDescriptors[j]) && LADSPA_IS_PORT_CONTROL(_descriptor->PortDescriptors[e])) { name = port_name + "_AR"; type_clash = true; } else { std::ostringstream ss; ss << port_name << "_" << offset; name = ss.str(); } existing = names.find(name); } port_name = name; names.insert(make_pair(port_name, j)); Path port_path(path().child(port_name)); DataType type = DataType::AUDIO; port_buffer_size = _buffer_size; if (LADSPA_IS_PORT_CONTROL(_descriptor->PortDescriptors[j])) { type = DataType::CONTROL; port_buffer_size = 1; } else { assert(LADSPA_IS_PORT_AUDIO(_descriptor->PortDescriptors[j])); } assert (LADSPA_IS_PORT_INPUT(_descriptor->PortDescriptors[j]) || LADSPA_IS_PORT_OUTPUT(_descriptor->PortDescriptors[j])); boost::optional default_val, min, max; get_port_limits(j, default_val, min, max); const float value = default_val ? default_val.get() : 0.0f; if (LADSPA_IS_PORT_INPUT(_descriptor->PortDescriptors[j])) { port = new InputPort(this, port_name, j, _polyphony, type, value, port_buffer_size); _ports->at(j) = port; } else if (LADSPA_IS_PORT_OUTPUT(_descriptor->PortDescriptors[j])) { port = new OutputPort(this, port_name, j, _polyphony, type, value, port_buffer_size); _ports->at(j) = port; } assert(port); assert(_ports->at(j) == port); // Work around broke-ass crackhead plugins if (default_val && default_val.get() < min.get()) { cerr << "WARNING: Broken LADSPA " << _descriptor->UniqueID << ": Port default < minimum. Minimum adjusted." << endl; min = default_val; } if (default_val && default_val.get() > max.get()) { cerr << "WARNING: Broken LADSPA " << _descriptor->UniqueID << ": Maximum adjusted." << endl; max = default_val; } // Set initial/default value if (port->buffer_size() == 1) { for (uint32_t i=0; i < _polyphony; ++i) ((AudioBuffer*)port->buffer(i))->set_value(value, 0, 0); } if (port->is_input() && port->buffer_size() == 1) { if (min) port->set_meta_property("lv2:minimum", min.get()); if (max) port->set_meta_property("lv2:maximum", max.get()); if (default_val) port->set_meta_property("lv2:default", default_val.get()); } } return true; } void LADSPANode::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); } } if (_descriptor->activate != NULL) _descriptor->activate((*_instances)[i]); } } void LADSPANode::deactivate() { NodeBase::deactivate(); for (uint32_t i=0; i < _polyphony; ++i) if (_descriptor->deactivate != NULL) _descriptor->deactivate((*_instances)[i]); } void LADSPANode::process(ProcessContext& context) { NodeBase::pre_process(context); for (uint32_t i=0; i < _polyphony; ++i) _descriptor->run((*_instances)[i], context.nframes()); NodeBase::post_process(context); } void LADSPANode::set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) { assert(voice < _polyphony); AudioBuffer* audio_buffer = dynamic_cast(buf); assert(audio_buffer); if (port_num < _descriptor->PortCount) _descriptor->connect_port((*_instances)[voice], port_num, audio_buffer->data()); } void LADSPANode::get_port_limits(unsigned long port_index, boost::optional& normal, boost::optional& lower, boost::optional& upper) { LADSPA_PortRangeHintDescriptor hint_descriptor = _descriptor->PortRangeHints[port_index].HintDescriptor; /* set upper and lower, possibly adjusted to the sample rate */ if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) { upper = _descriptor->PortRangeHints[port_index].UpperBound * _srate; lower = _descriptor->PortRangeHints[port_index].LowerBound * _srate; } else { upper = _descriptor->PortRangeHints[port_index].UpperBound; lower = _descriptor->PortRangeHints[port_index].LowerBound; } if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { /* FLT_EPSILON is defined as the different between 1.0 and the minimum * float greater than 1.0. So, if lower is < FLT_EPSILON, it will be 1.0 * and the logarithmic control will have a base of 1 and thus not change */ if (lower.get() < FLT_EPSILON) lower = FLT_EPSILON; } if (LADSPA_IS_HINT_HAS_DEFAULT(hint_descriptor)) { if (LADSPA_IS_HINT_DEFAULT_MINIMUM(hint_descriptor)) { normal = lower; } else if (LADSPA_IS_HINT_DEFAULT_LOW(hint_descriptor)) { assert(lower); assert(upper); if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { normal = exp(log(lower.get()) * 0.75 + log(upper.get()) * 0.25); } else { normal = lower.get() * 0.75 + upper.get() * 0.25; } } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(hint_descriptor)) { assert(lower); assert(upper); if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { normal = exp(log(lower.get()) * 0.5 + log(upper.get()) * 0.5); } else { normal = lower.get() * 0.5 + upper.get() * 0.5; } } else if (LADSPA_IS_HINT_DEFAULT_HIGH(hint_descriptor)) { assert(lower); assert(upper); if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { normal = exp(log(lower.get()) * 0.25 + log(upper.get()) * 0.75); } else { normal = lower.get() * 0.25 + upper.get() * 0.75; } } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(hint_descriptor)) { assert(upper); normal = upper.get(); } else if (LADSPA_IS_HINT_DEFAULT_0(hint_descriptor)) { normal = 0.0; } else if (LADSPA_IS_HINT_DEFAULT_1(hint_descriptor)) { normal = 1.0; } else if (LADSPA_IS_HINT_DEFAULT_100(hint_descriptor)) { normal = 100.0; } else if (LADSPA_IS_HINT_DEFAULT_440(hint_descriptor)) { normal = 440.0; } } else { // No default hint if (LADSPA_IS_HINT_BOUNDED_BELOW(hint_descriptor)) { assert(lower); normal = lower.get(); } else if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint_descriptor)) { assert(upper); normal = upper.get(); } } } } // namespace Ingen