581 lines
17 KiB
C++
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;
|
|
}
|
|
}
|
|
}
|