sintonia/library/ecasound-2.7.2/libecasound/preset.cpp

581 lines
17 KiB
C++

// ------------------------------------------------------------------------
// preset.cpp: Class for representing effect presets
// Copyright (C) 2000-2002,2004-2007,2009 Kai Vehmanen
// Copyright (C) 2001 Arto Hamara
//
// Attributes:
// eca-style-version: 3
//
// 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
// ------------------------------------------------------------------------
#include <cstdlib>
#include <iostream>
#include <vector>
#include <string>
#include <kvu_dbc.h>
#include <kvu_numtostr.h>
#include <kvu_utils.h>
#include "eca-chain.h"
#include "eca-chainop.h"
#include "generic-controller.h"
#include "eca-object-factory.h"
#include "samplebuffer.h"
#include "eca-logger.h"
#include "eca-error.h"
#include "preset.h"
#include "preset_impl.h"
using std::string;
using std::vector;
using std::cerr;
using std::endl;
PRESET::PRESET(void)
{
impl_repp = new PRESET_impl();
impl_repp->parsed_rep = false;
}
PRESET::PRESET(const string& formatted_string)
{
impl_repp = new PRESET_impl();
parse(formatted_string);
}
PRESET::~PRESET(void)
{
vector<CHAIN*>::iterator q = chains.begin();
while(q != chains.end()) {
delete *q;
++q;
}
vector<SAMPLE_BUFFER*>::iterator p = buffers.begin();
while(p != buffers.end()) {
if (p != buffers.begin()) delete *p; // first buffer points to an
// outside buffer -> not
// deleted here
++p;
}
for(size_t n = 0; n < impl_repp->pardesclist_rep.size(); n++) {
delete impl_repp->pardesclist_rep[n];
impl_repp->pardesclist_rep[n] = 0;
}
// NOTE: chainops and controllers are deleted in CHAIN::~CHAIN()
delete impl_repp;
impl_repp = 0;
}
PRESET* PRESET::clone(void) const
{
vector<parameter_t> param_values;
for(int n = 0; n < number_of_params(); n++) {
param_values.push_back(get_parameter(n + 1));
}
PRESET* preset = new PRESET(impl_repp->parse_string_rep);
for(int n = 0; n < preset->number_of_params(); n++) {
preset->set_parameter(n + 1, param_values[n]);
}
return preset;
}
PRESET* PRESET::new_expr(void) const
{
return new PRESET(impl_repp->parse_string_rep);
}
void PRESET::set_samples_per_second(SAMPLE_SPECS::sample_rate_t v)
{
for(size_t q = 0; q < chains.size(); q++) {
chains[q]->set_samples_per_second(v);
}
ECA_SAMPLERATE_AWARE::set_samples_per_second(v);
}
string PRESET::name(void) const
{
return impl_repp->name_rep;
}
string PRESET::description(void) const
{
return impl_repp->description_rep;
}
void PRESET::set_name(const string& v)
{
impl_repp->name_rep = v;
}
/**
* Whether preset data has been parsed
*/
bool PRESET::is_parsed(void) const
{
return impl_repp->parsed_rep;
}
void PRESET::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const
{
if (param > 0 && param <= static_cast<int>(impl_repp->pardesclist_rep.size()))
*pd = *impl_repp->pardesclist_rep[param - 1];
}
/**
* Parse preset data from the formatted string given
* as argument.
*
* require:
* formatted_string.empty() == false
* ensure:
* is_parsed() == true
*/
void PRESET::parse(const string& formatted_string)
{
// --------
DBC_REQUIRE(formatted_string.empty() == false);
// --------
impl_repp->parse_string_rep = formatted_string;
chains.clear();
chains.push_back(new CHAIN());
chains.back()->set_samples_per_second(samples_per_second());
// FIXME: add support for quotes (ie. "one token with space" style)
vector<string> tokens = kvu_string_to_words(formatted_string);
vector<string>::const_iterator p = tokens.begin();
while(p != tokens.end()) {
ECA_LOG_MSG(ECA_LOGGER::user_objects, "Parsing: " + *p + ".");
/* case 1: new chain */
if (*p == "|") {
add_chain();
}
/* case 2: preset specific option */
else if (is_preset_option(*p) == true) {
parse_preset_option(*p);
}
/* case 3: ecasound cop option */
else {
parse_operator_option(*p);
}
++p;
}
impl_repp->parsed_rep = true;
// --------
DBC_ENSURE(is_parsed() == true);
// --------
}
bool PRESET::is_preset_option(const string& arg) const
{
if (arg.size() < 2 ||
arg[0] != '-') return false;
switch(arg[1]) {
case 'p':
{
if (arg.size() < 3) return false;
switch(arg[2]) {
case 'd':
case 'p':
return true;
default: { }
}
}
default: { }
}
return false;
}
void PRESET::parse_preset_option(const string& arg)
{
if (arg.size() < 2) return;
if (arg[0] != '-') return;
switch(arg[1]) {
case 'p':
{
if (arg.size() < 3) return;
switch(arg[2]) {
case 'd':
{
/* -pd:preset_description */
impl_repp->description_rep = kvu_get_argument_number(1, arg);
break;
}
case 'p':
{
if (arg.size() < 4) return;
switch(arg[3]) {
case 'd':
{
/* -ppd:x,y,z (param default values) */
set_preset_defaults(kvu_get_arguments(arg));
break;
}
case 'n':
{
/* -ppn:x,y,z (param names) */
set_preset_param_names(kvu_get_arguments(arg));
break;
}
case 'l':
{
/* -ppl:x,y,z (param lower bounds) */
set_preset_lower_bounds(kvu_get_arguments(arg));
break;
}
case 'u':
{
/* -ppu:x,y,z (param upper bounds) */
set_preset_upper_bounds(kvu_get_arguments(arg));
break;
}
case 't':
{
/* -ppt:x,y,z (param toggle) */
set_preset_toggles(kvu_get_arguments(arg));
break;
}
default:
{
ECA_LOG_MSG(ECA_LOGGER::info, "Unknown preset option (1) " + arg + ".");
break;
}
}
break; /* -pp */
}
default: { ECA_LOG_MSG(ECA_LOGGER::info, "Unknown preset option (2) " + arg + "."); break; }
}
break; /* -p */
}
default: { ECA_LOG_MSG(ECA_LOGGER::info, "Unknown preset option (3) " + arg + "."); break; }
}
}
void PRESET::extend_pardesc_vector(int number)
{
while (static_cast<int>(impl_repp->pardesclist_rep.size()) < number) {
DBC_DECLARE(size_t oldsize = impl_repp->pardesclist_rep.size());
impl_repp->pardesclist_rep.push_back(new OPERATOR::PARAM_DESCRIPTION());
// cerr << "(preset) adding pardesclist_rep item." << endl;
DBC_CHECK(impl_repp->pardesclist_rep.size() == oldsize + 1);
}
}
void PRESET::set_preset_defaults(const vector<string>& args)
{
extend_pardesc_vector(args.size());
for(size_t n = 0; n < args.size(); n++) {
if (args[n].size() > 0 && args[n][0] == '-') continue;
impl_repp->pardesclist_rep[n]->default_value = atof(args[n].c_str());
set_parameter(n + 1, impl_repp->pardesclist_rep[n]->default_value);
// cerr << "(preset) setting default for " << n << " to " << impl_repp->pardesclist_rep[n]->default_value << "." << endl;
}
}
void PRESET::set_preset_param_names(const vector<string>& args)
{
impl_repp->preset_param_names_rep.resize(args.size());
for(size_t n = 0; n < args.size(); n++) {
impl_repp->preset_param_names_rep[n] = args[n];
// cerr << "(preset) setting param name for " << n << " to " << impl_repp->preset_param_names_rep[n] << "." << endl;
}
}
void PRESET::set_preset_lower_bounds(const vector<string>& args)
{
extend_pardesc_vector(args.size());
for(size_t n = 0; n < args.size(); n++) {
if (args[n].size() > 0 && args[n][0] == '-') {
impl_repp->pardesclist_rep[n]->bounded_below = false;
}
else {
impl_repp->pardesclist_rep[n]->bounded_below = true;
impl_repp->pardesclist_rep[n]->lower_bound = atof(args[n].c_str());
// cerr << "(preset) setting lowbound for " << n << " to " << impl_repp->pardesclist_rep[n]->lower_bound << "." << endl;
}
}
}
void PRESET::set_preset_upper_bounds(const vector<string>& args)
{
extend_pardesc_vector(args.size());
for(size_t n = 0; n < args.size(); n++) {
if (args[n].size() > 0 && args[n][0] == '-') {
impl_repp->pardesclist_rep[n]->bounded_above = false;
}
else {
impl_repp->pardesclist_rep[n]->bounded_above = true;
impl_repp->pardesclist_rep[n]->upper_bound = atof(args[n].c_str());
// cerr << "(preset) setting upperbound for " << n << " to " << impl_repp->pardesclist_rep[n]->upper_bound << "." << endl;
}
}
}
void PRESET::set_preset_toggles(const vector<string>& args)
{
extend_pardesc_vector(args.size());
for(size_t n = 0; n < args.size(); n++) {
impl_repp->pardesclist_rep[n]->integer = false;
impl_repp->pardesclist_rep[n]->logarithmic = false;
impl_repp->pardesclist_rep[n]->output = false;
impl_repp->pardesclist_rep[n]->toggled = false;
if (args[n].find("i") != string::npos)
impl_repp->pardesclist_rep[n]->integer = true;
if (args[n].find("l") != string::npos)
impl_repp->pardesclist_rep[n]->logarithmic = true;
if (args[n].find("o") != string::npos)
impl_repp->pardesclist_rep[n]->output = true;
if (args[n].find("t") != string::npos)
impl_repp->pardesclist_rep[n]->toggled = true;
ECA_LOG_MSG(ECA_LOGGER::user_objects,
string("Setting preset toggles: integer=")
+ kvu_numtostr(impl_repp->pardesclist_rep[n]->integer)
+ ", log="
+ kvu_numtostr(impl_repp->pardesclist_rep[n]->logarithmic)
+ ", output="
+ kvu_numtostr(impl_repp->pardesclist_rep[n]->output)
+ ", toggle="
+ kvu_numtostr(impl_repp->pardesclist_rep[n]->toggled)
+ ".");
}
}
void PRESET::parse_operator_option(const string& arg)
{
CHAIN_OPERATOR *cop;
GENERIC_CONTROLLER* gctrl;
/* phase 1: parse for cop definitions -eabc:1.0,%param1,2.0 */
vector<int> arg_indices;
vector<int> arg_slave_indices;
vector<string> ps_parts(kvu_get_number_of_arguments(arg));
for(int i = 0; i < kvu_get_number_of_arguments(arg); i++) {
string onearg = kvu_get_argument_number(i + 1, arg);
if(onearg.size() > 0 && onearg[0] == '%') {
// FIXME: what if %xxx options are given in the "wrong" order?
size_t preset_index = atoi(kvu_get_argument_number(i + 1, arg).substr(1).c_str());
if (preset_index <= arg_indices.size()) {
preset_index = arg_indices.size() + 1;
}
arg_indices.push_back(preset_index);
arg_slave_indices.push_back(i + 1);
ps_parts[i] = "0.0";
// make sure param is mentioned in param_names list
if (impl_repp->preset_param_names_rep.size() < preset_index) {
impl_repp->preset_param_names_rep.resize(preset_index);
impl_repp->preset_param_names_rep[preset_index - 1] = string("arg-") + kvu_numtostr(preset_index);
}
}
else {
ps_parts[i] = onearg;
}
}
DBC_CHECK(arg_indices.size() == arg_slave_indices.size());
/* phase 2: 'ps' is set to -eabc:1.0,2.0,2.0 (no %-params) */
string ps = "-" + kvu_get_argument_prefix(arg) + ":" + kvu_vector_to_string(ps_parts, ",");
// cerr << "Creating object from '" << ps << "'." << endl;
/* phase 3: create an object using 'ps' */
DYNAMIC_OBJECT<SAMPLE_SPECS::sample_t>* object = 0;
cop = 0;
cop = ECA_OBJECT_FACTORY::create_chain_operator(ps);
if (cop == 0) cop = ECA_OBJECT_FACTORY::create_ladspa_plugin(ps);
if (cop != 0) {
chains.back()->add_chain_operator(cop);
chains.back()->selected_chain_operator_as_target();
object = cop;
}
else {
if (kvu_get_argument_prefix(ps) == "kx")
chains.back()->selected_controller_as_target();
else {
gctrl = ECA_OBJECT_FACTORY::create_controller(ps);
if (gctrl != 0) {
impl_repp->gctrls_rep.push_back(gctrl);
chains.back()->add_controller(gctrl);
}
object = gctrl;
}
}
/* phase 4: create an object using 'ps' */
if (object != 0) {
for(int i = 0; i < static_cast<int>(arg_indices.size()); i++) {
// NOTE: there's a one-to-many connection between
// presets' parameters and 'object-param' pairs
// (ie. one preset-parameter can control
// multiple object params (param_objects and
// param_indices)
size_t preset_index = arg_indices[i];
// NOTE: for instance for LADSPA plugins -el:label,par1,par2
// number_of_args is 3, but number_of_params is 2!
int slave_index = arg_slave_indices[i];
slave_index -= kvu_get_number_of_arguments(arg) - object->number_of_params();
if (preset_index > impl_repp->slave_param_objects_rep.size()) {
impl_repp->slave_param_objects_rep.resize(preset_index);
impl_repp->slave_param_indices_rep.resize(preset_index);
}
impl_repp->slave_param_objects_rep[preset_index - 1].push_back(object);
impl_repp->slave_param_indices_rep[preset_index - 1].push_back(slave_index);
// cerr << "Linking preset parameter " << preset_index << " to object '" << impl_repp->slave_param_objects_rep[preset_index - 1].back()->name() << "', and its parameter '" << impl_repp->slave_param_objects_rep[preset_index - 1].back()->get_parameter_name(impl_repp->slave_param_indices_rep[preset_index - 1].back()) << "'." << endl;
DBC_CHECK(impl_repp->slave_param_objects_rep.size() == impl_repp->slave_param_indices_rep.size());
}
}
}
void PRESET::add_chain(void)
{
chains.push_back(new CHAIN());
buffers.push_back(new SAMPLE_BUFFER());
}
string PRESET::parameter_names(void) const
{
return kvu_vector_to_string(impl_repp->preset_param_names_rep, ",");
}
void PRESET::set_parameter(int param, CHAIN_OPERATOR::parameter_t value)
{
if (param > 0 && param <= static_cast<int>(impl_repp->slave_param_objects_rep.size())) {
for(size_t n = 0; n < impl_repp->slave_param_objects_rep[param - 1].size(); n++) {
DBC_CHECK(static_cast<int>(impl_repp->slave_param_indices_rep.size()) > param - 1);
DBC_CHECK(impl_repp->slave_param_indices_rep[param - 1].size() > n);
int index = impl_repp->slave_param_indices_rep[param - 1][n];
// cerr << "Setting preset " << name() << " param " << param << " (" << impl_repp->slave_param_objects_rep[param-1][n]->get_parameter_name(param) << "), of object " << impl_repp->slave_param_objects_rep[param-1][n]->name() << ", with index number " << index << ", to value " << value << "." << endl;
impl_repp->slave_param_objects_rep[param-1][n]->set_parameter(index, value);
}
}
}
CHAIN_OPERATOR::parameter_t PRESET::get_parameter(int param) const
{
if (param > 0 && param <= static_cast<int>(impl_repp->slave_param_objects_rep.size())) {
DBC_CHECK(static_cast<int>(impl_repp->slave_param_indices_rep.size()) > param - 1);
if (impl_repp->slave_param_indices_rep[param - 1].size() > 0) {
int index = impl_repp->slave_param_indices_rep[param - 1][0];
DBC_CHECK(index > 0);
// cerr << "Getting preset " << name() << " param " << param << ", index number " << index cerr << " with value " << impl_repp->slave_param_objects_rep[param-1][0]->get_parameter(index) << "." << endl;
return impl_repp->slave_param_objects_rep[param-1][0]->get_parameter(index);
}
}
return 0.0f;
}
void PRESET::init(SAMPLE_BUFFER *insample)
{
DBC_CHECK(samples_per_second() > 0);
first_buffer = insample;
chains[0]->set_samples_per_second(samples_per_second());
chains[0]->init(first_buffer, first_buffer->number_of_channels(), first_buffer->number_of_channels());
for(size_t q = 1; q < chains.size(); q++) {
DBC_CHECK(q - 1 < buffers.size());
buffers[q - 1]->length_in_samples(first_buffer->length_in_samples());
buffers[q - 1]->number_of_channels(first_buffer->number_of_channels());
chains[q]->set_samples_per_second(samples_per_second());
chains[q]->init(buffers[q - 1],
first_buffer->number_of_channels(),
first_buffer->number_of_channels());
}
for(size_t n = 0; n < impl_repp->gctrls_rep.size(); n++) {
impl_repp->gctrls_rep[n]->init();
}
}
void PRESET::release(void)
{
/* reimplemented from CHAIN_OPERATOR base class;
* see init() */
vector<CHAIN*>::iterator q = chains.begin();
while(q != chains.end()) {
(*q)->release();
++q;
}
first_buffer = 0;
}
void PRESET::process(void)
{
vector<SAMPLE_BUFFER*>::iterator p = buffers.begin();
while(p != buffers.end()) {
(*p)->copy_all_content(*first_buffer);
++p;
}
vector<CHAIN*>::iterator q = chains.begin();
while(q != chains.end()) {
(*q)->process();
++q;
}
if (chains.size() > 1) {
first_buffer->divide_by(chains.size());
p = buffers.begin();
while(p != buffers.end()) {
first_buffer->add_with_weight(**p, static_cast<int>(chains.size()));
++p;
}
}
}