// ------------------------------------------------------------------------ // eca-chainsetup-parser.cpp: Functionality for parsing chainsetup // option syntax. // Copyright (C) 2001-2006 Kai Vehmanen // // 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 // ------------------------------------------------------------------------ #ifdef HAVE_CONFIG_H #include #endif #include /* find() */ #include /* DBC_* */ #include #include #include #include "audioio.h" #include "file-preset.h" #include "global-preset.h" #include "midiio.h" #include "midi-client.h" #include "midi-server.h" #include "generic-controller.h" #include "eca-chain.h" #include "eca-logger.h" #include "eca-object-factory.h" #include "eca-preset-map.h" #include "eca-chainsetup.h" #include "eca-chainsetup-parser.h" #include "eca-chainsetup-bufparams.h" using std::string; ECA_CHAINSETUP_PARSER::ECA_CHAINSETUP_PARSER(ECA_CHAINSETUP* csetup) : csetup_repp(csetup), last_audio_add_vector_repp(0) { } /** * Interprets one option. This is the most generic variant of * the interpretation routines; both global and object specific * options are handled. * * @pre argu.size() > 0 * @pre argu[0] == '-' * * @post (option succesfully interpreted && interpret_result() == true) || * (unknown or invalid option && interpret_result() != true) */ void ECA_CHAINSETUP_PARSER::interpret_option (const string& arg) { interpret_entry(); istatus_rep = false; interpret_global_option(arg); if (istatus_rep != true) interpret_object_option(arg); interpret_exit(arg); } /** * Interprets one option. All non-global options are ignored. Global * options can be interpreted multiple times and in any order. * * @pre argu.size() > 0 * @pre argu[0] == '-' * @post (option succesfully interpreted && interpretation_result() == true) || * (unknown or invalid option && interpretation_result() == false) */ void ECA_CHAINSETUP_PARSER::interpret_global_option (const string& arg) { interpret_entry(); ECA_LOG_MSG(ECA_LOGGER::system_objects, "Interpreting global option \"" + arg + "\"."); if (istatus_rep == false) interpret_general_option(arg); if (istatus_rep == false) interpret_processing_control(arg); if (istatus_rep == false) interpret_chains(arg); interpret_exit(arg); } /** * Interprets one option. All options not directly related to * ecasound objects are ignored. * * @pre argu.size() > 0 * @pre argu[0] == '-' * * @post (option succesfully interpreted && interpretation_result() == true) || * (unknown or invalid option && interpretation_result() == false) */ void ECA_CHAINSETUP_PARSER::interpret_object_option (const string& arg) { interpret_entry(); ECA_LOG_MSG(ECA_LOGGER::system_objects, "Interpreting object option \"" + arg + "\"."); interpret_chains(arg); if (istatus_rep == false) interpret_audio_format(arg); if (istatus_rep == false) interpret_audioio_device(arg); if (istatus_rep == false) interpret_audioio_manager(arg); if (istatus_rep == false) interpret_midi_device(arg); if (istatus_rep == false) interpret_chain_operator(arg); if (istatus_rep == false) interpret_controller(arg); interpret_exit(arg); } /** * Interpret a vector of options. */ void ECA_CHAINSETUP_PARSER::interpret_options(const std::vector& opts) { int optcount = static_cast(opts.size()); int global_matches = 0; int other_matches = 0; interpret_set_result(true, ""); /* if opts.size() == 0 */ /* * phase1: parse global options only */ std::vector::const_iterator p = opts.begin(); while(p != opts.end()) { interpret_global_option(*p); /* note! below we make sure we don't calculate chain * definitions twice */ if (interpret_match_found() == true && !((*p)[0] == '-' && (*p)[1] == 'a')) global_matches++; ++p; } if (csetup_repp->chains.size() == 0) csetup_repp->add_default_chain(); /* * phase2: parse all options, including processing * the global options again */ p = opts.begin(); while(p != opts.end()) { interpret_object_option(*p); if (interpret_match_found() == true) { other_matches++; if (interpret_result() != true) { /* invalid option format */ break; } } else { /* hack to avoid printing the same info multiple times */ int dlevel = ECA_LOGGER::instance().get_log_level_bitmask(); ECA_LOGGER::instance().disable(); interpret_global_option(*p); ECA_LOGGER::instance().set_log_level_bitmask(dlevel); if (interpret_match_found() != true) { interpret_set_result(false, string("Invalid argument, unable to parse: \"") + *p + "\""); break; } else { if (interpret_result() != true) { /* invalid option format */ break; } } } ++p; } if (other_matches + global_matches != optcount) { ECA_LOG_MSG(ECA_LOGGER::info, string("WARNING: Only ") + kvu_numtostr(other_matches) + "+" + kvu_numtostr(global_matches) + " of the expected " + kvu_numtostr(optcount) + " parameters were recognized succesfully."); } } void ECA_CHAINSETUP_PARSER::reset_interpret_status(void) { istatus_rep = false; } /** * Preprocesses a set of options. * * Notes! See also ECA_SESSION::preprocess_options() * * @post all options valid for interpret_option() */ void ECA_CHAINSETUP_PARSER::preprocess_options(std::vector& opts) const { std::vector::iterator p = opts.begin(); while(p != opts.end()) { /* handle options not starting with an '-' sign */ if (p->size() > 0 && (*p)[0] != '-') { /* hack1: rest as "-i:file" */ ECA_LOG_MSG(ECA_LOGGER::info, "NOTE: Interpreting option " + *p + " as -i:" + *p + "."); *p = "-i:" + *p; } ++p; } } /** * Resets the interpretation logic. * * @post interpret_status() != true */ void ECA_CHAINSETUP_PARSER::interpret_entry(void) { istatus_rep = false; interpret_set_result(true, ""); DBC_ENSURE(interpret_match_found() != true); } /** * Exits the interpretation logic. * * @post interpret_result() == true && interpret_result_verbose() == "" || * interpret_result() == false && interpret_result_verbose() != "" */ void ECA_CHAINSETUP_PARSER::interpret_exit(const string& arg) { if (istatus_rep != true) { /* option 'arg' was not found */ interpret_set_result(false, string("Interpreting option \"") + arg + "\" failed."); } else { /* option 'arg' was found, but incorrect */ if (interpret_result() != true) { if (interpret_result_verbose() == "") { interpret_set_result(false, string("Interpreting option \"") + arg + "\" failed."); } /* else -> otherwise error code is already set */ } } DBC_ENSURE((interpret_result() == true && interpret_result_verbose() == "") || (interpret_result() == false && interpret_result_verbose() != "")); } /** * Handle general options. * * @pre argu.size() > 0 * @pre argu[0] == '-' * @pre istatus_rep == false */ void ECA_CHAINSETUP_PARSER::interpret_general_option (const string& argu) { // -------- DBC_REQUIRE(argu.size() > 0); DBC_REQUIRE(argu[0] == '-'); DBC_REQUIRE(istatus_rep == false); DBC_REQUIRE(csetup_repp->is_enabled() != true); // -------- bool match = true; if (argu.size() < 2) return; switch(argu[1]) { case 'b': { int bsize = atoi(kvu_get_argument_number(1, argu).c_str()); if (bsize > 0) { csetup_repp->set_buffersize(bsize); MESSAGE_ITEM mitemb; mitemb << "Setting buffersize to (samples) " << bsize << "."; ECA_LOG_MSG(ECA_LOGGER::info, mitemb.to_string()); } else { ECA_LOG_MSG(ECA_LOGGER::info, "Invalid buffersize given; using default value."); } break; } case 'B': { string temp = kvu_get_argument_number(1, argu); if (temp == "auto") { csetup_repp->set_buffering_mode(ECA_CHAINSETUP::cs_bmode_auto); ECA_LOG_MSG(ECA_LOGGER::info, "Buffering mode is selected automatically."); } else if (temp == "nonrt") { csetup_repp->set_buffering_mode(ECA_CHAINSETUP::cs_bmode_nonrt); ECA_LOG_MSG(ECA_LOGGER::info, "Buffering mode 'nonrt' selected."); } else if (temp == "rt") { csetup_repp->set_buffering_mode(ECA_CHAINSETUP::cs_bmode_rt); ECA_LOG_MSG(ECA_LOGGER::info, "Buffering mode 'rt' selected."); } else if (temp == "rtlowlatency") { csetup_repp->set_buffering_mode(ECA_CHAINSETUP::cs_bmode_rtlowlatency); ECA_LOG_MSG(ECA_LOGGER::info, "Buffering mode 'rtlowlatency' selected."); } else { csetup_repp->set_buffering_mode(ECA_CHAINSETUP::cs_bmode_auto); ECA_LOG_MSG(ECA_LOGGER::info, "Unknown buffering mode; 'auto' mode is used instead."); } break; } case 'n': { csetup_repp->set_name(kvu_get_argument_number(1, argu)); ECA_LOG_MSG(ECA_LOGGER::info, "Setting chainsetup name to \"" + csetup_repp->name() + "\"."); break; } case 'r': { int prio = ::atoi(kvu_get_argument_number(1, argu).c_str()); if (prio < 0) { ECA_LOG_MSG(ECA_LOGGER::info, "Raised-priority mode disabled."); csetup_repp->toggle_raised_priority(false); } else { if (prio == 0) prio = 50; csetup_repp->set_sched_priority(prio); ECA_LOG_MSG(ECA_LOGGER::info, "Raised-priority mode enabled. (prio:" + kvu_numtostr(prio) + ")"); csetup_repp->toggle_raised_priority(true); } break; } case 's': { if (argu.size() > 2 && argu[2] == 'r') { ECA_LOG_MSG(ECA_LOGGER::info, "Option '-sr' is obsolete. Use syntax '-f:sfmt,channels,srate,ileaving' instead."); } break; } case 'x': { ECA_LOG_MSG(ECA_LOGGER::info, "Truncating outputs (overwrite-mode)."); csetup_repp->set_output_openmode(AUDIO_IO::io_write); break; } case 'X': { ECA_LOG_MSG(ECA_LOGGER::info, "Updating outputs (rw-mode)."); csetup_repp->set_output_openmode(AUDIO_IO::io_readwrite); break; } case 'z': { string first_arg (kvu_get_argument_number(1, argu)); if (first_arg == "db") { long int bufs = atol(kvu_get_argument_number(2, argu).c_str()); if (bufs == 0) bufs = 100000; csetup_repp->set_double_buffer_size(bufs); ECA_LOG_MSG(ECA_LOGGER::info, "Using double-buffer of " + kvu_numtostr(bufs) + " sample frames."); csetup_repp->toggle_double_buffering(true); } else if (first_arg == "nodb") { ECA_LOG_MSG(ECA_LOGGER::info, "Double-buffering disabled."); csetup_repp->toggle_double_buffering(false); } else if (first_arg == "intbuf") { ECA_LOG_MSG(ECA_LOGGER::info, "Enabling extra buffering on realtime devices."); csetup_repp->toggle_max_buffers(true); } else if (first_arg == "nointbuf") { ECA_LOG_MSG(ECA_LOGGER::info, "Disabling extra buffering on realtime devices."); csetup_repp->toggle_max_buffers(false); } else if (first_arg == "multitrack") { ECA_LOG_MSG(ECA_LOGGER::info, "Enabling multitrack-mode (override)."); long int samples = -1; if (kvu_get_number_of_arguments(argu) > 1) { /* -z:multitrack,XXX */ samples = atol(kvu_get_argument_number(2, argu).c_str()); } csetup_repp->multitrack_mode_offset_rep = samples; csetup_repp->multitrack_mode_override_rep = true; csetup_repp->multitrack_mode_rep = true; } else if (first_arg == "nomultitrack") { ECA_LOG_MSG(ECA_LOGGER::info, "Disabling multitrack-mode (override)."); csetup_repp->multitrack_mode_override_rep = true; csetup_repp->multitrack_mode_offset_rep = 0; csetup_repp->multitrack_mode_rep = false; } else if (first_arg == "psr") { ECA_LOG_MSG(ECA_LOGGER::info, "Enabling precise-sample-rates with OSS audio devices."); csetup_repp->toggle_precise_sample_rates(true); } else if (first_arg == "nopsr") { ECA_LOG_MSG(ECA_LOGGER::info, "Disabling precise-sample-rates with OSS audio devices."); csetup_repp->toggle_precise_sample_rates(false); } else if (first_arg == "xruns") { ECA_LOG_MSG(ECA_LOGGER::info, "Processing is stopped if an xrun occurs."); csetup_repp->toggle_ignore_xruns(false); } else if (first_arg == "noxruns") { ECA_LOG_MSG(ECA_LOGGER::info, "Ignoring xruns during processing."); csetup_repp->toggle_ignore_xruns(true); } else if (first_arg == "mixmode") { if (kvu_get_argument_number(2, argu) == "sum") { ECA_LOG_MSG(ECA_LOGGER::info, "Enabling 'sum' mixmode."); csetup_repp->set_mix_mode(ECA_CHAINSETUP::cs_mmode_sum); } else { ECA_LOG_MSG(ECA_LOGGER::info, "Enabling 'avg' mixmode."); csetup_repp->set_mix_mode(ECA_CHAINSETUP::cs_mmode_avg); } } break; } default: { match = false; } } if (match == true) istatus_rep = true; } /** * Handle processing control * * @pre argu.size() > 0 * @pre argu[0] == '-' * @pre istatus_rep == false */ void ECA_CHAINSETUP_PARSER::interpret_processing_control (const string& argu) { // -------- DBC_REQUIRE(argu.size() > 0); DBC_REQUIRE(argu[0] == '-'); DBC_REQUIRE(istatus_rep == false); // -------- bool match = true; if (argu.size() < 2) return; switch(argu[1]) { case 't': { if (argu.size() < 3) return; switch(argu[2]) { case ':': { /* note! here we set the _maximum_ length of the chainsetup */ csetup_repp->set_max_length_in_seconds(atof(kvu_get_argument_number(1, argu).c_str())); ECA_LOG_MSG(ECA_LOGGER::info, "Set processing time to " + kvu_numtostr(csetup_repp->max_length_in_seconds_exact()) + "."); break; } case 'l': { csetup_repp->toggle_looping(true); if (csetup_repp->max_length_set() != true) ECA_LOG_MSG(ECA_LOGGER::info, "Looping enabled. Length of input objects will be used to set the loop point."); else ECA_LOG_MSG(ECA_LOGGER::info, "Looping enabled."); break; } } break; } default: { match = false; } } if (match == true) istatus_rep = true; } /** * Handle chain options. * * @pre argu.size() > 0 * @pre argu[0] == '-' * @pre istatus_rep == false */ void ECA_CHAINSETUP_PARSER::interpret_chains (const string& argu) { // -------- DBC_REQUIRE(argu.size() > 0); DBC_REQUIRE(argu[0] == '-'); DBC_REQUIRE(istatus_rep == false); // -------- bool match = true; if (argu.size() < 2) return; switch(argu[1]) { case 'a': { DBC_CHECK(csetup_repp->is_enabled() != true); std::vector schains = kvu_get_arguments(argu); if (std::find(schains.begin(), schains.end(), "all") != schains.end()) { csetup_repp->select_all_chains(); ECA_LOG_MSG(ECA_LOGGER::system_objects, "Selected all chains."); } else { csetup_repp->select_chains(schains); csetup_repp->add_new_chains(schains); MESSAGE_ITEM mtempa; mtempa << "Selected chain ids: "; for (std::vector::const_iterator p = schains.begin(); p != schains.end(); p++) { mtempa << *p << " "; } ECA_LOG_MSG(ECA_LOGGER::system_objects, mtempa.to_string()); } break; } default: { match = false; } } if (match == true) istatus_rep = true; } /** * Handle chainsetup options. * * @pre argu.size() > 0 * @pre argu[0] == '-' * @pre istatus_rep == false */ void ECA_CHAINSETUP_PARSER::interpret_audio_format (const string& argu) { // -------- DBC_REQUIRE(argu.size() > 0); DBC_REQUIRE(argu[0] == '-'); DBC_REQUIRE(istatus_rep == false); // -------- bool match = true; if (argu.size() < 2) return; switch(argu[1]) { case 'f': { ECA_AUDIO_FORMAT active_sinfo; int channels = atoi(kvu_get_argument_number(2, argu).c_str()); long int srate = atol(kvu_get_argument_number(3, argu).c_str()); string sample_fmt = kvu_get_argument_number(1, argu); /* initialize to current defaults */ active_sinfo.set_audio_format(csetup_repp->default_audio_format()); try { if (sample_fmt.size() > 0) active_sinfo.set_sample_format_string(sample_fmt); } catch(ECA_ERROR& e) { interpret_set_result(false, string("Unable to parse sample format \"") + sample_fmt + "\" passed to -f."); istatus_rep = true; return; } if (channels > 0) active_sinfo.set_channels(channels); if (srate > 0) active_sinfo.set_samples_per_second(srate); if (kvu_get_argument_number(4, argu) == "n") active_sinfo.toggle_interleaved_channels(false); else active_sinfo.toggle_interleaved_channels(true); /* modify the defaults */ csetup_repp->set_default_audio_format(active_sinfo); MESSAGE_ITEM ftemp; ftemp << "Changed active format to (bits/channels/srate/interleave): "; ftemp << csetup_repp->default_audio_format().format_string() << "/" << csetup_repp->default_audio_format().channels() << "/" << csetup_repp->default_audio_format().samples_per_second(); if (csetup_repp->default_audio_format().interleaved_channels() == true) { ftemp << "/i"; } else { ftemp << "/n"; } ECA_LOG_MSG(ECA_LOGGER::user_objects, ftemp.to_string()); break; } default: { match = false; } } if (match == true) istatus_rep = true; } /** * Handle effect preset options. * * @pre argu.size() > 0 * @pre argu[0] == '-' * @pre istatus_rep == false */ void ECA_CHAINSETUP_PARSER::interpret_effect_preset (const string& argu) { // -------- DBC_REQUIRE(argu.size() > 0); DBC_REQUIRE(argu[0] == '-'); DBC_REQUIRE(istatus_rep == false); // -------- bool match = true; if (argu.size() < 2) return; switch(argu[1]) { case 'p': { ECA_LOG_MSG(ECA_LOGGER::system_objects, "Interpreting preset \"" + argu + "\"."); CHAIN_OPERATOR* cop = 0; if (csetup_repp->selected_chainids.size() != 1) { ECA_LOG_MSG(ECA_LOGGER::info, "ERROR: Exactly one chain should be selected when adding chain operators."); match = false; } if (argu.size() < 3) return; switch(argu[2]) { case 'f': { #ifndef ECA_DISABLE_EFFECTS cop = dynamic_cast(new FILE_PRESET(kvu_get_argument_number(1,argu))); #endif break; } case 'n': { #ifndef ECA_DISABLE_EFFECTS string name = kvu_get_argument_number(1,argu); const PRESET* preset = dynamic_cast(ECA_OBJECT_FACTORY::preset_map().object(name)); if (preset != 0) cop = dynamic_cast(preset->new_expr()); else cop = 0; #endif break; } default: { } } if (cop != 0) { for(int n = 0; n < cop->number_of_params(); n++) { cop->set_parameter(n + 1, atof(kvu_get_argument_number(n + 2, argu).c_str())); } csetup_repp->add_chain_operator(cop); } break; } default: { match = false; } } if (match == true) istatus_rep = true; } /** * Handle audio-IO-devices and files. * * @pre argu.size() > 0 * @pre argu[0] == '-' */ void ECA_CHAINSETUP_PARSER::interpret_audioio_device (const string& argu) { // -------- DBC_REQUIRE(argu.size() > 0); DBC_REQUIRE(argu[0] == '-'); DBC_REQUIRE(istatus_rep == false); // -------- string tname = kvu_get_argument_number(1, argu); bool match = true; bool print_error = false; if (argu.size() < 2) return; switch(argu[1]) { case 'i': { DBC_CHECK(csetup_repp->is_enabled() != true); AUDIO_IO* audio_input = ECA_OBJECT_FACTORY::create_audio_object(argu); if (audio_input == 0) audio_input = ECA_OBJECT_FACTORY::create_loop_input(argu, &csetup_repp->loop_map); if (audio_input != 0) { if ((audio_input->supported_io_modes() & AUDIO_IO::io_read) != AUDIO_IO::io_read) { interpret_set_result(false, string("Audio object \"") + tname + "\" cannot be opened for input."); } else { ECA_LOG_MSG(ECA_LOGGER::system_objects,"adding file \"" + tname + "\"."); csetup_repp->add_input(audio_input); last_audio_add_vector_repp = &csetup_repp->inputs; /* for -y parsing */ } } else { print_error = true; } break; } case 'o': { DBC_CHECK(csetup_repp->is_enabled() != true); AUDIO_IO* audio_output = ECA_OBJECT_FACTORY::create_audio_object(argu); if (audio_output == 0) audio_output = ECA_OBJECT_FACTORY::create_loop_output(argu, &csetup_repp->loop_map); if (audio_output != 0) { bool truncate = false; int mode_tmp = csetup_repp->output_openmode(); if (mode_tmp == AUDIO_IO::io_readwrite) { if ((audio_output->supported_io_modes() & AUDIO_IO::io_readwrite) != AUDIO_IO::io_readwrite) { mode_tmp = AUDIO_IO::io_write; truncate = true; } } else { truncate = true; } if (((audio_output->supported_io_modes() & mode_tmp) != mode_tmp)) { interpret_set_result(false, string("io_write/io_readwrite access modes not supported by output \"") + audio_output->name() + "\"."); } else { ECA_LOG_MSG(ECA_LOGGER::system_objects,"adding file \"" + tname + "\"."); csetup_repp->add_output(audio_output, truncate); last_audio_add_vector_repp = &csetup_repp->outputs; /* for -y parsing */ } } else { print_error = true; } break; } case 'y': { DBC_CHECK(csetup_repp->is_enabled() != true); if (last_audio_add_vector_repp == 0) { ECA_LOG_MSG(ECA_LOGGER::info, "ERROR: Non-existant last audio object."); } else { AUDIO_IO* last_object = (*last_audio_add_vector_repp).back(); double newpos = atof(kvu_get_argument_number(1, argu).c_str()); if (newpos > 0.0f && last_object && last_object->supports_seeking() != true) { interpret_set_result(false, string("Audio object does not support seeking, unable to set a non-zero starting offset. Object generating the error is \"") + last_object->name() + "\"."); } else { last_object->seek_position_in_seconds(newpos); if (last_object->io_mode() == AUDIO_IO::io_read) { csetup_repp->input_start_pos[csetup_repp->input_start_pos.size() - 1] = last_object->position_in_seconds_exact(); } else { csetup_repp->output_start_pos[csetup_repp->output_start_pos.size() - 1] = last_object->position_in_seconds_exact(); } ECA_LOG_MSG(ECA_LOGGER::info, "Setting starting position for audio object \"" + last_object->label() + "\": " + kvu_numtostr(last_object->position_in_seconds_exact()) + " seconds."); } break; } } default: { match = false; } } if (match == true) istatus_rep = true; if (print_error == true) { interpret_set_result(false, string("Audio object \"") + tname + "\" does not match any of the known audio device types or " "file formats. You can check the list of supported " "audio object types by issuing the command 'aio-register' in " "ecasound's interactive mode."); } } /** * Handles audio-IO manager options. * * @pre argu.size() > 0 * @pre argu[0] == '-' */ void ECA_CHAINSETUP_PARSER::interpret_audioio_manager(const string& argu) { // -------- DBC_REQUIRE(argu.size() > 0); DBC_REQUIRE(argu[0] == '-'); DBC_REQUIRE(istatus_rep == false); // -------- string tname = kvu_get_argument_number(1, argu); bool match = true; if (argu.size() < 2) return; switch(argu[1]) { case 'G': { DBC_CHECK(csetup_repp->is_enabled() != true); std::vector args = kvu_get_arguments(argu); args.erase(args.begin()); DBC_CHECK(args.size() == kvu_get_arguments(argu).size() - 1); csetup_repp->set_audio_io_manager_option(tname, kvu_vector_to_string(args, ",")); break; } default: { match = false; } } if (match == true) istatus_rep = true; } /** * Handles MIDI-IO devices. * * @pre argu.size() > 0 * @pre argu[0] == '-' */ void ECA_CHAINSETUP_PARSER::interpret_midi_device (const string& argu) { // -------- DBC_REQUIRE(argu.size() > 0); DBC_REQUIRE(argu[0] == '-'); DBC_REQUIRE(istatus_rep == false); // -------- bool match = true; if (argu.size() < 2) return; switch(argu[1]) { case 'M': { if (argu.size() < 3) return; switch(argu[2]) { case 'd': { string tname = kvu_get_argument_number(1, argu); ECA_LOG_MSG(ECA_LOGGER::system_objects,"MIDI-config: Adding device \"" + tname + "\"."); MIDI_IO* mdev = 0; mdev = ECA_OBJECT_FACTORY::create_midi_device(argu); if (mdev != 0) { if ((mdev->supported_io_modes() & MIDI_IO::io_readwrite) == MIDI_IO::io_readwrite) { mdev->io_mode(MIDI_IO::io_readwrite); csetup_repp->add_midi_device(mdev); csetup_repp->midi_server_needed_rep = true; } else { ECA_LOG_MSG(ECA_LOGGER::info, "WARNING: I/O-mode 'io_readwrite' not supported by MIDI-device " + mdev->name()); } } break; } case 'm': { if (argu.size() < 4) return; switch(argu[3]) { case 'r': { // FIXME: not implemented! int id = atoi(kvu_get_argument_number(1, argu).c_str()); ECA_LOG_MSG(ECA_LOGGER::info, "MIDI-config: Receiving MMC messages with id \"" + kvu_numtostr(id) + "\"."); csetup_repp->midi_server_repp->set_mmc_receive_id(id); csetup_repp->midi_server_needed_rep = true; break; } case 's': { int id = atoi(kvu_get_argument_number(1, argu).c_str()); ECA_LOG_MSG(ECA_LOGGER::info, "MIDI-config: Adding MMC-send to device id \"" + kvu_numtostr(id) + "\"."); csetup_repp->midi_server_repp->add_mmc_send_id(id); csetup_repp->midi_server_needed_rep = true; break; } } break; } case 's': { if (argu.size() < 4) return; switch(argu[3]) { case 'r': { // FIXME: not implemented ECA_LOG_MSG(ECA_LOGGER::info, "MIDI-config: Receiving MIDI-sync."); csetup_repp->midi_server_needed_rep = true; csetup_repp->midi_server_repp->toggle_midi_sync_receive(true); break; } case 's': { // FIXME: not implemented ECA_LOG_MSG(ECA_LOGGER::info, "MIDI-config: Sending MIDI-sync."); csetup_repp->midi_server_repp->toggle_midi_sync_send(true); csetup_repp->midi_server_needed_rep = true; break; } } break; } } break; } default: { match = false; } } if (match == true) istatus_rep = true; return; } /** * Handle chain operator options (chain operators, presets * and plugins) * * @pre argu.size() > 0 * @pre argu[0] == '-' * @pre istatus_rep == false */ void ECA_CHAINSETUP_PARSER::interpret_chain_operator (const string& argu) { // -------- DBC_REQUIRE(argu.size() > 0); DBC_REQUIRE(argu[0] == '-'); DBC_REQUIRE(istatus_rep == false); // -------- CHAIN_OPERATOR* t = ECA_OBJECT_FACTORY::create_chain_operator(argu); if (t == 0) t = ECA_OBJECT_FACTORY::create_ladspa_plugin(argu); if (t != 0) { if (csetup_repp->selected_chainids.size() == 1) { csetup_repp->add_chain_operator(t); istatus_rep = true; } else { ECA_LOG_MSG(ECA_LOGGER::info, "ERROR: Exactly one chain should be selected when adding chain operators."); delete t; } } else interpret_effect_preset(argu); } /** * Handle controller sources and general controllers. * * @pre argu.size() > 0 * @pre argu[0] == '-' * @pre istatus_rep == false */ void ECA_CHAINSETUP_PARSER::interpret_controller (const string& argu) { // -------- DBC_REQUIRE(argu.size() > 0); DBC_REQUIRE(argu[0] == '-'); DBC_REQUIRE(istatus_rep == false); // -------- string prefix = kvu_get_argument_prefix(argu); if (prefix == "kx") { csetup_repp->set_target_to_controller(); ECA_LOG_MSG(ECA_LOGGER::system_objects, "Selected controllers as parameter control targets."); istatus_rep = true; } else { GENERIC_CONTROLLER* t = ECA_OBJECT_FACTORY::create_controller(argu); if (t != 0) { if (csetup_repp->selected_chainids.size() != 1) { ECA_LOG_MSG(ECA_LOGGER::info, "ERROR: Exactly one chain should be selected when adding controllers."); delete t; } else { MIDI_CLIENT* p = dynamic_cast(t->source_pointer()); if (p != 0) { csetup_repp->midi_server_needed_rep = true; p->register_server(csetup_repp->midi_server_repp); } csetup_repp->add_controller(t); istatus_rep = true; } } } } string ECA_CHAINSETUP_PARSER::general_options_to_string(void) const { MESSAGE_ITEM t; int setparams = csetup_repp->override_buffering_parameters().number_of_set(); ECA_LOG_MSG(ECA_LOGGER::system_objects, "genopts tostring - " + kvu_numtostr(setparams) + " overridden parameters."); if (setparams > 0) { t << "-b:" << csetup_repp->buffersize(); if (csetup_repp->raised_priority() == true) t << " -r:" << csetup_repp->get_sched_priority(); else t << " -r:-1"; if (csetup_repp->max_buffers() == true) t << " -z:intbuf"; else t << " -z:nointbuf"; if (csetup_repp->double_buffering() == true) t << " -z:db," << csetup_repp->double_buffer_size(); else t << " -z:nodb"; } else { ECA_CHAINSETUP::Buffering_mode_t bmode = csetup_repp->buffering_mode_rep; if (csetup_repp->active_buffering_mode_rep != ECA_CHAINSETUP::cs_bmode_none) bmode = csetup_repp->active_buffering_mode_rep; switch(bmode) { case ECA_CHAINSETUP::cs_bmode_nonrt: { t << "-B:nonrt"; break; } case ECA_CHAINSETUP::cs_bmode_rt: { t << "-B:rt"; break; } case ECA_CHAINSETUP::cs_bmode_rtlowlatency: { t << "-B:rtlowlatency"; break; } default: { t << " -B:auto"; } } } t << " -n:\"" << csetup_repp->name() << "\""; if (csetup_repp->output_openmode() == AUDIO_IO::io_write) t << " -x"; else t << " -X"; if (csetup_repp->multitrack_mode_override_rep == true) { if (csetup_repp->multitrack_mode() == true) { t << "-z:multitrack"; if (csetup_repp->multitrack_mode_offset_rep != -1) { t << "," << csetup_repp->multitrack_mode_offset_rep; } } else { t << "-z:nomultitrack"; } } if (csetup_repp->ignore_xruns() == true) t << " -z:noxruns"; else t << " -z:xruns"; if (csetup_repp->precise_sample_rates() == true) t << " -z:psr"; else t << " -z:nopsr"; if (csetup_repp->mix_mode() == ECA_CHAINSETUP::cs_mmode_avg) t << " -z:mixmode,avg"; else t << " -z:mixmode,sum"; t.setprecision(3); if (csetup_repp->max_length_set()) { t << " -t:" << csetup_repp->max_length_in_seconds_exact(); } if (csetup_repp->looping_enabled()) t << " -tl"; return t.to_string(); } string ECA_CHAINSETUP_PARSER::midi_to_string(void) const { MESSAGE_ITEM t; t.setprecision(3); std::vector::size_type p = 0; while (p < csetup_repp->midi_devices.size()) { t << "-Md:"; for(int n = 0; n < csetup_repp->midi_devices[p]->number_of_params(); n++) { // FIXME: should quote/escape possible commas and whitespace t << csetup_repp->midi_devices[p]->get_parameter(n + 1); if (n + 1 < csetup_repp->midi_devices[p]->number_of_params()) t << ","; } ++p; if (p < csetup_repp->midi_devices.size()) t << " "; } return t.to_string(); } string ECA_CHAINSETUP_PARSER::inputs_to_string(void) const { MESSAGE_ITEM t; t.setprecision(3); size_t p = 0; while (p < csetup_repp->inputs.size()) { t << "-a:"; std::vector c = csetup_repp->get_attached_chains_to_input(csetup_repp->inputs[p]); std::vector::const_iterator cp = c.begin(); while (cp != c.end()) { t << *cp; ++cp; if (cp != c.end()) t << ","; } t << " " << ECA_OBJECT_FACTORY::audio_object_format_to_eos(csetup_repp->inputs[p]) << " " << ECA_OBJECT_FACTORY::audio_object_to_eos(csetup_repp->inputs[p], "i"); if (csetup_repp->input_start_pos[p] != 0) { t << " -y:" << csetup_repp->input_start_pos[p]; } ++p; if (p < csetup_repp->inputs.size()) t << "\n"; } return t.to_string(); } string ECA_CHAINSETUP_PARSER::outputs_to_string(void) const { MESSAGE_ITEM t; t.setprecision(3); std::vector::size_type p = 0; while (p < csetup_repp->outputs.size()) { t << "-a:"; std::vector c = csetup_repp->get_attached_chains_to_output(csetup_repp->outputs[p]); std::vector::const_iterator cp = c.begin(); while (cp != c.end()) { t << *cp; ++cp; if (cp != c.end()) t << ","; } t << " " << ECA_OBJECT_FACTORY::audio_object_format_to_eos(csetup_repp->outputs[p]) << " " << ECA_OBJECT_FACTORY::audio_object_to_eos(csetup_repp->outputs[p], "o"); if (csetup_repp->output_start_pos[p] != 0) { t << " -y:" << csetup_repp->output_start_pos[p]; } ++p; if (p < csetup_repp->outputs.size()) t << "\n"; } return t.to_string(); } string ECA_CHAINSETUP_PARSER::chains_to_string(void) const { MESSAGE_ITEM t; std::vector::size_type p = 0; while (p < csetup_repp->chains.size()) { string tmpstr = csetup_repp->chains[p]->to_string(); if (tmpstr.size() > 0) { t << "-a:" << csetup_repp->chains[p]->name() << " "; t << tmpstr; if (p + 1 < csetup_repp->chains.size()) t << "\n"; } ++p; } return t.to_string(); }