419 lines
13 KiB
C++
419 lines
13 KiB
C++
// ------------------------------------------------------------------------
|
|
// audiofx_ladspa.cpp: Wrapper class for LADSPA plugins
|
|
// Copyright (C) 2000-2004 Kai Vehmanen
|
|
//
|
|
// Attributes:
|
|
// eca-style-version: 3
|
|
//
|
|
// References:
|
|
// http://www.ladspa.org
|
|
//
|
|
// This program 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.
|
|
//
|
|
// This program 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 more 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
// ------------------------------------------------------------------------
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <dlfcn.h>
|
|
#include <kvu_utils.h>
|
|
#include <kvu_dbc.h>
|
|
#include <kvu_numtostr.h>
|
|
#include "samplebuffer.h"
|
|
#include "audiofx_ladspa.h"
|
|
#include "eca-error.h"
|
|
#include "eca-logger.h"
|
|
|
|
EFFECT_LADSPA::EFFECT_LADSPA (const LADSPA_Descriptor *pdesc) throw(ECA_ERROR&)
|
|
{
|
|
plugin_desc = pdesc;
|
|
if ((plugin_desc->Properties & LADSPA_PROPERTY_INPLACE_BROKEN) ==
|
|
LADSPA_PROPERTY_INPLACE_BROKEN)
|
|
throw(ECA_ERROR("AUDIOFX_LADSPA", "Inplace-broken plugins not supported."));
|
|
|
|
/* FIXME: strip linefeeds and other forbidden characters; write down to
|
|
* to ECA_OBJECT docs what chars are allowed and what are not... */
|
|
name_rep = string(plugin_desc->Name);
|
|
unique_rep = string(plugin_desc->Label);
|
|
maker_rep = string(plugin_desc->Maker);
|
|
unique_number_rep = static_cast<long int>(plugin_desc->UniqueID);
|
|
buffer_repp = 0;
|
|
|
|
init_ports();
|
|
}
|
|
|
|
EFFECT_LADSPA::~EFFECT_LADSPA (void)
|
|
{
|
|
release();
|
|
|
|
if (plugin_desc != 0) {
|
|
for(unsigned int n = 0; n < plugins_rep.size(); n++) {
|
|
if (plugin_desc->deactivate != 0)
|
|
plugin_desc->deactivate(plugins_rep[n]);
|
|
if (plugin_desc->cleanup != 0)
|
|
plugin_desc->cleanup(plugins_rep[n]);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string EFFECT_LADSPA::description(void) const
|
|
{
|
|
return name_rep + " - Author: '" + maker_rep + "'";
|
|
}
|
|
|
|
EFFECT_LADSPA* EFFECT_LADSPA::clone(void) const
|
|
{
|
|
EFFECT_LADSPA* result = new EFFECT_LADSPA(plugin_desc);
|
|
for(int n = 0; n < number_of_params(); n++) {
|
|
result->set_parameter(n + 1, get_parameter(n + 1));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void EFFECT_LADSPA::init_ports(void)
|
|
{
|
|
// note: run from plugin constructor
|
|
|
|
port_count_rep = plugin_desc->PortCount;
|
|
in_audio_ports = 0;
|
|
out_audio_ports = 0;
|
|
|
|
for(unsigned long m = 0; m < port_count_rep; m++) {
|
|
if ((plugin_desc->PortDescriptors[m] & LADSPA_PORT_AUDIO) == LADSPA_PORT_AUDIO) {
|
|
if ((plugin_desc->PortDescriptors[m] & LADSPA_PORT_INPUT) == LADSPA_PORT_INPUT)
|
|
++in_audio_ports;
|
|
else
|
|
++out_audio_ports;
|
|
}
|
|
|
|
if ((plugin_desc->PortDescriptors[m] & LADSPA_PORT_CONTROL) == LADSPA_PORT_CONTROL) {
|
|
|
|
struct PARAM_DESCRIPTION pd;
|
|
parse_parameter_hint_information(m, params.size() + 1, &pd);
|
|
|
|
params.push_back(pd.default_value);
|
|
param_descs_rep.push_back(pd);
|
|
if (params.size() > 1) param_names_rep += ",";
|
|
string tmp (kvu_string_search_and_replace(string(plugin_desc->PortNames[m]), ",", "\\,"));
|
|
param_names_rep += kvu_string_search_and_replace(tmp, ":", "\\:");
|
|
}
|
|
}
|
|
}
|
|
|
|
void EFFECT_LADSPA::parse_parameter_hint_information(int portnum, int paramnum, struct PARAM_DESCRIPTION *pd)
|
|
{
|
|
LADSPA_PortRangeHintDescriptor hintdescriptor = plugin_desc->PortRangeHints[portnum].HintDescriptor;
|
|
|
|
/* if srate not set, use 44.1kHz (used only for calculating
|
|
* param hint values */
|
|
SAMPLE_SPECS::sample_rate_t srate = samples_per_second();
|
|
/* FIXME: this is just ugly! */
|
|
if (srate <= 0) { srate = 44100; }
|
|
|
|
/**
|
|
* For LADSPA v1.1, parameter hint information is
|
|
* used as advertised by the plugin. LADSPA v1.0
|
|
* API doesn't specify how to initialize control
|
|
* ports to sane initial values, so we just try to
|
|
* make an educated guess based on the lower and
|
|
* upper bounds.
|
|
*
|
|
* 1) v1.1 hint information available
|
|
* 2) lowb == x and upperb == n/a
|
|
* a) x < 0, initval = 0
|
|
* b) x >= 0, initval = x
|
|
* 3) lowb == n/a and upperb == x
|
|
* a) x > 0, initval = 0
|
|
* b) x <= 0, initval = x
|
|
* 4) lowb == x and upperb == y
|
|
* a) x < 0 and y > 0, initval = 0
|
|
* b) x < 0 and y < 0, initval = y
|
|
* c) x > 0 and y > 0, initval = x
|
|
* 5) lowb == n/a and upperb == n/a, initval = 1
|
|
*/
|
|
|
|
/* parameter name */
|
|
pd->description = get_parameter_name(paramnum);
|
|
|
|
/* upper and lower bounds */
|
|
if (LADSPA_IS_HINT_BOUNDED_BELOW(hintdescriptor)) {
|
|
pd->bounded_below = true;
|
|
|
|
if (LADSPA_IS_HINT_SAMPLE_RATE(hintdescriptor))
|
|
pd->lower_bound = plugin_desc->PortRangeHints[portnum].LowerBound * srate;
|
|
else
|
|
pd->lower_bound = plugin_desc->PortRangeHints[portnum].LowerBound;
|
|
}
|
|
else {
|
|
pd->bounded_below = false;
|
|
}
|
|
|
|
if (LADSPA_IS_HINT_BOUNDED_ABOVE(hintdescriptor)) {
|
|
pd->bounded_above = true;
|
|
|
|
if (LADSPA_IS_HINT_SAMPLE_RATE(hintdescriptor))
|
|
pd->upper_bound = plugin_desc->PortRangeHints[portnum].UpperBound * srate;
|
|
else
|
|
pd->upper_bound = plugin_desc->PortRangeHints[portnum].UpperBound;
|
|
}
|
|
else {
|
|
pd->bounded_above = false;
|
|
}
|
|
|
|
/* defaults - case 1 */
|
|
if (LADSPA_IS_HINT_HAS_DEFAULT(hintdescriptor)) {
|
|
if (LADSPA_IS_HINT_DEFAULT_MINIMUM(hintdescriptor)) {
|
|
pd->default_value = pd->lower_bound;
|
|
}
|
|
|
|
/* FIXME: add support for logarithmic defaults */
|
|
|
|
else if (LADSPA_IS_HINT_DEFAULT_LOW(hintdescriptor)) {
|
|
pd->default_value = pd->lower_bound * 0.75f + pd->upper_bound * 0.25f;
|
|
}
|
|
else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(hintdescriptor)) {
|
|
pd->default_value = pd->lower_bound * 0.50f + pd->upper_bound * 0.50f;
|
|
}
|
|
else if (LADSPA_IS_HINT_DEFAULT_HIGH(hintdescriptor)) {
|
|
pd->default_value = pd->lower_bound * 0.25f + pd->upper_bound * 0.75f;
|
|
}
|
|
else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(hintdescriptor)) {
|
|
pd->default_value = pd->upper_bound;
|
|
}
|
|
else if (LADSPA_IS_HINT_DEFAULT_0(hintdescriptor)) {
|
|
pd->default_value = 0.0f;
|
|
}
|
|
else if (LADSPA_IS_HINT_DEFAULT_1(hintdescriptor)) {
|
|
pd->default_value = 1.0f;
|
|
}
|
|
else if (LADSPA_IS_HINT_DEFAULT_100(hintdescriptor)) {
|
|
pd->default_value = 100.0f;
|
|
}
|
|
else if (LADSPA_IS_HINT_DEFAULT_440(hintdescriptor)) {
|
|
pd->default_value = 440.0f;
|
|
}
|
|
else {
|
|
ECA_LOG_MSG(ECA_LOGGER::info,
|
|
"No LADSPA hint info found for plugin '" + name_rep + "'.");
|
|
}
|
|
}
|
|
|
|
/* defaults - case 2 */
|
|
else if (LADSPA_IS_HINT_BOUNDED_BELOW(hintdescriptor) &&
|
|
!LADSPA_IS_HINT_BOUNDED_ABOVE(hintdescriptor)) {
|
|
|
|
if (pd->lower_bound < 0) pd->default_value = 0.0f;
|
|
else pd->default_value = pd->lower_bound;
|
|
}
|
|
|
|
/* defaults - case 3 */
|
|
else if (!LADSPA_IS_HINT_BOUNDED_BELOW(hintdescriptor) &&
|
|
LADSPA_IS_HINT_BOUNDED_ABOVE(hintdescriptor)) {
|
|
|
|
if (pd->upper_bound > 0) pd->default_value = 0.0f;
|
|
else pd->default_value = pd->upper_bound;
|
|
}
|
|
|
|
/* defaults - case 4 */
|
|
else if (LADSPA_IS_HINT_BOUNDED_BELOW(hintdescriptor) &&
|
|
LADSPA_IS_HINT_BOUNDED_ABOVE(hintdescriptor)) {
|
|
|
|
if (pd->lower_bound < 0 && pd->upper_bound > 0) pd->default_value = 0.0f;
|
|
else if (pd->lower_bound < 0 && pd->upper_bound < 0) pd->default_value = pd->upper_bound;
|
|
else pd->default_value = pd->lower_bound;
|
|
}
|
|
|
|
/* defaults - case 5 */
|
|
else {
|
|
DBC_CHECK(!LADSPA_IS_HINT_BOUNDED_BELOW(hintdescriptor) &&
|
|
!LADSPA_IS_HINT_BOUNDED_ABOVE(hintdescriptor));
|
|
|
|
if (LADSPA_IS_HINT_SAMPLE_RATE(hintdescriptor))
|
|
pd->default_value = srate;
|
|
else
|
|
pd->default_value = 1.0f;
|
|
}
|
|
|
|
if (LADSPA_IS_HINT_TOGGLED(hintdescriptor))
|
|
pd->toggled = true;
|
|
else
|
|
pd->toggled = false;
|
|
|
|
if (LADSPA_IS_HINT_INTEGER(hintdescriptor))
|
|
pd->integer = true;
|
|
else
|
|
pd->integer = false;
|
|
|
|
if (LADSPA_IS_HINT_LOGARITHMIC(hintdescriptor))
|
|
pd->logarithmic = true;
|
|
else
|
|
pd->logarithmic = false;
|
|
|
|
if ((plugin_desc->PortDescriptors[portnum] & LADSPA_PORT_OUTPUT) == LADSPA_PORT_CONTROL)
|
|
pd->output = true;
|
|
else
|
|
pd->output = false;
|
|
}
|
|
|
|
void EFFECT_LADSPA::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const
|
|
{
|
|
DBC_CHECK(param >= 0);
|
|
DBC_CHECK(param <= static_cast<int>(param_descs_rep.size()));
|
|
*pd = param_descs_rep[param - 1];
|
|
}
|
|
|
|
void EFFECT_LADSPA::set_parameter(int param, CHAIN_OPERATOR::parameter_t value)
|
|
{
|
|
if (param > 0 && (param - 1 < static_cast<int>(params.size()))) {
|
|
// cerr << "ladspa: setting param " << param << " to " << value << "." << endl;
|
|
params[param - 1] = value;
|
|
}
|
|
}
|
|
|
|
CHAIN_OPERATOR::parameter_t EFFECT_LADSPA::get_parameter(int param) const
|
|
{
|
|
if (param > 0 && (param - 1 < static_cast<int>(params.size()))) {
|
|
// cerr << "ladspa: getting param " << param << " with value " << params[param - 1] << "." << endl;
|
|
return(params[param - 1]);
|
|
}
|
|
return(0.0);
|
|
}
|
|
|
|
int EFFECT_LADSPA::output_channels(int i_channels) const
|
|
{
|
|
// note: We have two separate cases: either one plugin
|
|
// is instantiated for each channel, or one plugin
|
|
// per chain. See EFFECT_LADSPA::init().
|
|
|
|
if (in_audio_ports > 1 ||
|
|
out_audio_ports > 1) {
|
|
|
|
return out_audio_ports;
|
|
}
|
|
|
|
return i_channels;
|
|
}
|
|
|
|
void EFFECT_LADSPA::init(SAMPLE_BUFFER *insample)
|
|
{
|
|
EFFECT_BASE::init(insample);
|
|
|
|
DBC_CHECK(samples_per_second() > 0);
|
|
|
|
if (buffer_repp != insample) {
|
|
release();
|
|
buffer_repp = insample;
|
|
buffer_repp->get_pointer_reflock();
|
|
}
|
|
|
|
if (plugin_desc != 0) {
|
|
for(unsigned int n = 0; n < plugins_rep.size(); n++) {
|
|
if (plugin_desc->deactivate != 0) plugin_desc->deactivate(plugins_rep[n]);
|
|
plugin_desc->cleanup(plugins_rep[n]);
|
|
}
|
|
}
|
|
|
|
// NOTE: the fancy definition :)
|
|
// if ((in_audio_ports > 1 &&
|
|
// in_audio_ports <= channels() &&
|
|
// out_audio_ports <= channels()) ||
|
|
// (out_audio_ports > 1 &&
|
|
// in_audio_ports <= channels() &&
|
|
// out_audio_ports <= channels())) {}
|
|
|
|
if (in_audio_ports > 1 ||
|
|
out_audio_ports > 1) {
|
|
plugins_rep.resize(1);
|
|
plugins_rep[0] = reinterpret_cast<LADSPA_Handle*>(plugin_desc->instantiate(plugin_desc, samples_per_second()));
|
|
int inport = 0;
|
|
int outport = 0;
|
|
for(unsigned long m = 0; m < port_count_rep; m++) {
|
|
if ((plugin_desc->PortDescriptors[m] & LADSPA_PORT_AUDIO) == LADSPA_PORT_AUDIO) {
|
|
if ((plugin_desc->PortDescriptors[m] & LADSPA_PORT_INPUT) == LADSPA_PORT_INPUT) {
|
|
if (inport < channels())
|
|
plugin_desc->connect_port(plugins_rep[0], m, buffer_repp->buffer[inport]);
|
|
++inport;
|
|
}
|
|
else {
|
|
if (outport < channels())
|
|
plugin_desc->connect_port(plugins_rep[0], m, buffer_repp->buffer[outport]);
|
|
++outport;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (inport > channels())
|
|
ECA_LOG_MSG(ECA_LOGGER::info,
|
|
"WARNING: chain has less channels than plugin has input ports ("
|
|
+ name() + ").");
|
|
if (outport > channels())
|
|
ECA_LOG_MSG(ECA_LOGGER::info,
|
|
"WARNING: chain has less channels than plugin has output ports ("
|
|
+ name() + ").");
|
|
}
|
|
else {
|
|
plugins_rep.resize(channels());
|
|
for(unsigned int n = 0; n < plugins_rep.size(); n++) {
|
|
plugins_rep[n] = reinterpret_cast<LADSPA_Handle*>(plugin_desc->instantiate(plugin_desc, samples_per_second()));
|
|
|
|
for(unsigned long m = 0; m < port_count_rep; m++) {
|
|
if ((plugin_desc->PortDescriptors[m] & LADSPA_PORT_AUDIO) == LADSPA_PORT_AUDIO) {
|
|
plugin_desc->connect_port(plugins_rep[n], m, buffer_repp->buffer[n]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ECA_LOG_MSG(ECA_LOGGER::system_objects,
|
|
"Instantiated " +
|
|
kvu_numtostr(plugins_rep.size()) +
|
|
" LADSPA plugin(s), each with " +
|
|
kvu_numtostr(in_audio_ports) +
|
|
" audio input port(s) and " +
|
|
kvu_numtostr(out_audio_ports) +
|
|
" output port(s), to chain with " +
|
|
kvu_numtostr(channels()) +
|
|
" channel(s) and srate of " +
|
|
kvu_numtostr(samples_per_second()) +
|
|
".");
|
|
|
|
int data_index = 0;
|
|
for(unsigned long m = 0; m < port_count_rep; m++) {
|
|
if ((plugin_desc->PortDescriptors[m] & LADSPA_PORT_CONTROL) ==
|
|
LADSPA_PORT_CONTROL) {
|
|
for(unsigned int n = 0; n < plugins_rep.size(); n++) {
|
|
plugin_desc->connect_port(plugins_rep[n], m, &(params[data_index]));
|
|
}
|
|
++data_index;
|
|
}
|
|
}
|
|
for(unsigned long m = 0; m < plugins_rep.size(); m++)
|
|
if (plugin_desc->activate != 0) plugin_desc->activate(plugins_rep[m]);
|
|
}
|
|
|
|
void EFFECT_LADSPA::release(void)
|
|
{
|
|
if (buffer_repp != 0) {
|
|
buffer_repp->release_pointer_reflock();
|
|
}
|
|
buffer_repp = 0;
|
|
}
|
|
|
|
void EFFECT_LADSPA::process(void)
|
|
{
|
|
for(unsigned long m = 0; m < plugins_rep.size(); m++)
|
|
plugin_desc->run(plugins_rep[m], buffer_repp->length_in_samples());
|
|
}
|