X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=4277bea0226a190ac957974e9118acb8416a38a2;hb=cda09a0878eec8e553b3800fe2decd0c110e487b;hp=091a669dbcbae33ba6e794863b460071518bb3bc;hpb=1ceee92bb57744259afca833abbd07a2013567ee;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 091a669dbc..4277bea022 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -17,6 +17,10 @@ */ +#ifdef WAF_BUILD +#include "libardour-config.h" +#endif + #include #include #include @@ -27,6 +31,7 @@ #include "pbd/memento_command.h" #include "pbd/stacktrace.h" #include "pbd/convert.h" +#include "pbd/boost_debug.h" #include "evoral/Curve.hpp" @@ -46,7 +51,9 @@ #include "ardour/meter.h" #include "ardour/mix.h" #include "ardour/monitor_processor.h" +#include "ardour/pannable.h" #include "ardour/panner.h" +#include "ardour/panner_shell.h" #include "ardour/plugin_insert.h" #include "ardour/port.h" #include "ardour/port_insert.h" @@ -58,6 +65,9 @@ #include "ardour/session.h" #include "ardour/timestamps.h" #include "ardour/utils.h" +#include "ardour/graph.h" +#include "ardour/unknown_processor.h" +#include "ardour/capturing_processor.h" #include "i18n.h" @@ -71,25 +81,23 @@ PBD::Signal0 Route::RemoteControlIDChange; Route::Route (Session& sess, string name, Flag flg, DataType default_type) : SessionObject (sess, name) - , AutomatableControls (sess) + , Automatable (sess) + , GraphNode( sess.route_graph ) , _active (true) , _initial_delay (0) , _roll_delay (0) , _flags (flg) , _pending_declick (true) , _meter_point (MeterPostFader) - , _phase_invert (0) , _self_solo (false) - , _soloed_by_others (0) + , _soloed_by_others_upstream (0) + , _soloed_by_others_downstream (0) , _solo_isolated (0) , _denormal_protection (false) , _recordable (true) , _silent (false) , _declickable (false) - , _solo_control (new SoloControllable (X_("solo"), *this)) - , _mute_control (new MuteControllable (X_("mute"), *this)) , _mute_master (new MuteMaster (sess, name)) - , _mute_points (MuteMaster::AllPoints) , _have_internal_generator (false) , _solo_safe (false) , _default_type (default_type) @@ -103,14 +111,25 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) int Route::init () { - /* add standard controls */ + /* add standard controls */ + + _solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ())); + _mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ())); _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle)); - _mute_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle)); - + _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle)); + add_control (_solo_control); add_control (_mute_control); + /* panning */ + + Pannable* p = new Pannable (_session); +#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS + boost_debug_shared_ptr_mark_interesting (p, "Pannable"); +#endif + _pannable.reset (p); + /* input and output objects */ _input.reset (new IO (_session, _name, IO::Input, _default_type)); @@ -119,52 +138,56 @@ Route::init () _input->changed.connect_same_thread (*this, boost::bind (&Route::input_change_handler, this, _1, _2)); _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2)); + _input->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::input_port_count_changing, this, _1)); + /* add amp processor */ - _amp.reset (new Amp (_session, _mute_master)); + _amp.reset (new Amp (_session)); add_processor (_amp, PostFader); - /* add standard processors: meter, main outs, monitor out */ + /* create standard processors: meter, main outs, monitor out; + they will be added to _processors by setup_invisible_processors () + */ _meter.reset (new PeakMeter (_session)); _meter->set_display_to_user (false); + _meter->activate (); - add_processor (_meter, PostFader); - - _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main)); - - add_processor (_main_outs, PostFader); + _main_outs.reset (new Delivery (_session, _output, _pannable, _mute_master, _name, Delivery::Main)); + _main_outs->activate (); if (is_monitor()) { /* where we listen to tracks */ _intreturn.reset (new InternalReturn (_session)); - add_processor (_intreturn, PreFader); - - ProcessorList::iterator i; - - for (i = _processors.begin(); i != _processors.end(); ++i) { - if (*i == _intreturn) { - ++i; - break; - } - } + _intreturn->activate (); /* the thing that provides proper control over a control/monitor/listen bus (such as per-channel cut, dim, solo, invert, etc). - It always goes right after the internal return; */ _monitor_control.reset (new MonitorProcessor (_session)); - add_processor (_monitor_control, i); + _monitor_control->activate (); /* no panning on the monitor main outs */ +#ifdef PANNER_HACKS _main_outs->panner()->set_bypassed (true); +#endif } + if (is_master() || is_monitor() || is_hidden()) { + _mute_master->set_solo_ignore (true); + } + /* now that we have _meter, its safe to connect to this */ Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this))); + { + /* run a configure so that the invisible processors get set up */ + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + configure_processors (0); + } + return 0; } @@ -207,7 +230,7 @@ Route::remote_control_id() const return _remote_control_id; } -long +int32_t Route::order_key (std::string const & name) const { OrderKeys::const_iterator i = order_keys.find (name); @@ -219,17 +242,34 @@ Route::order_key (std::string const & name) const } void -Route::set_order_key (std::string const & name, long n) +Route::set_order_key (std::string const & name, int32_t n) { - order_keys[name] = n; + bool changed = false; + + /* This method looks more complicated than it should, but + it's important that we don't emit order_key_changed unless + it actually has, as expensive things happen on receipt of that + signal. + */ + + if (order_keys.find(name) == order_keys.end() || order_keys[name] != n) { + order_keys[name] = n; + changed = true; + } if (Config->get_sync_all_route_ordering()) { for (OrderKeys::iterator x = order_keys.begin(); x != order_keys.end(); ++x) { - x->second = n; + if (x->second != n) { + x->second = n; + changed = true; + } } } - _session.set_dirty (); + if (changed) { + order_key_changed (); /* EMIT SIGNAL */ + _session.set_dirty (); + } } /** Set all order keys to be the same as that for `base', if such a key @@ -244,7 +284,7 @@ Route::sync_order_keys (std::string const & base) } OrderKeys::iterator i; - uint32_t key; + int32_t key; if ((i = order_keys.find (base)) == order_keys.end()) { /* key doesn't exist, use the first existing key (during session initialization) */ @@ -257,8 +297,17 @@ Route::sync_order_keys (std::string const & base) i = order_keys.begin(); } + bool changed = false; + for (; i != order_keys.end(); ++i) { - i->second = key; + if (i->second != key) { + i->second = key; + changed = true; + } + } + + if (changed) { + order_key_changed (); /* EMIT SIGNAL */ } } @@ -267,8 +316,8 @@ Route::ensure_track_or_route_name(string name, Session &session) { string newname = name; - while (session.route_by_name (newname) != NULL) { - newname = bump_name_once (newname); + while (!session.io_name_is_legal (newname)) { + newname = bump_name_once (newname, '.'); } return newname; @@ -319,11 +368,11 @@ Route::set_gain (gain_t val, void *src) } } - _route_group->apply (&Route::inc_gain, factor, _route_group); + _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor, _route_group)); } else { - _route_group->apply (&Route::set_gain, val, _route_group); + _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, _route_group)); } return; @@ -348,8 +397,9 @@ Route::set_gain (gain_t val, void *src) */ void Route::process_output_buffers (BufferSet& bufs, - sframes_t start_frame, sframes_t end_frame, nframes_t nframes, - bool /*with_processors*/, int declick) + framepos_t start_frame, framepos_t end_frame, pframes_t nframes, + bool /*with_processors*/, int declick, + bool gain_automation_ok) { bool monitor; @@ -369,9 +419,12 @@ Route::process_output_buffers (BufferSet& bufs, } /* figure out if we're going to use gain automation */ - _amp->setup_gain_automation (start_frame, end_frame, nframes); - - + if (gain_automation_ok) { + _amp->setup_gain_automation (start_frame, end_frame, nframes); + } else { + _amp->apply_gain_automation (false); + } + /* tell main outs what to do about monitoring */ _main_outs->no_outs_cuz_we_no_monitor (!monitor); @@ -381,9 +434,9 @@ Route::process_output_buffers (BufferSet& bufs, ----------------------------------------------------------------------------------------- */ if (declick > 0) { - Amp::apply_gain (bufs, nframes, 0.0, 1.0); + Amp::declick (bufs, nframes, 1); } else if (declick < 0) { - Amp::apply_gain (bufs, nframes, 1.0, 0.0); + Amp::declick (bufs, nframes, -1); } _pending_declick = 0; @@ -392,7 +445,7 @@ Route::process_output_buffers (BufferSet& bufs, DENORMAL CONTROL/PHASE INVERT ----------------------------------------------------------------------------------------- */ - if (_phase_invert) { + if (_phase_invert.any ()) { int chn = 0; @@ -401,13 +454,13 @@ Route::process_output_buffers (BufferSet& bufs, for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) { Sample* const sp = i->data(); - if (_phase_invert & chn) { - for (nframes_t nx = 0; nx < nframes; ++nx) { + if (_phase_invert[chn]) { + for (pframes_t nx = 0; nx < nframes; ++nx) { sp[nx] = -sp[nx]; sp[nx] += 1.0e-27f; } } else { - for (nframes_t nx = 0; nx < nframes; ++nx) { + for (pframes_t nx = 0; nx < nframes; ++nx) { sp[nx] += 1.0e-27f; } } @@ -418,8 +471,8 @@ Route::process_output_buffers (BufferSet& bufs, for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) { Sample* const sp = i->data(); - if (_phase_invert & (1<data(); - for (nframes_t nx = 0; nx < nframes; ++nx) { + for (pframes_t nx = 0; nx < nframes; ++nx) { sp[nx] += 1.0e-27f; } } @@ -444,21 +497,29 @@ Route::process_output_buffers (BufferSet& bufs, and go .... ----------------------------------------------------------------------------------------- */ - Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if (rm.locked()) { - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + if (boost::dynamic_pointer_cast (*i)) { + break; + } - if (bufs.count() != (*i)->input_streams()) { - cerr << _name << " bufs = " << bufs.count() - << " input for " << (*i)->name() << " = " << (*i)->input_streams() - << endl; - } - assert (bufs.count() == (*i)->input_streams()); +#ifndef NDEBUG + /* if it has any inputs, make sure they match */ + if ((*i)->input_streams() != ChanCount::ZERO) { + if (bufs.count() != (*i)->input_streams()) { + cerr << _name << " bufs = " << bufs.count() + << " input for " << (*i)->name() << " = " << (*i)->input_streams() + << endl; + abort (); + } + } +#endif + /* should we NOT run plugins here if the route is inactive? + do we catch route != active somewhere higher? + */ - (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back()); - bufs.set_count ((*i)->output_streams()); - } + (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back()); + bufs.set_count ((*i)->output_streams()); } } @@ -469,7 +530,7 @@ Route::n_process_buffers () } void -Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick) +Route::passthru (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick) { BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); @@ -478,7 +539,7 @@ Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, assert (bufs.available() >= input_streams()); if (_input->n_ports() == ChanCount::ZERO) { - silence (nframes); + silence_unlocked (nframes); } bufs.set_count (input_streams()); @@ -505,29 +566,33 @@ Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, } write_out_of_band_data (bufs, start_frame, end_frame, nframes); - process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick); + process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, true); } void -Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick) +Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick) { BufferSet& bufs (_session.get_silent_buffers (n_process_buffers())); bufs.set_count (_input->n_ports()); write_out_of_band_data (bufs, start_frame, end_frame, nframes); - process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick); + process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, false); } void Route::set_listen (bool yn, void* src) { + if (_solo_safe) { + return; + } + if (_monitor_send) { if (yn != _monitor_send->active()) { if (yn) { - _monitor_send->set_solo_level (1); _monitor_send->activate (); - } else { - _monitor_send->set_solo_level (0); + _mute_master->set_soloed (true); + } else { _monitor_send->deactivate (); + _mute_master->set_soloed (false); } listen_changed (src); /* EMIT SIGNAL */ @@ -536,7 +601,7 @@ Route::set_listen (bool yn, void* src) } bool -Route::listening () const +Route::listening_via_monitor () const { if (_monitor_send) { return _monitor_send->active (); @@ -568,14 +633,14 @@ Route::set_solo (bool yn, void *src) } if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) { - _route_group->apply (&Route::set_solo, yn, _route_group); + _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group)); return; } if (self_soloed() != yn) { set_self_solo (yn); - set_delivery_solo (); - solo_changed (src); /* EMIT SIGNAL */ + set_mute_master_solo (); + solo_changed (true, src); /* EMIT SIGNAL */ _solo_control->Changed (); /* EMIT SIGNAL */ } } @@ -583,42 +648,91 @@ Route::set_solo (bool yn, void *src) void Route::set_self_solo (bool yn) { - _self_solo = yn; + _self_solo = yn; } void -Route::mod_solo_by_others (int32_t delta) +Route::mod_solo_by_others_upstream (int32_t delta) { + if (_solo_safe) { + return; + } + + uint32_t old_sbu = _soloed_by_others_upstream; + if (delta < 0) { - if (_soloed_by_others >= (uint32_t) abs (delta)) { - _soloed_by_others += delta; + if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) { + _soloed_by_others_upstream += delta; } else { - _soloed_by_others = 0; + _soloed_by_others_upstream = 0; } } else { - _soloed_by_others += delta; - } + _soloed_by_others_upstream += delta; + } + + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n", + name(), delta, _soloed_by_others_upstream, old_sbu, + _soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo())); + + /* push the inverse solo change to everything that feeds us. + + This is important for solo-within-group. When we solo 1 track out of N that + feed a bus, that track will cause mod_solo_by_upstream (+1) to be called + on the bus. The bus then needs to call mod_solo_by_downstream (-1) on all + tracks that feed it. This will silence them if they were audible because + of a bus solo, but the newly soloed track will still be audible (because + it is self-soloed). + + but .. do this only when we are being told to solo-by-upstream (i.e delta = +1), + not in reverse. + */ + + if ((_self_solo || _soloed_by_others_downstream) && + ((old_sbu == 0 && _soloed_by_others_upstream > 0) || + (old_sbu > 0 && _soloed_by_others_upstream == 0))) { + + if (delta > 0 || !Config->get_exclusive_solo()) { + DEBUG_TRACE (DEBUG::Solo, "\t ... INVERT push\n"); + for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) { + boost::shared_ptr sr = i->r.lock(); + if (sr) { + sr->mod_solo_by_others_downstream (-delta); + } + } + } + } - set_delivery_solo (); + set_mute_master_solo (); + solo_changed (false, this); } void -Route::set_delivery_solo () +Route::mod_solo_by_others_downstream (int32_t delta) { - /* tell all delivery processors what the solo situation is, so that they keep - delivering even though Session::soloing() is true and they were not - explicitly soloed. - */ + if (_solo_safe) { + return; + } - Glib::RWLock::ReaderLock rm (_processor_lock); - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - boost::shared_ptr d; - - if ((d = boost::dynamic_pointer_cast (*i)) != 0) { - d->set_solo_level (soloed ()); - d->set_solo_isolated (solo_isolated()); + if (delta < 0) { + if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) { + _soloed_by_others_downstream += delta; + } else { + _soloed_by_others_downstream = 0; } + } else { + _soloed_by_others_downstream += delta; } + + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream)); + + set_mute_master_solo (); + solo_changed (false, this); +} + +void +Route::set_mute_master_solo () +{ + _mute_master->set_soloed (self_soloed() || soloed_by_others_downstream() || soloed_by_others_upstream()); } void @@ -629,7 +743,7 @@ Route::set_solo_isolated (bool yn, void *src) } if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) { - _route_group->apply (&Route::set_solo_isolated, yn, _route_group); + _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, _route_group)); return; } @@ -637,34 +751,42 @@ Route::set_solo_isolated (bool yn, void *src) boost::shared_ptr routes = _session.get_routes (); for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { + + if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) { + continue; + } + bool sends_only; - bool does_feed = feeds (*i, &sends_only); + bool does_feed = direct_feeds (*i, &sends_only); // we will recurse anyway, so don't use ::feeds() if (does_feed && !sends_only) { (*i)->set_solo_isolated (yn, (*i)->route_group()); } } - /* XXX should we back-propagate as well? */ + /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */ - bool changed = false; + bool changed = false; if (yn) { - if (_solo_isolated == 0) { - changed = true; - } + if (_solo_isolated == 0) { + _mute_master->set_solo_ignore (true); + changed = true; + } _solo_isolated++; } else { - changed = (_solo_isolated == 1); if (_solo_isolated > 0) { _solo_isolated--; + if (_solo_isolated == 0) { + _mute_master->set_solo_ignore (false); + changed = true; + } } } - if (changed) { - set_delivery_solo (); - solo_isolated_changed (src); - } + if (changed) { + solo_isolated_changed (src); + } } bool @@ -676,38 +798,34 @@ Route::solo_isolated () const void Route::set_mute_points (MuteMaster::MutePoint mp) { - _mute_points = mp; - mute_points_changed (); /* EMIT SIGNAL */ - - if (_mute_master->muted()) { - _mute_master->mute_at (_mute_points); - mute_changed (this); /* EMIT SIGNAL */ - } + _mute_master->set_mute_points (mp); + mute_points_changed (); /* EMIT SIGNAL */ + + if (_mute_master->muted_by_self()) { + mute_changed (this); /* EMIT SIGNAL */ + _mute_control->Changed (); /* EMIT SIGNAL */ + } } void Route::set_mute (bool yn, void *src) { if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_mute()) { - _route_group->apply (&Route::set_mute, yn, _route_group); + _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, _route_group)); return; } if (muted() != yn) { - if (yn) { - _mute_master->mute_at (_mute_points); - } else { - _mute_master->clear_mute (); - } - + _mute_master->set_muted_by_self (yn); mute_changed (src); /* EMIT SIGNAL */ + _mute_control->Changed (); /* EMIT SIGNAL */ } } bool -Route::muted() const +Route::muted () const { - return _mute_master->muted (); + return _mute_master->muted_by_self(); } #if 0 @@ -745,13 +863,16 @@ Route::add_processor (boost::shared_ptr processor, Placement placemen /** Add a processor to the route. - * @a iter must point to an iterator in _processors and the new - * processor will be inserted immediately before this location. Otherwise, - * @a position is used. + * @param iter an iterator in _processors; the new processor will be inserted immediately before this location. */ int Route::add_processor (boost::shared_ptr processor, ProcessorList::iterator iter, ProcessorStreams* err, bool activation_allowed) { + assert (processor != _meter); + assert (processor != _main_outs); + + DEBUG_TRACE (DEBUG::Processors, string_compose ("%1 adding processor %2\n", name(), processor->name())); + ChanCount old_pms = processor_max_streams; if (!_session.engine().connected() || !processor) { @@ -760,14 +881,15 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite { Glib::RWLock::WriterLock lm (_processor_lock); + ProcessorState pstate (this); boost::shared_ptr pi; boost::shared_ptr porti; ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), processor); - if (processor == _amp || processor == _meter || processor == _main_outs) { - // Ensure only one of these are in the list at any time + if (processor == _amp) { + // Ensure only one amp is in the list at any time if (loc != _processors.end()) { if (iter == loc) { // Already in place, do nothing return 0; @@ -790,13 +912,14 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite // Set up processor list channels. This will set processor->[input|output]_streams(), // configure redirect ports properly, etc. - if (configure_processors_unlocked (err)) { - ProcessorList::iterator ploc = loc; - --ploc; - _processors.erase(ploc); - configure_processors_unlocked (0); // it worked before we tried to add it ... - cerr << "configure failed\n"; - return -1; + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + + if (configure_processors_unlocked (err)) { + pstate.restore (); + configure_processors_unlocked (0); // it worked before we tried to add it ... + return -1; + } } if ((pi = boost::dynamic_pointer_cast(processor)) != 0) { @@ -808,29 +931,7 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite } - /* all delivery processors on master, monitor and auditioner never ever pay attention to solo - */ - boost::shared_ptr d = boost::dynamic_pointer_cast(processor); - - if (d && (is_master() || is_monitor() || is_hidden())) { - d->set_solo_ignored (true); - } - - /* is this the monitor send ? if so, make sure we keep track of it */ - - boost::shared_ptr isend = boost::dynamic_pointer_cast (processor); - - if (isend && _session.monitor_out() && (isend->target_id() == _session.monitor_out()->id())) { - _monitor_send = isend; - - if (_monitor_send->active()) { - _monitor_send->set_solo_level (1); - } else { - _monitor_send->set_solo_level (0); - } - } - - if (activation_allowed && (processor != _monitor_send)) { + if (activation_allowed) { processor->activate (); } @@ -840,18 +941,38 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite } processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + set_processor_positions (); return 0; } bool -Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorList::iterator iter) +Route::add_processor_from_xml_2X (const XMLNode& node, int version) { const XMLProperty *prop; try { boost::shared_ptr processor; + /* bit of a hack: get the `placement' property from the tag here + so that we can add the processor in the right place (pre/post-fader) + */ + + XMLNodeList const & children = node.children (); + XMLNodeList::const_iterator i = children.begin (); + + while (i != children.end() && (*i)->name() != X_("Redirect")) { + ++i; + } + + Placement placement = PreFader; + + if (i != children.end()) { + if ((prop = (*i)->property (X_("placement"))) != 0) { + placement = Placement (string_2_enum (prop->value(), placement)); + } + } + if (node.name() == "Insert") { if ((prop = node.property ("type")) != 0) { @@ -865,14 +986,14 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorLis } else { - processor.reset (new PortInsert (_session, _mute_master)); + processor.reset (new PortInsert (_session, _pannable, _mute_master)); } } } else if (node.name() == "Send") { - processor.reset (new Send (_session, _mute_master)); + processor.reset (new Send (_session, _pannable, _mute_master)); } else { @@ -884,19 +1005,7 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorLis return false; } - if (iter == _processors.end() && processor->display_to_user() && !_processors.empty()) { - /* check for invisible processors stacked at the end and leave them there */ - ProcessorList::iterator p; - p = _processors.end(); - --p; - while (!(*p)->display_to_user() && p != _processors.begin()) { - --p; - } - ++p; - iter = p; - } - - return (add_processor (processor, iter) == 0); + return (add_processor (processor, placement) == 0); } catch (failed_constructor &err) { @@ -908,26 +1017,20 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorLis int Route::add_processors (const ProcessorList& others, boost::shared_ptr before, ProcessorStreams* err) { + /* NOTE: this is intended to be used ONLY when copying + processors from another Route. Hence the subtle + differences between this and ::add_processor() + */ + ProcessorList::iterator loc; if (before) { loc = find(_processors.begin(), _processors.end(), before); } else { - /* nothing specified - at end but before main outs */ - loc = find (_processors.begin(), _processors.end(), _main_outs); + /* nothing specified - at end */ + loc = _processors.end (); } - return add_processors (others, loc, err); -} - -int -Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter, ProcessorStreams* err) -{ - /* NOTE: this is intended to be used ONLY when copying - processors from another Route. Hence the subtle - differences between this and ::add_processor() - */ - ChanCount old_pms = processor_max_streams; if (!_session.engine().connected()) { @@ -940,41 +1043,33 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter { Glib::RWLock::WriterLock lm (_processor_lock); - - ChanCount potential_max_streams = ChanCount::max (_input->n_ports(), _output->n_ports()); + ProcessorState pstate (this); for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) { - // Ensure meter only appears in the list once if (*i == _meter) { - ProcessorList::iterator m = find(_processors.begin(), _processors.end(), *i); - if (m != _processors.end()) { - _processors.erase(m); - } + continue; } boost::shared_ptr pi; if ((pi = boost::dynamic_pointer_cast(*i)) != 0) { pi->set_count (1); - - ChanCount m = max (pi->input_streams(), pi->output_streams()); - - if (m > potential_max_streams) { - potential_max_streams = m; - } } - ProcessorList::iterator inserted = _processors.insert (iter, *i); + ProcessorList::iterator inserted = _processors.insert (loc, *i); if ((*i)->active()) { (*i)->activate (); } - if (configure_processors_unlocked (err)) { - _processors.erase (inserted); - configure_processors_unlocked (0); // it worked before we tried to add it ... - return -1; + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + if (configure_processors_unlocked (err)) { + pstate.restore (); + configure_processors_unlocked (0); // it worked before we tried to add it ... + return -1; + } } (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false)); @@ -984,6 +1079,7 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter } processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + set_processor_positions (); return 0; } @@ -1178,12 +1274,17 @@ Route::clear_processors (Placement p) } _processors = new_list; - configure_processors_unlocked (&err); // this can't fail + + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + configure_processors_unlocked (&err); // this can't fail + } } processor_max_streams.reset(); _have_internal_generator = false; processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + set_processor_positions (); if (!already_deleting) { _session.clear_deletion_in_progress(); @@ -1209,6 +1310,8 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream { Glib::RWLock::WriterLock lm (_processor_lock); + ProcessorState pstate (this); + ProcessorList::iterator i; bool removed = false; @@ -1251,12 +1354,15 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream return 1; } - if (configure_processors_unlocked (err)) { - /* get back to where we where */ - _processors.insert (i, processor); - /* we know this will work, because it worked before :) */ - configure_processors_unlocked (0); - return -1; + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + + if (configure_processors_unlocked (err)) { + pstate.restore (); + /* we know this will work, because it worked before :) */ + configure_processors_unlocked (0); + return -1; + } } _have_internal_generator = false; @@ -1275,6 +1381,7 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream processor->drop_references (); processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + set_processor_positions (); return 0; } @@ -1292,11 +1399,11 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* { Glib::RWLock::WriterLock lm (_processor_lock); + ProcessorState pstate (this); + ProcessorList::iterator i; boost::shared_ptr processor; - ProcessorList as_we_were = _processors; - for (i = _processors.begin(); i != _processors.end(); ) { processor = *i; @@ -1337,12 +1444,15 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* _output->set_user_latency (0); - if (configure_processors_unlocked (err)) { - /* get back to where we where */ - _processors = as_we_were; - /* we know this will work, because it worked before :) */ - configure_processors_unlocked (0); - return -1; + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + + if (configure_processors_unlocked (err)) { + pstate.restore (); + /* we know this will work, because it worked before :) */ + configure_processors_unlocked (0); + return -1; + } } _have_internal_generator = false; @@ -1366,18 +1476,22 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* } processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + set_processor_positions (); return 0; } - +/** Caller must hold process lock */ int Route::configure_processors (ProcessorStreams* err) { + assert (!AudioEngine::instance()->process_lock().trylock()); + if (!_in_configure_processors) { Glib::RWLock::WriterLock lm (_processor_lock); return configure_processors_unlocked (err); } + return 0; } @@ -1387,37 +1501,34 @@ Route::input_streams () const return _input->n_ports (); } -/** Configure the input/output configuration of each processor in the processors list. - * Return 0 on success, otherwise configuration is impossible. - */ -int -Route::configure_processors_unlocked (ProcessorStreams* err) +list > +Route::try_configure_processors (ChanCount in, ProcessorStreams* err) { - if (_in_configure_processors) { - return 0; - } + Glib::RWLock::ReaderLock lm (_processor_lock); - _in_configure_processors = true; + return try_configure_processors_unlocked (in, err); +} +list > +Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err) +{ // Check each processor in order to see if we can configure as requested - ChanCount in = input_streams (); ChanCount out; - list< pair > configuration; + list > configuration; uint32_t index = 0; DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configure processors\n", _name)); -#ifndef NDEBUG DEBUG_TRACE (DEBUG::Processors, "{\n"); - for (list >::const_iterator p = _processors.begin(); p != _processors.end(); ++p) { - DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID = %2\n", (*p)->name(), (*p)->id())); - } - DEBUG_TRACE (DEBUG::Processors, "}\n"); -#endif for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) { + if (boost::dynamic_pointer_cast (*p)) { + DEBUG_TRACE (DEBUG::Processors, "--- CONFIGURE ABORTED due to unknown processor.\n"); + break; + } + if ((*p)->can_support_io_configuration(in, out)) { - DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1in = %2 out = %3\n",(*p)->name(), in, out)); + DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID=%2 in=%3 out=%4\n",(*p)->name(), (*p)->id(), in, out)); configuration.push_back(make_pair(in, out)); in = out; } else { @@ -1425,14 +1536,52 @@ Route::configure_processors_unlocked (ProcessorStreams* err) err->index = index; err->count = in; } - _in_configure_processors = false; - return -1; + DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n"); + DEBUG_TRACE (DEBUG::Processors, string_compose ("---- %1 cannot support in=%2 out=%3\n", (*p)->name(), in, out)); + DEBUG_TRACE (DEBUG::Processors, "}\n"); + return list > (); } } - // We can, so configure everything + DEBUG_TRACE (DEBUG::Processors, "}\n"); + + return configuration; +} + +/** Set the input/output configuration of each processor in the processors list. + * Caller must hold process lock. + * Return 0 on success, otherwise configuration is impossible. + */ +int +Route::configure_processors_unlocked (ProcessorStreams* err) +{ + assert (!AudioEngine::instance()->process_lock().trylock()); + + if (_in_configure_processors) { + return 0; + } + + /* put invisible processors where they should be */ + setup_invisible_processors (); + + _in_configure_processors = true; + + list > configuration = try_configure_processors_unlocked (input_streams (), err); + + if (configuration.empty ()) { + _in_configure_processors = false; + return -1; + } + + ChanCount out; + list< pair >::iterator c = configuration.begin(); for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) { + + if (boost::dynamic_pointer_cast (*p)) { + break; + } + (*p)->configure_io(c->first, c->second); processor_max_streams = ChanCount::max(processor_max_streams, c->first); processor_max_streams = ChanCount::max(processor_max_streams, c->second); @@ -1447,6 +1596,8 @@ Route::configure_processors_unlocked (ProcessorStreams* err) configuration */ _session.ensure_buffers (n_process_buffers ()); + DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration complete\n", _name)); + _in_configure_processors = false; return 0; } @@ -1542,10 +1693,10 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err { Glib::RWLock::WriterLock lm (_processor_lock); - ChanCount old_pms = processor_max_streams; + ProcessorState pstate (this); + ProcessorList::iterator oiter; ProcessorList::const_iterator niter; - ProcessorList as_it_was_before = _processors; ProcessorList as_it_will_be; oiter = _processors.begin(); @@ -1599,16 +1750,18 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err _processors.insert (oiter, as_it_will_be.begin(), as_it_will_be.end()); - if (configure_processors_unlocked (err)) { - _processors = as_it_was_before; - processor_max_streams = old_pms; - return -1; + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + + if (configure_processors_unlocked (err)) { + pstate.restore (); + return -1; + } } } - if (true) { - processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ - } + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + set_processor_positions (); return 0; } @@ -1642,7 +1795,9 @@ Route::state(bool full_state) } node->add_property("active", _active?"yes":"no"); - node->add_property("phase-invert", _phase_invert?"yes":"no"); + string p; + boost::to_string (_phase_invert, p); + node->add_property("phase-invert", p); node->add_property("denormal-protection", _denormal_protection?"yes":"no"); node->add_property("meter-point", enum_2_string (_meter_point)); @@ -1669,12 +1824,17 @@ Route::state(bool full_state) } node->add_property ("order-keys", order_string); node->add_property ("self-solo", (_self_solo ? "yes" : "no")); - snprintf (buf, sizeof (buf), "%d", _soloed_by_others); - node->add_property ("soloed-by-others", buf); + snprintf (buf, sizeof (buf), "%d", _soloed_by_others_upstream); + node->add_property ("soloed-by-upstream", buf); + snprintf (buf, sizeof (buf), "%d", _soloed_by_others_downstream); + node->add_property ("soloed-by-downstream", buf); + node->add_property ("solo-isolated", solo_isolated() ? "yes" : "no"); + node->add_property ("solo-safe", _solo_safe ? "yes" : "no"); node->add_child_nocopy (_input->state (full_state)); node->add_child_nocopy (_output->state (full_state)); node->add_child_nocopy (_solo_control->get_state ()); + node->add_child_nocopy (_mute_control->get_state ()); node->add_child_nocopy (_mute_master->get_state ()); XMLNode* remote_control_node = new XMLNode (X_("RemoteControl")); @@ -1687,6 +1847,8 @@ Route::state(bool full_state) cmt->add_content (_comment); } + node->add_child_nocopy (_pannable->state (full_state)); + for (i = _processors.begin(); i != _processors.end(); ++i) { node->add_child_nocopy((*i)->state (full_state)); } @@ -1714,7 +1876,6 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) XMLNodeList nlist; XMLNodeConstIterator niter; XMLNode *child; - XMLPropertyList plist; const XMLProperty *prop; if (node.name() != "Route"){ @@ -1736,6 +1897,10 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) _flags = Flag (0); } + if (is_master() || is_monitor() || is_hidden()) { + _mute_master->set_solo_ignore (true); + } + /* add all processors (except amp, which is always present) */ nlist = node.children(); @@ -1760,6 +1925,11 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) if (child->name() == X_("Processor")) { processor_state.add_child_copy (*child); } + + + if (child->name() == X_("Pannable")) { + _pannable->set_state (*child, version); + } } set_processor_state (processor_state); @@ -1768,17 +1938,26 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) set_self_solo (string_is_affirmative (prop->value())); } - if ((prop = node.property ("soloed-by-others")) != 0) { - _soloed_by_others = 0; // needed for mod_solo_by_others () to work - mod_solo_by_others (atoi (prop->value())); + if ((prop = node.property ("soloed-by-upstream")) != 0) { + _soloed_by_others_upstream = 0; // needed for mod_.... () to work + mod_solo_by_others_upstream (atoi (prop->value())); + } + + if ((prop = node.property ("soloed-by-downstream")) != 0) { + _soloed_by_others_downstream = 0; // needed for mod_.... () to work + mod_solo_by_others_downstream (atoi (prop->value())); } if ((prop = node.property ("solo-isolated")) != 0) { set_solo_isolated (string_is_affirmative (prop->value()), this); } + if ((prop = node.property ("solo-safe")) != 0) { + set_solo_safe (string_is_affirmative (prop->value()), this); + } + if ((prop = node.property (X_("phase-invert"))) != 0) { - set_phase_invert (string_is_affirmative (prop->value())); + set_phase_invert (boost::dynamic_bitset<> (prop->value ())); } if ((prop = node.property (X_("denormal-protection"))) != 0) { @@ -1788,12 +1967,12 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) if ((prop = node.property (X_("active"))) != 0) { bool yn = string_is_affirmative (prop->value()); _active = !yn; // force switch - set_active (yn); + set_active (yn, this); } if ((prop = node.property (X_("meter-point"))) != 0) { MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point)); - set_meter_point (mp); + set_meter_point (mp, true); if (_meter) { _meter->set_display_to_user (_meter_point == MeterCustom); } @@ -1801,7 +1980,7 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) if ((prop = node.property (X_("order-keys"))) != 0) { - long n; + int32_t n; string::size_type colon, equal; string remaining = prop->value(); @@ -1812,7 +1991,7 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining) << endmsg; } else { - if (sscanf (remaining.substr (equal+1).c_str(), "%ld", &n) != 1) { + if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) { error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining) << endmsg; } else { @@ -1844,11 +2023,9 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) _extra_xml = new XMLNode (*child); - } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) { - + } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) { if (prop->value() == "solo") { _solo_control->set_state (*child, version); - _session.add_controllable (_solo_control); } } else if (child->name() == X_("RemoteControl")) { @@ -1872,7 +2049,6 @@ Route::_set_state_2X (const XMLNode& node, int version) XMLNodeList nlist; XMLNodeConstIterator niter; XMLNode *child; - XMLPropertyList plist; const XMLProperty *prop; /* 2X things which still remain to be handled: @@ -1893,7 +2069,11 @@ Route::_set_state_2X (const XMLNode& node, int version) } if ((prop = node.property (X_("phase-invert"))) != 0) { - set_phase_invert (string_is_affirmative (prop->value())); + boost::dynamic_bitset<> p (_input->n_ports().n_audio ()); + if (string_is_affirmative (prop->value ())) { + p.set (); + } + set_phase_invert (p); } if ((prop = node.property (X_("denormal-protection"))) != 0) { @@ -1913,7 +2093,7 @@ Route::_set_state_2X (const XMLNode& node, int version) bool first = true; bool muted = string_is_affirmative (prop->value()); - if(muted){ + if (muted){ string mute_point; @@ -1963,7 +2143,7 @@ Route::_set_state_2X (const XMLNode& node, int version) } } - _mute_master->set_state (mute_point); + _mute_master->set_mute_points (mute_point); } } @@ -1977,7 +2157,7 @@ Route::_set_state_2X (const XMLNode& node, int version) if ((prop = node.property (X_("order-keys"))) != 0) { - long n; + int32_t n; string::size_type colon, equal; string remaining = prop->value(); @@ -1988,7 +2168,7 @@ Route::_set_state_2X (const XMLNode& node, int version) error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining) << endmsg; } else { - if (sscanf (remaining.substr (equal+1).c_str(), "%ld", &n) != 1) { + if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) { error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining) << endmsg; } else { @@ -2006,23 +2186,6 @@ Route::_set_state_2X (const XMLNode& node, int version) } } - /* add standard processors */ - - //_meter.reset (new PeakMeter (_session)); - //add_processor (_meter, PreFader); - - if (is_monitor()) { - /* where we listen to tracks */ - _intreturn.reset (new InternalReturn (_session)); - add_processor (_intreturn, PreFader); - - _monitor_control.reset (new MonitorProcessor (_session)); - add_processor (_monitor_control, PostFader); - } - - _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main)); - add_processor (_main_outs, PostFader); - /* IOs */ nlist = node.children (); @@ -2050,7 +2213,7 @@ Route::_set_state_2X (const XMLNode& node, int version) if ((prop = child->property (X_("active"))) != 0) { bool yn = string_is_affirmative (prop->value()); _active = !yn; // force switch - set_active (yn); + set_active (yn, this); } if ((prop = child->property (X_("gain"))) != 0) { @@ -2072,7 +2235,10 @@ Route::_set_state_2X (const XMLNode& node, int version) io_child = *io_niter; if (io_child->name() == X_("Panner")) { - _main_outs->panner()->set_state(*io_child, version); + _main_outs->panner_shell()->set_state(*io_child, version); + } else if (io_child->name() == X_("Automation")) { + /* IO's automation is for the fader */ + _amp->set_automation_xml_state (*io_child, Evoral::Parameter (GainAutomation)); } } } @@ -2102,17 +2268,17 @@ Route::_set_state_2X (const XMLNode& node, int version) XMLNode *cmt = *(child->children().begin()); _comment = cmt->content(); - } else if (child->name() == X_("Extra")) { + } else if (child->name() == X_("extra")) { _extra_xml = new XMLNode (*child); - } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) { - - if (prop->value() == "solo") { + } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) { + if (prop->value() == X_("solo")) { _solo_control->set_state (*child, version); - _session.add_controllable (_solo_control); - } - + } else if (prop->value() == X_("mute")) { + _mute_control->set_state (*child, version); + } + } else if (child->name() == X_("RemoteControl")) { if ((prop = child->property (X_("id"))) != 0) { int32_t x; @@ -2147,7 +2313,7 @@ Route::set_processor_state_2X (XMLNodeList const & nList, int version) */ for (XMLNodeConstIterator i = nList.begin(); i != nList.end(); ++i) { - add_processor_from_xml_2X (**i, version, _processors.begin ()); + add_processor_from_xml_2X (**i, version); } } @@ -2168,24 +2334,22 @@ Route::set_processor_state (const XMLNode& node) new_order.push_back (_amp); } else if (prop->value() == "meter") { _meter->set_state (**niter, Stateful::current_state_version); - new_order.push_back (_meter); } else if (prop->value() == "main-outs") { _main_outs->set_state (**niter, Stateful::current_state_version); - new_order.push_back (_main_outs); } else if (prop->value() == "intreturn") { if (!_intreturn) { _intreturn.reset (new InternalReturn (_session)); must_configure = true; } _intreturn->set_state (**niter, Stateful::current_state_version); - new_order.push_back (_intreturn); } else if (is_monitor() && prop->value() == "monitor") { if (!_monitor_control) { _monitor_control.reset (new MonitorProcessor (_session)); must_configure = true; } _monitor_control->set_state (**niter, Stateful::current_state_version); - new_order.push_back (_monitor_control); + } else if (prop->value() == "capture") { + _capturing_processor.reset (new CapturingProcessor (_session)); } else { ProcessorList::iterator o; @@ -2206,8 +2370,7 @@ Route::set_processor_state (const XMLNode& node) if (prop->value() == "intsend") { - processor.reset (new InternalSend (_session, _mute_master, boost::shared_ptr(), Delivery::Role (0))); - + processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr(), Delivery::Role (0))); } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || prop->value() == "vst" || @@ -2217,33 +2380,51 @@ Route::set_processor_state (const XMLNode& node) } else if (prop->value() == "port") { - processor.reset (new PortInsert (_session, _mute_master)); + processor.reset (new PortInsert (_session, _pannable, _mute_master)); } else if (prop->value() == "send") { - processor.reset (new Send (_session, _mute_master)); + processor.reset (new Send (_session, _pannable, _mute_master)); } else { error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg; continue; } - processor->set_state (**niter, Stateful::current_state_version); - new_order.push_back (processor); - must_configure = true; - } - } - } + if (processor->set_state (**niter, Stateful::current_state_version) != 0) { + /* This processor could not be configured. Turn it into a UnknownProcessor */ + processor.reset (new UnknownProcessor (_session, **niter)); + } + + /* we have to note the monitor send here, otherwise a new one will be created + and the state of this one will be lost. + */ + boost::shared_ptr isend = boost::dynamic_pointer_cast (processor); + if (isend && isend->role() == Delivery::Listen) { + _monitor_send = isend; + } + + /* it doesn't matter if invisible processors are added here, as they + will be sorted out by setup_invisible_processors () shortly. + */ - { + new_order.push_back (processor); + must_configure = true; + } + } + } + + { Glib::RWLock::WriterLock lm (_processor_lock); _processors = new_order; if (must_configure) { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); configure_processors_unlocked (0); } } processors_changed (RouteProcessorChange ()); + set_processor_positions (); } void @@ -2254,33 +2435,39 @@ Route::curve_reallocate () } void -Route::silence (nframes_t nframes) +Route::silence (framecnt_t nframes) { - if (!_silent) { - - _output->silence (nframes); - - { - Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); + Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); + if (!lm.locked()) { + return; + } - if (lm.locked()) { - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - boost::shared_ptr pi; + silence_unlocked (nframes); +} - if (!_active && (pi = boost::dynamic_pointer_cast (*i)) != 0) { - // skip plugins, they don't need anything when we're not active - continue; - } +void +Route::silence_unlocked (framecnt_t nframes) +{ + /* Must be called with the processor lock held */ + + if (!_silent) { - (*i)->silence (nframes); - } + _output->silence (nframes); - if (nframes == _session.get_block_size()) { - // _silent = true; - } + 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 + continue; } + + (*i)->silence (nframes); + } + + if (nframes == _session.get_block_size()) { + // _silent = true; } - } } @@ -2293,8 +2480,8 @@ Route::add_internal_return () } } -BufferSet* -Route::get_return_buffer () const +void +Route::add_send_to_internal_return (InternalSend* send) { Glib::RWLock::ReaderLock rm (_processor_lock); @@ -2302,16 +2489,13 @@ Route::get_return_buffer () const boost::shared_ptr d = boost::dynamic_pointer_cast(*x); if (d) { - BufferSet* bs = d->get_buffers (); - return bs; + return d->add_send (send); } } - - return 0; } void -Route::release_return_buffer () const +Route::remove_send_from_internal_return (InternalSend* send) { Glib::RWLock::ReaderLock rm (_processor_lock); @@ -2319,81 +2503,62 @@ Route::release_return_buffer () const boost::shared_ptr d = boost::dynamic_pointer_cast(*x); if (d) { - return d->release_buffers (); + return d->remove_send (send); } } } +/** Add a monitor send (if we don't already have one) but don't activate it */ int -Route::listen_via (boost::shared_ptr route, Placement placement, bool /*active*/, bool aux) +Route::listen_via_monitor () { - vector ports; - vector::const_iterator i; + /* master never sends to control outs */ + assert (!is_master ()); + + /* make sure we have one */ + if (!_monitor_send) { + _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, _session.monitor_out(), Delivery::Listen)); + _monitor_send->set_display_to_user (false); + } + + /* set it up */ + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + configure_processors (0); + + return 0; +} +/** Add an internal send to a route. + * @param route route to send to. + * @param placement placement for the send. + */ +int +Route::listen_via (boost::shared_ptr route, Placement placement) +{ + assert (route != _session.monitor_out ()); + { Glib::RWLock::ReaderLock rm (_processor_lock); for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) { - boost::shared_ptr d = boost::dynamic_pointer_cast(*x); + boost::shared_ptr d = boost::dynamic_pointer_cast (*x); if (d && d->target_route() == route) { - - /* if the target is the control outs, then make sure - we take note of which i-send is doing that. - */ - - if (route == _session.monitor_out()) { - _monitor_send = boost::dynamic_pointer_cast(d); - if (_monitor_send->active()) { - _monitor_send->set_solo_level (1); - } else { - _monitor_send->set_solo_level (0); - } - } - /* already listening via the specified IO: do nothing */ - return 0; } } } - boost::shared_ptr listener; - try { - - if (is_master()) { - - if (route == _session.monitor_out()) { - /* master never sends to control outs */ - return 0; - } else { - listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen))); - } - - } else { - listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen))); - if (route == _session.monitor_out()) { - } - } + boost::shared_ptr listener (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux)); + add_processor (listener, placement); } catch (failed_constructor& err) { return -1; } - if (route == _session.monitor_out()) { - _monitor_send = listener; - } - - if (placement == PostFader) { - /* put it *really* at the end, not just after the panner (main outs) - */ - add_processor (listener, _processors.end()); - } else { - add_processor (listener, PreFader); - } - return 0; } @@ -2440,7 +2605,53 @@ Route::set_comment (string cmt, void *src) } bool -Route::feeds (boost::shared_ptr other, bool* only_send) +Route::add_fed_by (boost::shared_ptr other, bool via_sends_only) +{ + FeedRecord fr (other, via_sends_only); + + pair result = _fed_by.insert (fr); + + if (!result.second) { + + /* already a record for "other" - make sure sends-only information is correct */ + if (!via_sends_only && result.first->sends_only) { + FeedRecord* frp = const_cast(&(*result.first)); + frp->sends_only = false; + } + } + + return result.second; +} + +void +Route::clear_fed_by () +{ + _fed_by.clear (); +} + +bool +Route::feeds (boost::shared_ptr other, bool* via_sends_only) +{ + const FedBy& fed_by (other->fed_by()); + + for (FedBy::iterator f = fed_by.begin(); f != fed_by.end(); ++f) { + boost::shared_ptr sr = f->r.lock(); + + if (sr && (sr.get() == this)) { + + if (via_sends_only) { + *via_sends_only = f->sends_only; + } + + return true; + } + } + + return false; +} + +bool +Route::direct_feeds (boost::shared_ptr other, bool* only_send) { DEBUG_TRACE (DEBUG::Graph, string_compose ("Feeds? %1\n", _name)); @@ -2478,10 +2689,11 @@ Route::feeds (boost::shared_ptr other, bool* only_send) return false; } +/** Called from the (non-realtime) butler thread when the transport is stopped */ void -Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors) +Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors) { - nframes_t now = _session.transport_frame(); + framepos_t now = _session.transport_frame(); { Glib::RWLock::ReaderLock lm (_processor_lock); @@ -2490,11 +2702,12 @@ Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool c automation_snapshot (now, true); } + Automatable::transport_stopped (now); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (Config->get_plugins_stop_with_transport() && can_flush_processors) { - (*i)->deactivate (); - (*i)->activate (); + (*i)->flush (); } (*i)->transport_stopped (now); @@ -2504,21 +2717,52 @@ Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool c _roll_delay = _initial_delay; } +/** Called with the process lock held if change contains ConfigurationChanged */ void Route::input_change_handler (IOChange change, void * /*src*/) { - if ((change & ConfigurationChanged)) { + if ((change.type & IOChange::ConfigurationChanged)) { configure_processors (0); + _phase_invert.resize (_input->n_ports().n_audio ()); + io_changed (); /* EMIT SIGNAL */ } } +/** Called with the process lock held if change contains ConfigurationChanged */ void Route::output_change_handler (IOChange change, void * /*src*/) { - if ((change & ConfigurationChanged)) { + if ((change.type & IOChange::ConfigurationChanged)) { /* XXX resize all listeners to match _main_outs? */ + /* Auto-connect newly-created outputs, unless we're auto-connecting to master + and we are master (as an auto-connect in this situation would cause a + feedback loop) + */ + AutoConnectOption ac = Config->get_output_auto_connect (); + if (ac == AutoConnectPhysical || (ac == AutoConnectMaster && !is_master ())) { + + ChanCount start = change.before; + + for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) { + if (change.before.get(*i) < change.after.get(*i)) { + /* the existing ChanCounts don't matter for this call as they are only + to do with matching input and output indices, and we are only changing + outputs here. + */ + ChanCount dummy; + + /* only auto-connect the newly-created outputs, not the ones that were + already there + */ + start.set (*i, start.get (*i) + 1); + + _session.auto_connect_route (this, dummy, dummy, false, ChanCount(), change.before); + } + } + } + // configure_processors (0); } } @@ -2534,15 +2778,20 @@ Route::pans_required () const } int -Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, +Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool session_state_changing, bool /*can_record*/, bool /*rec_monitors_input*/) { + Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); + if (!lm.locked()) { + return 0; + } + if (n_outputs().n_total() == 0) { return 0; } if (!_active || n_inputs() == ChanCount::ZERO) { - silence (nframes); + silence_unlocked (nframes); return 0; } if (session_state_changing) { @@ -2552,7 +2801,7 @@ Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, XXX note the absurdity of ::no_roll() being called when we ARE rolling! */ - silence (nframes); + silence_unlocked (nframes); return 0; } /* we're really not rolling, so we're either delivery silence or actually @@ -2566,20 +2815,20 @@ Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, return 0; } -nframes_t -Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame) +framecnt_t +Route::check_initial_delay (framecnt_t nframes, framecnt_t& transport_frame) { if (_roll_delay > nframes) { _roll_delay -= nframes; - silence (nframes); + silence_unlocked (nframes); /* transport frame is not legal for caller to use */ return 0; } else if (_roll_delay > 0) { nframes -= _roll_delay; - silence (_roll_delay); + silence_unlocked (_roll_delay); /* we've written _roll_delay of samples into the output ports, so make a note of that for future reference. @@ -2594,29 +2843,26 @@ Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame) } int -Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int declick, +Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */) { - { - // automation snapshot can also be called from the non-rt context - // and it uses the processor list, so we try to acquire the lock here - Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); - - if (lm.locked()) { - automation_snapshot (_session.transport_frame(), false); - } + Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); + if (!lm.locked()) { + return 0; } + + automation_snapshot (_session.transport_frame(), false); if (n_outputs().n_total() == 0) { return 0; } if (!_active || n_inputs().n_total() == 0) { - silence (nframes); + silence_unlocked (nframes); return 0; } - nframes_t unused = 0; + framecnt_t unused = 0; if ((nframes = check_initial_delay (nframes, unused)) == 0) { return 0; @@ -2630,7 +2876,7 @@ Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int } int -Route::silent_roll (nframes_t nframes, sframes_t /*start_frame*/, sframes_t /*end_frame*/, +Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/, bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */) { silence (nframes); @@ -2683,43 +2929,34 @@ Route::flush_processors () Glib::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - (*i)->deactivate (); - (*i)->activate (); + (*i)->flush (); } } void -Route::set_meter_point (MeterPoint p) +Route::set_meter_point (MeterPoint p, bool force) { /* CAN BE CALLED FROM PROCESS CONTEXT */ - if (_meter_point == p) { + if (_meter_point == p && !force) { return; } + _meter_point = p; + bool meter_was_visible_to_user = _meter->display_to_user (); { Glib::RWLock::WriterLock lm (_processor_lock); - if (p != MeterCustom) { - // Move meter in the processors list to reflect the new position - ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter); - _processors.erase(loc); - switch (p) { - case MeterInput: - loc = _processors.begin(); - break; - case MeterPreFader: - loc = find (_processors.begin(), _processors.end(), _amp); - break; - case MeterPostFader: - loc = _processors.end(); - break; - default: - break; - } + if (_meter_point != MeterCustom) { + + _meter->set_display_to_user (false); + + setup_invisible_processors (); + ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter); + ChanCount m_in; if (loc == _processors.begin()) { @@ -2732,16 +2969,12 @@ Route::set_meter_point (MeterPoint p) _meter->reflect_inputs (m_in); - _processors.insert (loc, _meter); - /* we do not need to reconfigure the processors, because the meter (a) is always ready to handle processor_max_streams (b) is always an N-in/N-out processor, and thus moving it doesn't require any changes to the other processors. */ - _meter->set_display_to_user (false); - } else { // just make it visible and let the user move it @@ -2750,7 +2983,6 @@ Route::set_meter_point (MeterPoint p) } } - _meter_point = p; meter_change (); /* EMIT SIGNAL */ bool const meter_visibly_changed = (_meter->display_to_user() != meter_was_visible_to_user); @@ -2759,36 +2991,20 @@ Route::set_meter_point (MeterPoint p) } void -Route::put_monitor_send_at (Placement p) +Route::listen_position_changed () { - if (!_monitor_send) { - return; - } - { Glib::RWLock::WriterLock lm (_processor_lock); - ProcessorList as_it_was (_processors); - ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _monitor_send); - _processors.erase(loc); - - switch (p) { - case PreFader: - loc = find(_processors.begin(), _processors.end(), _amp); - if (loc != _processors.begin()) { - --loc; - } - break; - case PostFader: - loc = _processors.end(); - break; - } - - _processors.insert (loc, _monitor_send); + ProcessorState pstate (this); - if (configure_processors_unlocked (0)) { - _processors = as_it_was; - configure_processors_unlocked (0); // it worked before we tried to add it ... - return; + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + + if (configure_processors_unlocked (0)) { + pstate.restore (); + configure_processors_unlocked (0); // it worked before we tried to add it ... + return; + } } } @@ -2796,11 +3012,29 @@ Route::put_monitor_send_at (Placement p) _session.set_dirty (); } -nframes_t +boost::shared_ptr +Route::add_export_point() +{ + if (!_capturing_processor) { + + _capturing_processor.reset (new CapturingProcessor (_session)); + _capturing_processor->activate (); + + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + configure_processors (0); + } + + } + + return _capturing_processor; +} + +framecnt_t Route::update_total_latency () { - nframes_t old = _output->effective_latency(); - nframes_t own_latency = _output->user_latency(); + framecnt_t old = _output->effective_latency(); + framecnt_t own_latency = _output->user_latency(); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if ((*i)->active ()) { @@ -2835,16 +3069,16 @@ Route::update_total_latency () } void -Route::set_user_latency (nframes_t nframes) +Route::set_user_latency (framecnt_t nframes) { _output->set_user_latency (nframes); _session.update_latency_compensation (false, false); } void -Route::set_latency_delay (nframes_t longest_session_latency) +Route::set_latency_delay (framecnt_t longest_session_latency) { - nframes_t old = _initial_delay; + framecnt_t old = _initial_delay; if (_output->effective_latency() < longest_session_latency) { _initial_delay = longest_session_latency - _output->effective_latency(); @@ -2862,86 +3096,97 @@ Route::set_latency_delay (nframes_t longest_session_latency) } void -Route::automation_snapshot (nframes_t now, bool force) +Route::automation_snapshot (framepos_t now, bool force) { - panner()->automation_snapshot (now, force); - + _pannable->automation_snapshot (now, force); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->automation_snapshot (now, force); } } -Route::SoloControllable::SoloControllable (std::string name, Route& r) - : AutomationControl (r.session(), Evoral::Parameter (SoloAutomation), +Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr r) + : AutomationControl (r->session(), Evoral::Parameter (SoloAutomation), boost::shared_ptr(), name) - , route (r) + , _route (r) { boost::shared_ptr gl(new AutomationList(Evoral::Parameter(SoloAutomation))); set_list (gl); } void -Route::SoloControllable::set_value (float val) +Route::SoloControllable::set_value (double val) { bool bval = ((val >= 0.5f) ? true: false); -# if 0 - this is how it should be done boost::shared_ptr rl (new RouteList); - rl->push_back (route); + + boost::shared_ptr r = _route.lock (); + if (!r) { + return; + } + + rl->push_back (r); if (Config->get_solo_control_is_listen_control()) { _session.set_listen (rl, bval); } else { _session.set_solo (rl, bval); } -#else - route.set_solo (bval, this); -#endif } -float -Route::SoloControllable::get_value (void) const +double +Route::SoloControllable::get_value () const { + boost::shared_ptr r = _route.lock (); + if (!r) { + return 0; + } + if (Config->get_solo_control_is_listen_control()) { - return route.listening() ? 1.0f : 0.0f; + return r->listening_via_monitor() ? 1.0f : 0.0f; } else { - return route.self_soloed() ? 1.0f : 0.0f; + return r->self_soloed() ? 1.0f : 0.0f; } } -Route::MuteControllable::MuteControllable (std::string name, Route& r) - : AutomationControl (r.session(), Evoral::Parameter (MuteAutomation), +Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr r) + : AutomationControl (r->session(), Evoral::Parameter (MuteAutomation), boost::shared_ptr(), name) - , route (r) + , _route (r) { boost::shared_ptr gl(new AutomationList(Evoral::Parameter(MuteAutomation))); set_list (gl); } void -Route::MuteControllable::set_value (float val) +Route::MuteControllable::set_value (double val) { bool bval = ((val >= 0.5f) ? true: false); -# if 0 - this is how it should be done boost::shared_ptr rl (new RouteList); - rl->push_back (route); + + boost::shared_ptr r = _route.lock (); + if (!r) { + return; + } + + rl->push_back (r); _session.set_mute (rl, bval); -#else - route.set_mute (bval, this); -#endif } -float -Route::MuteControllable::get_value (void) const +double +Route::MuteControllable::get_value () const { - return route.muted() ? 1.0f : 0.0f; + boost::shared_ptr r = _route.lock (); + if (!r) { + return 0; + } + + return r->muted() ? 1.0f : 0.0f; } void -Route::set_block_size (nframes_t nframes) +Route::set_block_size (pframes_t nframes) { for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->set_block_size (nframes); @@ -2980,44 +3225,53 @@ Route::set_pending_declick (int declick) */ void -Route::shift (nframes64_t /*pos*/, nframes64_t /*frames*/) +Route::shift (framepos_t pos, framecnt_t frames) { -#ifdef THIS_NEEDS_FIXING_FOR_V3 - /* gain automation */ - XMLNode &before = _gain_control->get_state (); - _gain_control->shift (pos, frames); - XMLNode &after = _gain_control->get_state (); - _session.add_command (new MementoCommand (_gain_automation_curve, &before, &after)); + { + boost::shared_ptr gc = _amp->gain_control(); + + XMLNode &before = gc->alist()->get_state (); + gc->alist()->shift (pos, frames); + XMLNode &after = gc->alist()->get_state (); + _session.add_command (new MementoCommand (*gc->alist().get(), &before, &after)); + } /* pan automation */ - for (std::vector::iterator i = _panner->begin (); i != _panner->end (); ++i) { - Curve & c = (*i)->automation (); - XMLNode &before = c.get_state (); - c.shift (pos, frames); - XMLNode &after = c.get_state (); - _session.add_command (new MementoCommand (c, &before, &after)); - } + { + ControlSet::Controls& c (_pannable->controls()); + + for (ControlSet::Controls::const_iterator ci = c.begin(); ci != c.end(); ++ci) { + boost::shared_ptr pc = boost::dynamic_pointer_cast (ci->second); + if (pc) { + boost::shared_ptr al = pc->alist(); + XMLNode& before = al->get_state (); + al->shift (pos, frames); + XMLNode& after = al->get_state (); + _session.add_command (new MementoCommand (*al.get(), &before, &after)); + } + } + } /* redirect automation */ { - Glib::RWLock::ReaderLock lm (redirect_lock); - for (RedirectList::iterator i = _redirects.begin (); i != _redirects.end (); ++i) { - - set a; - (*i)->what_has_automation (a); - - for (set::const_iterator j = a.begin (); j != a.end (); ++j) { - AutomationList & al = (*i)->automation_list (*j); - XMLNode &before = al.get_state (); - al.shift (pos, frames); - XMLNode &after = al.get_state (); - _session.add_command (new MementoCommand (al, &before, &after)); - } - } + Glib::RWLock::ReaderLock lm (_processor_lock); + for (ProcessorList::iterator i = _processors.begin (); i != _processors.end (); ++i) { + + set parameters = (*i)->what_can_be_automated(); + + for (set::const_iterator p = parameters.begin (); p != parameters.end (); ++p) { + boost::shared_ptr ac = (*i)->automation_control (*p); + if (ac) { + boost::shared_ptr al = ac->alist(); + XMLNode &before = al->get_state (); + al->shift (pos, frames); + XMLNode &after = al->get_state (); + _session.add_command (new MementoCommand (*al.get(), &before, &after)); + } + } + } } -#endif - } @@ -3086,25 +3340,39 @@ Route::internal_send_for (boost::shared_ptr target) const return boost::shared_ptr(); } +/** @param c Audio channel index. + * @param yn true to invert phase, otherwise false. + */ void -Route::set_phase_invert (bool yn) +Route::set_phase_invert (uint32_t c, bool yn) { - if (_phase_invert != yn) { - if (yn) { - _phase_invert = 0xffff; // XXX all channels - } else { - _phase_invert = 0; // XXX no channels - } + if (_phase_invert[c] != yn) { + _phase_invert[c] = yn; + phase_invert_changed (); /* EMIT SIGNAL */ + _session.set_dirty (); + } +} +void +Route::set_phase_invert (boost::dynamic_bitset<> p) +{ + if (_phase_invert != p) { + _phase_invert = p; phase_invert_changed (); /* EMIT SIGNAL */ _session.set_dirty (); } } bool +Route::phase_invert (uint32_t c) const +{ + return _phase_invert[c]; +} + +boost::dynamic_bitset<> Route::phase_invert () const { - return _phase_invert != 0; + return _phase_invert; } void @@ -3123,8 +3391,13 @@ Route::denormal_protection () const } void -Route::set_active (bool yn) +Route::set_active (bool yn, void* src) { + if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_route_active()) { + _route_group->foreach_route (boost::bind (&Route::set_active, _1, yn, _route_group)); + return; + } + if (_active != yn) { _active = yn; _input->set_active (yn); @@ -3155,10 +3428,23 @@ Route::meter () } } +boost::shared_ptr +Route::pannable() const +{ + return _pannable; +} + boost::shared_ptr Route::panner() const { - return _main_outs->panner(); + /* may be null ! */ + return _main_outs->panner_shell()->panner(); +} + +boost::shared_ptr +Route::panner_shell() const +{ + return _main_outs->panner_shell(); } boost::shared_ptr @@ -3230,3 +3516,297 @@ Route::nth_send (uint32_t n) return boost::shared_ptr (); } + +bool +Route::has_io_processor_named (const string& name) +{ + Glib::RWLock::ReaderLock lm (_processor_lock); + ProcessorList::iterator i; + + for (i = _processors.begin(); i != _processors.end(); ++i) { + if (boost::dynamic_pointer_cast (*i) || + boost::dynamic_pointer_cast (*i)) { + if ((*i)->name() == name) { + return true; + } + } + } + + return false; +} + +MuteMaster::MutePoint +Route::mute_points () const +{ + return _mute_master->mute_points (); +} + +void +Route::set_processor_positions () +{ + Glib::RWLock::ReaderLock lm (_processor_lock); + + bool had_amp = false; + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + (*i)->set_pre_fader (!had_amp); + if (boost::dynamic_pointer_cast (*i)) { + had_amp = true; + } + } +} + +/** Called when there is a proposed change to the input port count */ +bool +Route::input_port_count_changing (ChanCount to) +{ + list > c = try_configure_processors (to, 0); + if (c.empty()) { + /* The processors cannot be configured with the new input arrangement, so + block the change. + */ + return true; + } + + /* The change is ok */ + return false; +} + +list +Route::unknown_processors () const +{ + list p; + + Glib::RWLock::ReaderLock lm (_processor_lock); + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + if (boost::dynamic_pointer_cast (*i)) { + p.push_back ((*i)->name ()); + } + } + + return p; +} + +void +Route::set_latency_ranges (bool playback) const +{ + framecnt_t own_latency = 0; + + /* Processor list not protected by lock: MUST BE CALLED FROM PROCESS THREAD OR + LATENCY CALLBACK + */ + + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + if ((*i)->active ()) { + own_latency += (*i)->signal_latency (); + } + } + + if (playback) { + update_port_latencies (_input->ports (), _output->ports (), true, own_latency); + } else { + update_port_latencies (_output->ports (), _input->ports (), false, own_latency); + } +} + +void +Route::update_port_latencies (const PortSet& operands, const PortSet& feeders, bool playback, framecnt_t our_latency) const +{ +#ifdef HAVE_JACK_NEW_LATENCY + + /* we assume that all our input ports feed all our output ports. its not + universally true, but the alternative is way too corner-case to worry about. + */ + + jack_latency_range_t all_connections; + + all_connections.min = ~((jack_nframes_t) 0); + all_connections.max = 0; + + /* iterate over all feeder ports and determine their relevant latency, taking + the maximum and minimum across all of them. + */ + + for (PortSet::const_iterator p = feeders.begin(); p != feeders.end(); ++p) { + + jack_latency_range_t range; + + p->get_connected_latency_range (range, playback); + + all_connections.min = min (all_connections.min, range.min); + all_connections.max = max (all_connections.max, range.max); + } + + all_connections.min += our_latency; + all_connections.max += our_latency; + + for (PortSet::const_iterator p = operands.begin(); p != operands.end(); ++p) { + + p->set_latency_range (all_connections, playback); + + DEBUG_TRACE (DEBUG::Latency, string_compose ("Port %1 %5 latency range %2 .. %3 (including route latency of %4)\n", + p->name(), + all_connections.min, + all_connections.max, + our_latency, + (playback ? "PLAYBACK" : "CAPTURE"))); + } +#endif +} + + +/** Put the invisible processors in the right place in _processors. + * Must be called with a writer lock on _processor_lock held. + */ +void +Route::setup_invisible_processors () +{ +#ifndef NDEBUG + Glib::RWLock::WriterLock lm (_processor_lock, Glib::TRY_LOCK); + assert (!lm.locked ()); +#endif + + if (!_main_outs) { + /* too early to be doing this stuff */ + return; + } + + /* we'll build this new list here and then use it */ + + ProcessorList new_processors; + + /* find visible processors */ + + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + if ((*i)->display_to_user ()) { + new_processors.push_back (*i); + } + } + + /* find the amp */ + + ProcessorList::iterator amp = new_processors.begin (); + while (amp != new_processors.end() && boost::dynamic_pointer_cast (*amp) == 0) { + ++amp; + } + + assert (amp != _processors.end ()); + + /* and the processor after the amp */ + + ProcessorList::iterator after_amp = amp; + ++after_amp; + + /* METER */ + + if (_meter) { + switch (_meter_point) { + case MeterInput: + assert (!_meter->display_to_user ()); + new_processors.push_front (_meter); + break; + case MeterPreFader: + assert (!_meter->display_to_user ()); + new_processors.insert (amp, _meter); + break; + case MeterPostFader: + /* do nothing here */ + break; + case MeterOutput: + /* do nothing here */ + break; + case MeterCustom: + /* the meter is visible, so we don't touch it here */ + break; + } + } + + /* MAIN OUTS */ + + assert (_main_outs); + assert (!_main_outs->display_to_user ()); + new_processors.push_back (_main_outs); + + /* iterator for the main outs */ + + ProcessorList::iterator main = new_processors.end(); + --main; + + /* OUTPUT METERING */ + + if (_meter && (_meter_point == MeterOutput || _meter_point == MeterPostFader)) { + assert (!_meter->display_to_user ()); + + /* add the processor just before or just after the main outs */ + + ProcessorList::iterator meter_point = main; + + if (_meter_point == MeterOutput) { + ++meter_point; + } + new_processors.insert (meter_point, _meter); + } + + /* MONITOR SEND */ + + if (_monitor_send && !is_monitor ()) { + assert (!_monitor_send->display_to_user ()); + if (Config->get_solo_control_is_listen_control()) { + switch (Config->get_listen_position ()) { + case PreFaderListen: + switch (Config->get_pfl_position ()) { + case PFLFromBeforeProcessors: + new_processors.push_front (_monitor_send); + break; + case PFLFromAfterProcessors: + new_processors.insert (amp, _monitor_send); + break; + } + break; + case AfterFaderListen: + switch (Config->get_afl_position ()) { + case AFLFromBeforeProcessors: + new_processors.insert (after_amp, _monitor_send); + break; + case AFLFromAfterProcessors: + new_processors.insert (new_processors.end(), _monitor_send); + break; + } + break; + } + } else { + new_processors.insert (new_processors.end(), _monitor_send); + } + } + + /* MONITOR CONTROL */ + + if (_monitor_control && is_monitor ()) { + assert (!_monitor_control->display_to_user ()); + new_processors.push_front (_monitor_control); + } + + /* INTERNAL RETURN */ + + /* doing this here means that any monitor control will come just after + the return. + */ + + if (_intreturn) { + assert (!_intreturn->display_to_user ()); + new_processors.push_front (_intreturn); + } + + /* EXPORT PROCESSOR */ + + if (_capturing_processor) { + assert (!_capturing_processor->display_to_user ()); + new_processors.push_front (_capturing_processor); + } + + _processors = new_processors; + + DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: setup_invisible_processors\n", _name)); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1\n", (*i)->name ())); + } +}