X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=be7dfb846973aa67c1c49016d84e8d4f3e420d0d;hb=1059b3f48e568b60d5e248d94fbb9110a2dd1a16;hp=9359c8f76705ea5f25f114c4d8a4b1d570bf7824;hpb=70fd14afe809c7ac7d3b5b382c77580d7b8f6085;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 9359c8f767..be7dfb8469 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -29,7 +29,9 @@ #include #include #include -#include +#include +#include +#include #include #include #include @@ -51,21 +53,21 @@ using namespace ARDOUR; using namespace PBD; uint32_t Route::order_key_cnt = 0; - +sigc::signal Route::SyncOrderKeys; Route::Route (Session& sess, string name, int input_min, int input_max, int output_min, int output_max, Flag flg, DataType default_type) : IO (sess, name, input_min, input_max, output_min, output_max, default_type), _flags (flg), - _solo_control (X_("solo"), *this, ToggleControllable::SoloControl), - _mute_control (X_("mute"), *this, ToggleControllable::MuteControl) + _solo_control (new ToggleControllable (X_("solo"), *this, ToggleControllable::SoloControl)), + _mute_control (new ToggleControllable (X_("mute"), *this, ToggleControllable::MuteControl)) { init (); } Route::Route (Session& sess, const XMLNode& node, DataType default_type) : IO (sess, *node.child ("IO"), default_type), - _solo_control (X_("solo"), *this, ToggleControllable::SoloControl), - _mute_control (X_("mute"), *this, ToggleControllable::MuteControl) + _solo_control (new ToggleControllable (X_("solo"), *this, ToggleControllable::SoloControl)), + _mute_control (new ToggleControllable (X_("mute"), *this, ToggleControllable::MuteControl)) { init (); _set_state (node, false); @@ -74,7 +76,7 @@ Route::Route (Session& sess, const XMLNode& node, DataType default_type) void Route::init () { - redirect_max_outs.reset(); + processor_max_outs.reset(); _muted = false; _soloed = false; _solo_safe = false; @@ -87,6 +89,7 @@ Route::init () _initial_delay = 0; _roll_delay = 0; _own_latency = 0; + _user_latency = 0; _have_internal_generator = false; _declickable = false; _pending_declick = true; @@ -113,8 +116,8 @@ Route::init () Route::~Route () { - clear_redirects (PreFader, this); - clear_redirects (PostFader, this); + clear_processors (PreFader); + clear_processors (PostFader); for (OrderKeys::iterator i = order_keys.begin(); i != order_keys.end(); ++i) { free ((void*)(i->first)); @@ -158,9 +161,34 @@ void Route::set_order_key (const char* name, long n) { order_keys[strdup(name)] = n; + + if (Config->get_sync_all_route_ordering()) { + for (OrderKeys::iterator x = order_keys.begin(); x != order_keys.end(); ++x) { + x->second = n; + } + } + _session.set_dirty (); } +void +Route::sync_order_keys () +{ + uint32_t key; + + if (order_keys.empty()) { + return; + } + + OrderKeys::iterator x = order_keys.begin(); + key = x->second; + ++x; + + for (; x != order_keys.end(); ++x) { + x->second = key; + } +} + void Route::inc_gain (gain_t fraction, void *src) { @@ -174,33 +202,33 @@ Route::set_gain (gain_t val, void *src) if (_mix_group->is_relative()) { - - gain_t usable_gain = gain(); + gain_t usable_gain = gain(); if (usable_gain < 0.000001f) { - usable_gain=0.000001f; + usable_gain = 0.000001f; } gain_t delta = val; if (delta < 0.000001f) { - delta=0.000001f; + delta = 0.000001f; } delta -= usable_gain; - if (delta == 0.0f) return; + if (delta == 0.0f) + return; gain_t factor = delta / usable_gain; if (factor > 0.0f) { factor = _mix_group->get_max_factor(factor); if (factor == 0.0f) { - gain_changed (src); + _gain_control->Changed(); /* EMIT SIGNAL */ return; } } else { factor = _mix_group->get_min_factor(factor); if (factor == 0.0f) { - gain_changed (src); + _gain_control->Changed(); /* EMIT SIGNAL */ return; } } @@ -236,13 +264,13 @@ Route::set_gain (gain_t val, void *src) void Route::process_output_buffers (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, - nframes_t nframes, nframes_t offset, bool with_redirects, int declick, + nframes_t nframes, nframes_t offset, bool with_processors, int declick, bool meter) { // This is definitely very audio-only for now assert(_default_type == DataType::AUDIO); - RedirectList::iterator i; + ProcessorList::iterator i; bool post_fader_work = false; bool mute_declick_applied = false; gain_t dmg, dsg, dg; @@ -264,7 +292,7 @@ Route::process_output_buffers (BufferSet& bufs, declick = _pending_declick; { - Glib::Mutex::Lock cm (control_outs_lock, Glib::TRY_LOCK); + Glib::Mutex::Lock cm (_control_outs_lock, Glib::TRY_LOCK); if (cm.locked()) { co = _control_outs; @@ -292,17 +320,17 @@ Route::process_output_buffers (BufferSet& bufs, -------------------------------------------------------------------------------------------------- */ if (declick > 0) { - Amp::run (bufs, nframes, 0.0, 1.0, false); + Amp::run_in_place (bufs, nframes, 0.0, 1.0, false); _pending_declick = 0; } else if (declick < 0) { - Amp::run (bufs, nframes, 1.0, 0.0, false); + Amp::run_in_place (bufs, nframes, 1.0, 0.0, false); _pending_declick = 0; } else { /* no global declick */ if (solo_gain != dsg) { - Amp::run (bufs, nframes, solo_gain, dsg, false); + Amp::run_in_place (bufs, nframes, solo_gain, dsg, false); solo_gain = dsg; } } @@ -313,11 +341,11 @@ Route::process_output_buffers (BufferSet& bufs, -------------------------------------------------------------------------------------------------- */ if (meter && (_meter_point == MeterInput)) { - _meter->run(bufs, nframes); + _meter->run(bufs, start_frame, end_frame, nframes, offset); } if (!_soloed && _mute_affects_pre_fader && (mute_gain != dmg)) { - Amp::run (bufs, nframes, mute_gain, dmg, false); + Amp::run_in_place (bufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } @@ -371,18 +399,14 @@ Route::process_output_buffers (BufferSet& bufs, PRE-FADER REDIRECTS -------------------------------------------------------------------------------------------------- */ - /* FIXME: Somewhere in these loops is where bufs.count() should go from n_inputs() to redirect_max_outs() - * (if they differ). Something explicit needs to be done here to make sure the list of redirects will - * give us what we need (possibly by inserting transparent 'translators' into the list to make it work) */ - - if (with_redirects) { - Glib::RWLock::ReaderLock rm (redirect_lock, Glib::TRY_LOCK); + if (with_processors) { + Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK); if (rm.locked()) { if (mute_gain > 0 || !_mute_affects_pre_fader) { - for (i = _redirects.begin(); i != _redirects.end(); ++i) { + for (i = _processors.begin(); i != _processors.end(); ++i) { switch ((*i)->placement()) { case PreFader: - (*i)->run (bufs, start_frame, end_frame, nframes, offset); + (*i)->run_in_place (bufs, start_frame, end_frame, nframes, offset); break; case PostFader: post_fader_work = true; @@ -390,7 +414,7 @@ Route::process_output_buffers (BufferSet& bufs, } } } else { - for (i = _redirects.begin(); i != _redirects.end(); ++i) { + for (i = _processors.begin(); i != _processors.end(); ++i) { switch ((*i)->placement()) { case PreFader: (*i)->silence (nframes, offset); @@ -404,12 +428,11 @@ Route::process_output_buffers (BufferSet& bufs, } } - // FIXME: for now, just hope the redirects list did what it was supposed to - bufs.set_count(n_process_buffers()); - + // This really should already be true... + bufs.set_count(pre_fader_streams()); if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_post_fader) { - Amp::run (bufs, nframes, mute_gain, dmg, false); + Amp::run_in_place (bufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } @@ -419,7 +442,7 @@ Route::process_output_buffers (BufferSet& bufs, -------------------------------------------------------------------------------------------------- */ if (meter && (_meter_point == MeterPreFader)) { - _meter->run(bufs, nframes); + _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset); } @@ -462,13 +485,9 @@ Route::process_output_buffers (BufferSet& bufs, // OR recording - // h/w monitoring not in use - - (!Config->get_monitoring_model() == HardwareMonitoring && - // AND software monitoring required - Config->get_monitoring_model() == SoftwareMonitoring)) { + Config->get_monitoring_model() == SoftwareMonitoring) { if (apply_gain_automation) { @@ -500,7 +519,7 @@ Route::process_output_buffers (BufferSet& bufs, if (_gain != dg) { - Amp::run (bufs, nframes, _gain, dg, _phase_invert); + Amp::run_in_place (bufs, nframes, _gain, dg, _phase_invert); _gain = dg; } else if (_gain != 0 && (_phase_invert || _gain != 1.0)) { @@ -541,24 +560,24 @@ Route::process_output_buffers (BufferSet& bufs, POST-FADER REDIRECTS -------------------------------------------------------------------------------------------------- */ - /* note that post_fader_work cannot be true unless with_redirects was also true, so don't test both */ + /* note that post_fader_work cannot be true unless with_processors was also true, so don't test both */ if (post_fader_work) { - Glib::RWLock::ReaderLock rm (redirect_lock, Glib::TRY_LOCK); + Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK); if (rm.locked()) { if (mute_gain > 0 || !_mute_affects_post_fader) { - for (i = _redirects.begin(); i != _redirects.end(); ++i) { + for (i = _processors.begin(); i != _processors.end(); ++i) { switch ((*i)->placement()) { case PreFader: break; case PostFader: - (*i)->run (bufs, start_frame, end_frame, nframes, offset); + (*i)->run_in_place (bufs, start_frame, end_frame, nframes, offset); break; } } } else { - for (i = _redirects.begin(); i != _redirects.end(); ++i) { + for (i = _processors.begin(); i != _processors.end(); ++i) { switch ((*i)->placement()) { case PreFader: break; @@ -572,7 +591,7 @@ Route::process_output_buffers (BufferSet& bufs, } if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_control_outs) { - Amp::run (bufs, nframes, mute_gain, dmg, false); + Amp::run_in_place (bufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } @@ -617,7 +636,7 @@ Route::process_output_buffers (BufferSet& bufs, ----------------------------------------------------------------------*/ if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_main_outs) { - Amp::run (bufs, nframes, mute_gain, dmg, false); + Amp::run_in_place (bufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } @@ -654,7 +673,7 @@ Route::process_output_buffers (BufferSet& bufs, ) { /* don't use Route::silence() here, because that causes - all outputs (sends, port inserts, etc. to be silent). + all outputs (sends, port processors, etc. to be silent). */ if (_meter_point == MeterPostFader) { @@ -679,7 +698,7 @@ Route::process_output_buffers (BufferSet& bufs, if ((_gain == 0 && !apply_gain_automation) || dmg == 0) { _meter->reset(); } else { - _meter->run(output_buffers(), nframes, offset); + _meter->run_in_place(output_buffers(), start_frame, end_frame, nframes, offset); } } } @@ -687,7 +706,7 @@ Route::process_output_buffers (BufferSet& bufs, ChanCount Route::n_process_buffers () { - return max (n_inputs(), redirect_max_outs); + return max (n_inputs(), processor_max_outs); } void @@ -699,18 +718,12 @@ Route::passthru (nframes_t start_frame, nframes_t end_frame, nframes_t nframes, collect_input (bufs, nframes, offset); -#define meter_stream meter_first - if (meter_first) { - _meter->run(bufs, nframes); - meter_stream = false; - } else { - meter_stream = true; + _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset); + meter_first = false; } - process_output_buffers (bufs, start_frame, end_frame, nframes, offset, true, declick, meter_stream); - -#undef meter_stream + process_output_buffers (bufs, start_frame, end_frame, nframes, offset, true, declick, meter_first); } void @@ -734,7 +747,7 @@ Route::set_solo (bool yn, void *src) if (_soloed != yn) { _soloed = yn; solo_changed (src); /* EMIT SIGNAL */ - _solo_control.Changed (); /* EMIT SIGNAL */ + _solo_control->Changed (); /* EMIT SIGNAL */ } } @@ -771,7 +784,7 @@ Route::set_mute (bool yn, void *src) _muted = yn; mute_changed (src); /* EMIT SIGNAL */ - _mute_control.Changed (); /* EMIT SIGNAL */ + _mute_control->Changed (); /* EMIT SIGNAL */ Glib::Mutex::Lock lm (declick_lock); desired_mute_gain = (yn?0.0f:1.0f); @@ -779,90 +792,79 @@ Route::set_mute (bool yn, void *src) } int -Route::add_redirect (boost::shared_ptr redirect, void *src, uint32_t* err_streams) +Route::add_processor (boost::shared_ptr processor, ProcessorStreams* err) { - ChanCount old_rmo = redirect_max_outs; + ChanCount old_rmo = processor_max_outs; if (!_session.engine().connected()) { return 1; } { - Glib::RWLock::WriterLock lm (redirect_lock); + Glib::RWLock::WriterLock lm (_processor_lock); boost::shared_ptr pi; boost::shared_ptr porti; - redirect->set_default_type(_default_type); + //processor->set_default_type(_default_type); - if ((pi = boost::dynamic_pointer_cast(redirect)) != 0) { + if ((pi = boost::dynamic_pointer_cast(processor)) != 0) { pi->set_count (1); - if (pi->input_streams() == ChanCount::ZERO) { + if (pi->natural_input_streams() == ChanCount::ZERO) { /* generator plugin */ _have_internal_generator = true; } - } else if ((porti = boost::dynamic_pointer_cast(redirect)) != 0) { - - /* force new port inserts to start out with an i/o configuration - that matches this route's i/o configuration. - - the "inputs" for the port are supposed to match the output - of this route. - - the "outputs" of the route should match the inputs of this - route. XXX shouldn't they match the number of active signal - streams at the point of insertion? - */ - // FIXME: (yes, they should) - - porti->ensure_io (n_outputs (), n_inputs(), false, this); } - // Ensure peak vector sizes before the plugin is activated - ChanCount potential_max_streams = max(redirect->input_streams(), redirect->output_streams()); - _meter->setup(potential_max_streams); - - _redirects.push_back (redirect); + _processors.push_back (processor); - if (_reset_plugin_counts (err_streams)) { - _redirects.pop_back (); + // Set up processor list channels. This will set processor->[input|output]_streams(), + // configure redirect ports properly, etc. + if (_reset_plugin_counts (err)) { + _processors.pop_back (); _reset_plugin_counts (0); // it worked before we tried to add it ... return -1; } - redirect->activate (); - redirect->active_changed.connect (mem_fun (*this, &Route::redirect_active_proxy)); + // Ensure peak vector sizes before the plugin is activated + ChanCount potential_max_streams = max(processor->input_streams(), processor->output_streams()); + _meter->configure_io(potential_max_streams, potential_max_streams); + + processor->activate (); + processor->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false)); + + _user_latency = 0; } - if (redirect_max_outs != old_rmo || old_rmo == ChanCount::ZERO) { + if (processor_max_outs != old_rmo || old_rmo == ChanCount::ZERO) { reset_panner (); } - redirects_changed (src); /* EMIT SIGNAL */ + processors_changed (); /* EMIT SIGNAL */ return 0; } int -Route::add_redirects (const RedirectList& others, void *src, uint32_t* err_streams) +Route::add_processors (const ProcessorList& others, ProcessorStreams* err) { - ChanCount old_rmo = redirect_max_outs; + ChanCount old_rmo = processor_max_outs; if (!_session.engine().connected()) { return 1; } { - Glib::RWLock::WriterLock lm (redirect_lock); + Glib::RWLock::WriterLock lm (_processor_lock); - RedirectList::iterator existing_end = _redirects.end(); + ProcessorList::iterator existing_end = _processors.end(); --existing_end; ChanCount potential_max_streams; - for (RedirectList::const_iterator i = others.begin(); i != others.end(); ++i) { + for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) { boost::shared_ptr pi; @@ -875,57 +877,63 @@ Route::add_redirects (const RedirectList& others, void *src, uint32_t* err_strea } // Ensure peak vector sizes before the plugin is activated - _meter->setup(potential_max_streams); + _meter->configure_io(potential_max_streams, potential_max_streams); - _redirects.push_back (*i); + _processors.push_back (*i); - if (_reset_plugin_counts (err_streams)) { + if (_reset_plugin_counts (err)) { ++existing_end; - _redirects.erase (existing_end, _redirects.end()); + _processors.erase (existing_end, _processors.end()); _reset_plugin_counts (0); // it worked before we tried to add it ... return -1; } (*i)->activate (); - (*i)->active_changed.connect (mem_fun (*this, &Route::redirect_active_proxy)); + (*i)->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false)); } + + _user_latency = 0; } - if (redirect_max_outs != old_rmo || old_rmo == ChanCount::ZERO) { + if (processor_max_outs != old_rmo || old_rmo == ChanCount::ZERO) { reset_panner (); } - redirects_changed (src); /* EMIT SIGNAL */ + processors_changed (); /* EMIT SIGNAL */ return 0; } -/** Turn off all redirects with a given placement - * @param p Placement of redirects to disable +/** Turn off all processors with a given placement + * @param p Placement of processors to disable */ void -Route::disable_redirects (Placement p) +Route::disable_processors (Placement p) { - Glib::RWLock::ReaderLock lm (redirect_lock); + Glib::RWLock::ReaderLock lm (_processor_lock); - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if ((*i)->placement() == p) { - (*i)->set_active (false, this); + (*i)->set_active (false); } } + + _session.set_dirty (); } /** Turn off all redirects */ void -Route::disable_redirects () +Route::disable_processors () { - Glib::RWLock::ReaderLock lm (redirect_lock); + Glib::RWLock::ReaderLock lm (_processor_lock); - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { - (*i)->set_active (false, this); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + (*i)->set_active (false); } + + _session.set_dirty (); } /** Turn off all redirects with a given placement @@ -935,13 +943,15 @@ Route::disable_redirects () void Route::disable_plugins (Placement p) { - Glib::RWLock::ReaderLock lm (redirect_lock); + Glib::RWLock::ReaderLock lm (_processor_lock); - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (boost::dynamic_pointer_cast (*i) && (*i)->placement() == p) { - (*i)->set_active (false, this); + (*i)->set_active (false); } } + + _session.set_dirty (); } /** Turn off all plugins @@ -950,20 +960,22 @@ Route::disable_plugins (Placement p) void Route::disable_plugins () { - Glib::RWLock::ReaderLock lm (redirect_lock); + Glib::RWLock::ReaderLock lm (_processor_lock); - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (boost::dynamic_pointer_cast (*i)) { - (*i)->set_active (false, this); + (*i)->set_active (false); } } + + _session.set_dirty (); } void Route::ab_plugins (bool forward) { - Glib::RWLock::ReaderLock lm (redirect_lock); + Glib::RWLock::ReaderLock lm (_processor_lock); if (forward) { @@ -971,13 +983,13 @@ Route::ab_plugins (bool forward) we go the other way, we will revert them */ - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (!boost::dynamic_pointer_cast (*i)) { continue; } if ((*i)->active()) { - (*i)->set_active (false, this); + (*i)->set_active (false); (*i)->set_next_ab_is_active (true); } else { (*i)->set_next_ab_is_active (false); @@ -988,38 +1000,62 @@ Route::ab_plugins (bool forward) /* backward = if the redirect was marked to go active on the next ab, do so */ - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (!boost::dynamic_pointer_cast (*i)) { continue; } if ((*i)->get_next_ab_is_active()) { - (*i)->set_active (true, this); + (*i)->set_active (true); } else { - (*i)->set_active (false, this); + (*i)->set_active (false); } } } + + _session.set_dirty (); } + + +/* Figure out the streams that will feed into PreFader */ +ChanCount +Route::pre_fader_streams() const +{ + boost::shared_ptr processor; -/** Remove redirects with a given placement. - * @param p Placement of redirects to remove. + // Find the last pre-fader redirect + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + if ((*i)->placement() == PreFader) { + processor = *i; + } + } + + if (processor) { + return processor->output_streams(); + } else { + return n_inputs (); + } +} + + +/** Remove processors with a given placement. + * @param p Placement of processors to remove. */ void -Route::clear_redirects (Placement p, void *src) +Route::clear_processors (Placement p) { - const ChanCount old_rmo = redirect_max_outs; + const ChanCount old_rmo = processor_max_outs; if (!_session.engine().connected()) { return; } { - Glib::RWLock::WriterLock lm (redirect_lock); - RedirectList new_list; + Glib::RWLock::WriterLock lm (_processor_lock); + ProcessorList new_list; - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if ((*i)->placement() == p) { /* it's the placement we want to get rid of */ (*i)->drop_references (); @@ -1029,42 +1065,42 @@ Route::clear_redirects (Placement p, void *src) } } - _redirects = new_list; + _processors = new_list; } /* FIXME: can't see how this test can ever fire */ - if (redirect_max_outs != old_rmo) { + if (processor_max_outs != old_rmo) { reset_panner (); } - redirect_max_outs.reset(); + processor_max_outs.reset(); _have_internal_generator = false; - redirects_changed (src); /* EMIT SIGNAL */ + processors_changed (); /* EMIT SIGNAL */ } int -Route::remove_redirect (boost::shared_ptr redirect, void *src, uint32_t* err_streams) +Route::remove_processor (boost::shared_ptr processor, ProcessorStreams* err) { - ChanCount old_rmo = redirect_max_outs; + ChanCount old_rmo = processor_max_outs; if (!_session.engine().connected()) { return 1; } - redirect_max_outs.reset(); + processor_max_outs.reset(); { - Glib::RWLock::WriterLock lm (redirect_lock); - RedirectList::iterator i; + Glib::RWLock::WriterLock lm (_processor_lock); + ProcessorList::iterator i; bool removed = false; - for (i = _redirects.begin(); i != _redirects.end(); ++i) { - if (*i == redirect) { + for (i = _processors.begin(); i != _processors.end(); ++i) { + if (*i == processor) { - RedirectList::iterator tmp; + ProcessorList::iterator tmp; /* move along, see failure case for reset_plugin_counts() - where we may need to reinsert the redirect. + where we may need to reprocessor the processor. */ tmp = i; @@ -1075,23 +1111,21 @@ Route::remove_redirect (boost::shared_ptr redirect, void *src, uint32_ run. */ - boost::shared_ptr send; - boost::shared_ptr port_insert; + boost::shared_ptr redirect; - if ((send = boost::dynamic_pointer_cast (*i)) != 0) { - send->disconnect_inputs (this); - send->disconnect_outputs (this); - } else if ((port_insert = boost::dynamic_pointer_cast (*i)) != 0) { - port_insert->disconnect_inputs (this); - port_insert->disconnect_outputs (this); + if ((redirect = boost::dynamic_pointer_cast (*i)) != 0) { + redirect->io()->disconnect_inputs (this); + redirect->io()->disconnect_outputs (this); } - _redirects.erase (i); + _processors.erase (i); i = tmp; removed = true; break; } + + _user_latency = 0; } if (!removed) { @@ -1099,9 +1133,9 @@ Route::remove_redirect (boost::shared_ptr redirect, void *src, uint32_ return 1; } - if (_reset_plugin_counts (err_streams)) { + if (_reset_plugin_counts (err)) { /* get back to where we where */ - _redirects.insert (i, redirect); + _processors.insert (i, processor); /* we know this will work, because it worked before :) */ _reset_plugin_counts (0); return -1; @@ -1109,7 +1143,7 @@ Route::remove_redirect (boost::shared_ptr redirect, void *src, uint32_ bool foo = false; - for (i = _redirects.begin(); i != _redirects.end(); ++i) { + for (i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr pi; if ((pi = boost::dynamic_pointer_cast(*i)) != 0) { @@ -1122,137 +1156,78 @@ Route::remove_redirect (boost::shared_ptr redirect, void *src, uint32_ _have_internal_generator = foo; } - if (old_rmo != redirect_max_outs) { + if (old_rmo != processor_max_outs) { reset_panner (); } - redirect->drop_references (); + processor->drop_references (); - redirects_changed (src); /* EMIT SIGNAL */ + processors_changed (); /* EMIT SIGNAL */ return 0; } int -Route::reset_plugin_counts (uint32_t* lpc) +Route::reset_plugin_counts (ProcessorStreams* err) { - Glib::RWLock::WriterLock lm (redirect_lock); - return _reset_plugin_counts (lpc); + Glib::RWLock::WriterLock lm (_processor_lock); + return _reset_plugin_counts (err); } int -Route::_reset_plugin_counts (uint32_t* err_streams) -{ - RedirectList::iterator r; - uint32_t i_cnt; - uint32_t s_cnt; - map > insert_map; - nframes_t initial_streams; - - redirect_max_outs.reset(); - i_cnt = 0; - s_cnt = 0; - - /* divide inserts up by placement so we get the signal flow - properly modelled. we need to do this because the _redirects - list is not sorted by placement, and because other reasons may - exist now or in the future for this separate treatment. +Route::_reset_plugin_counts (ProcessorStreams* err) +{ + ProcessorList::iterator r; + map > processor_map; + ChanCount initial_streams; + + /* Process each placement in order, checking to see if we + can really do what has been requested. */ - for (r = _redirects.begin(); r != _redirects.end(); ++r) { - - boost::shared_ptr insert; - - /* do this here in case we bomb out before we get to the end of - this function. - */ - - redirect_max_outs = max ((*r)->output_streams (), redirect_max_outs); - - if ((insert = boost::dynamic_pointer_cast(*r)) != 0) { - ++i_cnt; - insert_map[insert->placement()].push_back (InsertCount (insert)); + /* divide processors up by placement so we get the signal flow + properly modelled. we need to do this because the _processors + list is not sorted by placement + */ - /* reset plugin counts back to one for now so - that we have a predictable, controlled - state to try to configure. - */ + /* ... but it should/will be... */ + + for (r = _processors.begin(); r != _processors.end(); ++r) { - boost::shared_ptr pi; - - if ((pi = boost::dynamic_pointer_cast(insert)) != 0) { - pi->set_count (1); - } + boost::shared_ptr processor; - } else if (boost::dynamic_pointer_cast (*r) != 0) { - ++s_cnt; + if ((processor = boost::dynamic_pointer_cast(*r)) != 0) { + processor_map[processor->placement()].push_back (ProcessorCount (processor)); } } - if (i_cnt == 0) { - if (s_cnt) { - goto recompute; - } else { - return 0; - } - } - - /* Now process each placement in order, checking to see if we - can really do what has been requested. - */ /* A: PreFader */ - if (check_some_plugin_counts (insert_map[PreFader], n_inputs ().get(_default_type), err_streams)) { + if ( ! check_some_plugin_counts (processor_map[PreFader], n_inputs (), err)) { return -1; } - /* figure out the streams that will feed into PreFader */ - - if (!insert_map[PreFader].empty()) { - InsertCount& ic (insert_map[PreFader].back()); - initial_streams = ic.insert->compute_output_streams (ic.cnt); - } else { - initial_streams = n_inputs ().get(_default_type); - } + ChanCount post_fader_input = (err ? err->count : n_inputs()); /* B: PostFader */ - if (check_some_plugin_counts (insert_map[PostFader], initial_streams, err_streams)) { + if ( ! check_some_plugin_counts (processor_map[PostFader], post_fader_input, err)) { return -1; } /* OK, everything can be set up correctly, so lets do it */ - apply_some_plugin_counts (insert_map[PreFader]); - apply_some_plugin_counts (insert_map[PostFader]); + apply_some_plugin_counts (processor_map[PreFader]); + apply_some_plugin_counts (processor_map[PostFader]); - /* recompute max outs of any redirect */ + /* recompute max outs of any processor */ - recompute: + processor_max_outs.reset(); + ProcessorList::iterator prev = _processors.end(); - redirect_max_outs.reset(); - RedirectList::iterator prev = _redirects.end(); - - for (r = _redirects.begin(); r != _redirects.end(); prev = r, ++r) { - boost::shared_ptr s; - - if ((s = boost::dynamic_pointer_cast (*r)) != 0) { - if (r == _redirects.begin()) { - s->expect_inputs (n_inputs()); - } else { - s->expect_inputs ((*prev)->output_streams()); - } - - } else { - - /* don't pay any attention to send output configuration, since it doesn't - affect the route. - */ - - redirect_max_outs = max ((*r)->output_streams (), redirect_max_outs); - - } + for (r = _processors.begin(); r != _processors.end(); prev = r, ++r) { + processor_max_outs = max ((*r)->output_streams (), processor_max_outs); } /* we're done */ @@ -1261,72 +1236,96 @@ Route::_reset_plugin_counts (uint32_t* err_streams) } int32_t -Route::apply_some_plugin_counts (list& iclist) +Route::apply_some_plugin_counts (list& iclist) { - list::iterator i; + list::iterator i; for (i = iclist.begin(); i != iclist.end(); ++i) { - if ((*i).insert->configure_io ((*i).cnt, (*i).in, (*i).out)) { + cerr << "now applying for " << (*i).processor->name() << " in = " << (*i).in.n_audio() << " out = " << (*i).out.n_audio() << endl; + + if ((*i).processor->configure_io ((*i).in, (*i).out)) { return -1; } /* make sure that however many we have, they are all active */ - (*i).insert->activate (); + (*i).processor->activate (); } return 0; } -int32_t -Route::check_some_plugin_counts (list& iclist, int32_t required_inputs, uint32_t* err_streams) +/** Returns whether \a iclist can be configured and run starting with + * \a required_inputs at the first processor's inputs. + * If false is returned, \a iclist can not be run with \a required_inputs, and \a err is set. + * Otherwise, \a err is set to the output of the list. + */ +bool +Route::check_some_plugin_counts (list& iclist, ChanCount required_inputs, ProcessorStreams* err) { - list::iterator i; - + list::iterator i; + size_t index = 0; + + if (err) { + err->index = 0; + err->count = required_inputs; + } + for (i = iclist.begin(); i != iclist.end(); ++i) { - if (((*i).cnt = (*i).insert->can_support_input_configuration (required_inputs)) < 0) { - if (err_streams) { - *err_streams = required_inputs; + + cerr << "Checking whether " << (*i).processor->name() << " can support " << required_inputs.n_audio() << " inputs\n"; + + if ((*i).processor->can_support_input_configuration (required_inputs) < 0) { + if (err) { + err->index = index; + err->count = required_inputs; } - return -1; + return false; } (*i).in = required_inputs; - (*i).out = (*i).insert->compute_output_streams ((*i).cnt); + (*i).out = (*i).processor->output_for_input_configuration (required_inputs); + + cerr << "config looks like " << (*i).processor->name() << " in = " << (*i).in.n_audio() << " out = " << (*i).out.n_audio() << endl; required_inputs = (*i).out; + + ++index; + } + + if (err) { + if (!iclist.empty()) { + err->index = index; + err->count = iclist.back().processor->output_for_input_configuration(required_inputs); + } } - return 0; + return true; } int -Route::copy_redirects (const Route& other, Placement placement, uint32_t* err_streams) +Route::copy_processors (const Route& other, Placement placement, ProcessorStreams* err) { - ChanCount old_rmo = redirect_max_outs; + ChanCount old_rmo = processor_max_outs; - if (err_streams) { - *err_streams = 0; - } - - RedirectList to_be_deleted; + ProcessorList to_be_deleted; { - Glib::RWLock::WriterLock lm (redirect_lock); - RedirectList::iterator tmp; - RedirectList the_copy; + Glib::RWLock::WriterLock lm (_processor_lock); + ProcessorList::iterator tmp; + ProcessorList the_copy; - the_copy = _redirects; + the_copy = _processors; - /* remove all relevant redirects */ + /* remove all relevant processors */ - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ) { tmp = i; ++tmp; if ((*i)->placement() == placement) { to_be_deleted.push_back (*i); - _redirects.erase (i); + _processors.erase (i); } i = tmp; @@ -1334,27 +1333,27 @@ Route::copy_redirects (const Route& other, Placement placement, uint32_t* err_st /* now copy the relevant ones from "other" */ - for (RedirectList::const_iterator i = other._redirects.begin(); i != other._redirects.end(); ++i) { + for (ProcessorList::const_iterator i = other._processors.begin(); i != other._processors.end(); ++i) { if ((*i)->placement() == placement) { - _redirects.push_back (Redirect::clone (*i)); + _processors.push_back (IOProcessor::clone (*i)); } } /* reset plugin stream handling */ - if (_reset_plugin_counts (err_streams)) { + if (_reset_plugin_counts (err)) { /* FAILED COPY ATTEMPT: we have to restore order */ - /* delete all cloned redirects */ + /* delete all cloned processors */ - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ) { tmp = i; ++tmp; if ((*i)->placement() == placement) { - _redirects.erase (i); + _processors.erase (i); } i = tmp; @@ -1362,8 +1361,8 @@ Route::copy_redirects (const Route& other, Placement placement, uint32_t* err_st /* restore the natural order */ - _redirects = the_copy; - redirect_max_outs = old_rmo; + _processors = the_copy; + processor_max_outs = old_rmo; /* we failed, even though things are OK again */ @@ -1371,84 +1370,89 @@ Route::copy_redirects (const Route& other, Placement placement, uint32_t* err_st } else { - /* SUCCESSFUL COPY ATTEMPT: delete the redirects we removed pre-copy */ + /* SUCCESSFUL COPY ATTEMPT: delete the processors we removed pre-copy */ to_be_deleted.clear (); + _user_latency = 0; } } - if (redirect_max_outs != old_rmo || old_rmo == ChanCount::ZERO) { + if (processor_max_outs != old_rmo || old_rmo == ChanCount::ZERO) { reset_panner (); } - redirects_changed (this); /* EMIT SIGNAL */ + processors_changed (); /* EMIT SIGNAL */ return 0; } void -Route::all_redirects_flip () +Route::all_processors_flip () { - Glib::RWLock::ReaderLock lm (redirect_lock); + Glib::RWLock::ReaderLock lm (_processor_lock); - if (_redirects.empty()) { + if (_processors.empty()) { return; } - bool first_is_on = _redirects.front()->active(); + bool first_is_on = _processors.front()->active(); - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { - (*i)->set_active (!first_is_on, this); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + (*i)->set_active (!first_is_on); } + + _session.set_dirty (); } -/** Set all redirects with a given placement to a given active state. - * @param p Placement of redirects to change. - * @param state New active state for those redirects. +/** Set all processors with a given placement to a given active state. + * @param p Placement of processors to change. + * @param state New active state for those processors. */ void -Route::all_redirects_active (Placement p, bool state) +Route::all_processors_active (Placement p, bool state) { - Glib::RWLock::ReaderLock lm (redirect_lock); + Glib::RWLock::ReaderLock lm (_processor_lock); - if (_redirects.empty()) { + if (_processors.empty()) { return; } - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if ((*i)->placement() == p) { - (*i)->set_active (state, this); + (*i)->set_active (state); } } + + _session.set_dirty (); } -struct RedirectSorter { - bool operator() (boost::shared_ptr a, boost::shared_ptr b) { +struct ProcessorSorter { + bool operator() (boost::shared_ptr a, boost::shared_ptr b) { return a->sort_key() < b->sort_key(); } }; int -Route::sort_redirects (uint32_t* err_streams) +Route::sort_processors (ProcessorStreams* err) { { - RedirectSorter comparator; - Glib::RWLock::WriterLock lm (redirect_lock); - ChanCount old_rmo = redirect_max_outs; + ProcessorSorter comparator; + Glib::RWLock::WriterLock lm (_processor_lock); + ChanCount old_rmo = processor_max_outs; /* the sweet power of C++ ... */ - RedirectList as_it_was_before = _redirects; + ProcessorList as_it_was_before = _processors; - _redirects.sort (comparator); + _processors.sort (comparator); - if (_reset_plugin_counts (err_streams)) { - _redirects = as_it_was_before; - redirect_max_outs = old_rmo; + if (_reset_plugin_counts (err)) { + _processors = as_it_was_before; + processor_max_outs = old_rmo; return -1; } } reset_panner (); - redirects_changed (this); /* EMIT SIGNAL */ + processors_changed (); /* EMIT SIGNAL */ return 0; } @@ -1469,7 +1473,7 @@ XMLNode& Route::state(bool full_state) { XMLNode *node = new XMLNode("Route"); - RedirectList:: iterator i; + ProcessorList::iterator i; char buf[32]; if (_flags) { @@ -1515,8 +1519,8 @@ Route::state(bool full_state) node->add_property ("order-keys", order_string); node->add_child_nocopy (IO::state (full_state)); - node->add_child_nocopy (_solo_control.get_state ()); - node->add_child_nocopy (_mute_control.get_state ()); + node->add_child_nocopy (_solo_control->get_state ()); + node->add_child_nocopy (_mute_control->get_state ()); XMLNode* remote_control_node = new XMLNode (X_("remote_control")); snprintf (buf, sizeof (buf), "%d", _remote_control_id); @@ -1534,7 +1538,7 @@ Route::state(bool full_state) cmt->add_content (_comment); } - for (i = _redirects.begin(); i != _redirects.end(); ++i) { + for (i = _processors.begin(); i != _processors.end(); ++i) { node->add_child_nocopy((*i)->state (full_state)); } @@ -1546,10 +1550,10 @@ Route::state(bool full_state) } XMLNode& -Route::get_redirect_state () +Route::get_processor_state () { XMLNode* root = new XMLNode (X_("redirects")); - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { root->add_child_nocopy ((*i)->state (true)); } @@ -1557,7 +1561,7 @@ Route::get_redirect_state () } int -Route::set_redirect_state (const XMLNode& root) +Route::set_processor_state (const XMLNode& root) { if (root.name() != X_("redirects")) { return -1; @@ -1567,13 +1571,13 @@ Route::set_redirect_state (const XMLNode& root) XMLNodeList nnlist; XMLNodeConstIterator iter; XMLNodeConstIterator niter; - Glib::RWLock::ReaderLock lm (redirect_lock); + Glib::RWLock::ReaderLock lm (_processor_lock); nlist = root.children(); for (iter = nlist.begin(); iter != nlist.end(); ++iter){ - /* iter now points to a Redirect state node */ + /* iter now points to a IOProcessor state node */ nnlist = (*iter)->children (); @@ -1588,15 +1592,15 @@ Route::set_redirect_state (const XMLNode& root) XMLProperty* prop = (*niter)->property (X_("id")); if (!prop) { - warning << _("Redirect node has no ID, ignored") << endmsg; + warning << _("IOProcessor node has no ID, ignored") << endmsg; break; } ID id = prop->value (); - /* now look for a redirect with that ID */ + /* now look for a processor with that ID */ - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if ((*i)->id() == id) { (*i)->set_state (**iter); break; @@ -1626,7 +1630,7 @@ Route::set_deferred_state () nlist = deferred_state->children(); for (niter = nlist.begin(); niter != nlist.end(); ++niter){ - add_redirect_from_xml (**niter); + add_processor_from_xml (**niter); } delete deferred_state; @@ -1634,16 +1638,16 @@ Route::set_deferred_state () } void -Route::add_redirect_from_xml (const XMLNode& node) +Route::add_processor_from_xml (const XMLNode& node) { const XMLProperty *prop; + // legacy sessions use a different node name for sends if (node.name() == "Send") { - - + try { boost::shared_ptr send (new Send (_session, node)); - add_redirect (send, this); + add_processor (send); } catch (failed_constructor &err) { @@ -1651,36 +1655,40 @@ Route::add_redirect_from_xml (const XMLNode& node) return; } - } else if (node.name() == "Insert") { + // use "Processor" in XML? + } else if (node.name() == "Processor") { try { if ((prop = node.property ("type")) != 0) { - boost::shared_ptr insert; + boost::shared_ptr processor; if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "vst") { - insert.reset (new PluginInsert(_session, node)); + processor.reset (new PluginInsert(_session, node)); } else if (prop->value() == "port") { + processor.reset (new PortInsert (_session, node)); + + } else if (prop->value() == "send") { - insert.reset (new PortInsert (_session, node)); + processor.reset (new Send (_session, node)); } else { - error << string_compose(_("unknown Insert type \"%1\"; ignored"), prop->value()) << endmsg; + error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg; } - add_redirect (insert, this); + add_processor (processor); } else { - error << _("Insert XML node has no type property") << endmsg; + error << _("Processor XML node has no type property") << endmsg; } } catch (failed_constructor &err) { - warning << _("insert could not be created. Ignored.") << endmsg; + warning << _("processor could not be created. Ignored.") << endmsg; return; } } @@ -1826,24 +1834,24 @@ Route::_set_state (const XMLNode& node, bool call_base) } } - XMLNodeList redirect_nodes; + XMLNodeList processor_nodes; for (niter = nlist.begin(); niter != nlist.end(); ++niter){ child = *niter; - if (child->name() == X_("Send") || child->name() == X_("Insert")) { - redirect_nodes.push_back(child); + if (child->name() == X_("Send") || child->name() == X_("Processor")) { + processor_nodes.push_back(child); } } - _set_redirect_states(redirect_nodes); + _set_processor_states(processor_nodes); for (niter = nlist.begin(); niter != nlist.end(); ++niter){ child = *niter; - // All redirects (sends and inserts) have been applied already + // All processors have been applied already if (child->name() == X_("Automation")) { @@ -1873,12 +1881,12 @@ Route::_set_state (const XMLNode& node, bool call_base) } else if (child->name() == X_("controllable") && (prop = child->property("name")) != 0) { if (prop->value() == "solo") { - _solo_control.set_state (*child); - _session.add_controllable (&_solo_control); + _solo_control->set_state (*child); + _session.add_controllable (_solo_control); } else if (prop->value() == "mute") { - _mute_control.set_state (*child); - _session.add_controllable (&_mute_control); + _mute_control->set_state (*child); + _session.add_controllable (_mute_control); } } else if (child->name() == X_("remote_control")) { @@ -1903,33 +1911,37 @@ Route::_set_state (const XMLNode& node, bool call_base) } void -Route::_set_redirect_states(const XMLNodeList &nlist) +Route::_set_processor_states(const XMLNodeList &nlist) { XMLNodeConstIterator niter; char buf[64]; - RedirectList::iterator i, o; + ProcessorList::iterator i, o; - // Iterate through existing redirects, remove those which are not in the state list - for (i = _redirects.begin(); i != _redirects.end(); ) { - RedirectList::iterator tmp = i; + // Iterate through existing processors, remove those which are not in the state list + for (i = _processors.begin(); i != _processors.end(); ) { + ProcessorList::iterator tmp = i; ++tmp; - bool redirectInStateList = false; + bool processorInStateList = false; (*i)->id().print (buf, sizeof (buf)); for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if (strncmp(buf,(*niter)->child(X_("Redirect"))->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) { - redirectInStateList = true; + // legacy sessions (IOProcessor as a child of Processor, both is-a IO) + if (strncmp(buf,(*niter)->child(X_("IOProcessor"))->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) { + processorInStateList = true; + break; + } else if (strncmp(buf,(*niter)->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) { + processorInStateList = true; break; } } - if (!redirectInStateList) { - remove_redirect ( *i, this); + if (!processorInStateList) { + remove_processor (*i); } @@ -1937,65 +1949,68 @@ Route::_set_redirect_states(const XMLNodeList &nlist) } - // Iterate through state list and make sure all redirects are on the track and in the correct order, - // set the state of existing redirects according to the new state on the same go - i = _redirects.begin(); + // Iterate through state list and make sure all processors are on the track and in the correct order, + // set the state of existing processors according to the new state on the same go + i = _processors.begin(); for (niter = nlist.begin(); niter != nlist.end(); ++niter, ++i) { - // Check whether the next redirect in the list + // Check whether the next processor in the list o = i; - while (o != _redirects.end()) { + while (o != _processors.end()) { (*o)->id().print (buf, sizeof (buf)); - if ( strncmp(buf, (*niter)->child(X_("Redirect"))->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) + if ( strncmp(buf, (*niter)->child(X_("IOProcessor"))->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) + break; + else if (strncmp(buf,(*niter)->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) break; + ++o; } - if (o == _redirects.end()) { - // If the redirect (*niter) is not on the route, we need to create it + if (o == _processors.end()) { + // If the processor (*niter) is not on the route, we need to create it // and move it to the correct location - RedirectList::iterator prev_last = _redirects.end(); + ProcessorList::iterator prev_last = _processors.end(); --prev_last; // We need this to check whether adding succeeded - add_redirect_from_xml (**niter); + add_processor_from_xml (**niter); - RedirectList::iterator last = _redirects.end(); + ProcessorList::iterator last = _processors.end(); --last; if (prev_last == last) { - cerr << "Could not fully restore state as some redirects were not possible to create" << endl; + cerr << "Could not fully restore state as some processors were not possible to create" << endl; continue; } - boost::shared_ptr tmp = (*last); - // remove the redirect from the wrong location - _redirects.erase(last); - // insert the new redirect at the current location - _redirects.insert(i, tmp); + boost::shared_ptr tmp = (*last); + // remove the processor from the wrong location + _processors.erase(last); + // processor the new processor at the current location + _processors.insert(i, tmp); - --i; // move pointer to the newly inserted redirect + --i; // move pointer to the newly processored processor continue; } - // We found the redirect (*niter) on the route, first we must make sure the redirect + // We found the processor (*niter) on the route, first we must make sure the processor // is at the location provided in the XML state if (i != o) { - boost::shared_ptr tmp = (*o); + boost::shared_ptr tmp = (*o); // remove the old copy - _redirects.erase(o); - // insert the redirect at the correct location - _redirects.insert(i, tmp); + _processors.erase(o); + // processor the processor at the correct location + _processors.insert(i, tmp); - --i; // move pointer so it points to the right redirect + --i; // move pointer so it points to the right processor } (*i)->set_state( (**niter) ); } - redirects_changed(this); + processors_changed (); } void @@ -2017,10 +2032,10 @@ Route::silence (nframes_t nframes, nframes_t offset) } { - Glib::RWLock::ReaderLock lm (redirect_lock, Glib::TRY_LOCK); + Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); if (lm.locked()) { - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr pi; if (!_active && (pi = boost::dynamic_pointer_cast (*i)) != 0) { // skip plugins, they don't need anything when we're not active @@ -2042,7 +2057,7 @@ Route::silence (nframes_t nframes, nframes_t offset) int Route::set_control_outs (const vector& ports) { - Glib::Mutex::Lock lm (control_outs_lock); + Glib::Mutex::Lock lm (_control_outs_lock); vector::const_iterator i; size_t limit; @@ -2051,7 +2066,7 @@ Route::set_control_outs (const vector& ports) _control_outs = 0; } - if (control() || master()) { + if (is_control() || is_master()) { /* no control outs for these two special busses */ return 0; } @@ -2080,7 +2095,7 @@ Route::set_control_outs (const vector& ports) /* now connect to the named ports */ for (size_t n = 0; n < limit; ++n) { - if (_control_outs->connect_output (_control_outs->output (n), ports[n], this)) { + if (_control_outs->connect_output (_control_outs->output (n), ports[n % ports.size()], this)) { error << string_compose (_("could not connect %1 to %2"), _control_outs->output(n)->name(), ports[n]) << endmsg; return -1; } @@ -2170,15 +2185,22 @@ Route::feeds (boost::shared_ptr other) } } - /* check Redirects which may also interconnect Routes */ + /* check IOProcessors which may also interconnect Routes */ - for (RedirectList::iterator r = _redirects.begin(); r != _redirects.end(); r++) { + for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); r++) { - no = (*r)->n_outputs().n_total(); + boost::shared_ptr redirect = boost::dynamic_pointer_cast(*r); + + if ( ! redirect) + continue; + + // TODO: support internal redirects here + + no = redirect->io()->n_outputs().n_total(); for (i = 0; i < no; ++i) { for (j = 0; j < ni; ++j) { - if ((*r)->output(i)->connected_to (other->input (j)->name())) { + if (redirect->io()->output(i)->connected_to (other->input (j)->name())) { return true; } } @@ -2256,24 +2278,24 @@ void Route::set_active (bool yn) { _active = yn; - active_changed(); /* EMIT SIGNAL */ + active_changed(); /* EMIT SIGNAL */ } void -Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_flush_redirects) +Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_flush_processors) { nframes_t now = _session.transport_frame(); { - Glib::RWLock::ReaderLock lm (redirect_lock); + Glib::RWLock::ReaderLock lm (_processor_lock); if (!did_locate) { automation_snapshot (now); } - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if (Config->get_plugins_stop_with_transport() && can_flush_redirects) { + if (Config->get_plugins_stop_with_transport() && can_flush_processors) { (*i)->deactivate (); (*i)->activate (); } @@ -2314,7 +2336,7 @@ Route::pans_required () const return 0; } - return max (n_inputs ().n_audio(), static_cast(redirect_max_outs.n_audio())); + return max (n_inputs ().n_audio(), processor_max_outs.n_audio()); } int @@ -2371,15 +2393,15 @@ Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nfra bool can_record, bool rec_monitors_input) { { - Glib::RWLock::ReaderLock lm (redirect_lock, Glib::TRY_LOCK); + Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); if (lm.locked()) { // automation snapshot can also be called from the non-rt context - // and it uses the redirect list, so we take the lock out here + // and it uses the processor list, so we take the lock out here automation_snapshot (_session.transport_frame()); } } - if ((n_outputs().n_total() == 0 && _redirects.empty()) || n_inputs().n_total() == 0 || !_active) { + if ((n_outputs().n_total() == 0 && _processors.empty()) || n_inputs().n_total() == 0 || !_active) { silence (nframes, offset); return 0; } @@ -2395,12 +2417,13 @@ Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nfra apply_gain_automation = false; { - Glib::Mutex::Lock am (automation_lock, Glib::TRY_LOCK); + Glib::Mutex::Lock am (_automation_lock, Glib::TRY_LOCK); if (am.locked() && _session.transport_rolling()) { - if (gain_automation_playback()) { - apply_gain_automation = _gain_automation_curve.rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes); + if (_gain_control->list()->automation_playback()) { + apply_gain_automation = _gain_control->list()->curve().rt_safe_get_vector ( + start_frame, end_frame, _session.gain_automation_buffer(), nframes); } } } @@ -2429,13 +2452,15 @@ Route::toggle_monitor_input () bool Route::has_external_redirects () const { + // FIXME: what about sends? + boost::shared_ptr pi; - for (RedirectList::const_iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { if ((pi = boost::dynamic_pointer_cast(*i)) != 0) { - for (PortSet::const_iterator port = pi->outputs().begin(); - port != pi->outputs().end(); ++port) { + for (PortSet::const_iterator port = pi->io()->outputs().begin(); + port != pi->io()->outputs().end(); ++port) { string port_name = port->name(); string client_name = port_name.substr (0, port_name.find(':')); @@ -2453,15 +2478,15 @@ Route::has_external_redirects () const } void -Route::flush_redirects () +Route::flush_processors () { /* XXX shouldn't really try to take this lock, since this is called from the RT audio thread. */ - Glib::RWLock::ReaderLock lm (redirect_lock); + Glib::RWLock::ReaderLock lm (_processor_lock); - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->deactivate (); (*i)->activate (); } @@ -2480,32 +2505,62 @@ Route::set_meter_point (MeterPoint p, void *src) nframes_t Route::update_total_latency () { - _own_latency = 0; + nframes_t old = _own_latency; + + if (_user_latency) { + _own_latency = _user_latency; + } else { + _own_latency = 0; - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { - if ((*i)->active ()) { - _own_latency += (*i)->latency (); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + if ((*i)->active ()) { + _own_latency += (*i)->signal_latency (); + } } } set_port_latency (_own_latency); + + if (!_user_latency) { + /* this (virtual) function is used for pure Routes, + not derived classes like AudioTrack. this means + that the data processed here comes from an input + port, not prerecorded material, and therefore we + have to take into account any input latency. + */ - /* this (virtual) function is used for pure Routes, - not derived classes like AudioTrack. this means - that the data processed here comes from an input - port, not prerecorded material, and therefore we - have to take into account any input latency. - */ - _own_latency += input_latency (); + _own_latency += input_latency (); + } + if (old != _own_latency) { + signal_latency_changed (); /* EMIT SIGNAL */ + } + return _own_latency; } +void +Route::set_user_latency (nframes_t nframes) +{ + Latent::set_user_latency (nframes); + _session.update_latency_compensation (false, false); +} + void Route::set_latency_delay (nframes_t longest_session_latency) { - _initial_delay = longest_session_latency - _own_latency; + nframes_t old = _initial_delay; + + if (_own_latency < longest_session_latency) { + _initial_delay = longest_session_latency - _own_latency; + } else { + _initial_delay = 0; + } + + if (_initial_delay != old) { + initial_delay_changed (); /* EMIT SIGNAL */ + } if (_session.transport_stopped()) { _roll_delay = _initial_delay; @@ -2517,7 +2572,7 @@ Route::automation_snapshot (nframes_t now) { IO::automation_snapshot (now); - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->automation_snapshot (now); } } @@ -2567,47 +2622,18 @@ Route::ToggleControllable::get_value (void) const void Route::set_block_size (nframes_t nframes) { - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->set_block_size (nframes); } } -void -Route::redirect_active_proxy (Redirect* ignored, void* ignored_src) -{ - _session.update_latency_compensation (false, false); -} - void Route::protect_automation () { - switch (gain_automation_state()) { - case Write: - set_gain_automation_state (Off); - case Touch: - set_gain_automation_state (Play); - break; - default: - break; - } - - switch (panner().automation_state ()) { - case Write: - panner().set_automation_state (Off); - break; - case Touch: - panner().set_automation_state (Play); - break; - default: - break; - } + Automatable::protect_automation(); - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { - boost::shared_ptr pi; - if ((pi = boost::dynamic_pointer_cast (*i)) != 0) { - pi->protect_automation (); - } - } + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) + (*i)->protect_automation(); } void