// ------------------------------------------------------------------------ // 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 #include #include #include #include #include #include #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::iterator q = chains.begin(); while(q != chains.end()) { delete *q; ++q; } vector::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 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(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 tokens = kvu_string_to_words(formatted_string); vector::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(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& 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& 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& 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& 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& 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 arg_indices; vector arg_slave_indices; vector 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* 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(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(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(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(impl_repp->slave_param_objects_rep.size())) { DBC_CHECK(static_cast(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::iterator q = chains.begin(); while(q != chains.end()) { (*q)->release(); ++q; } first_buffer = 0; } void PRESET::process(void) { vector::iterator p = buffers.begin(); while(p != buffers.end()) { (*p)->copy_all_content(*first_buffer); ++p; } vector::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(chains.size())); ++p; } } }