X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=290d9209d93c1bdda31e46f055396f69c9958c5d;hb=7b6b75f38ff6b34de3f70e36045498f227c1727d;hp=ac51bcff97a18c56eba9429c2873b22d7e8050cd;hpb=d0cc6edadfd793b5207a63226402eedc15f101b4;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index ac51bcff97..290d9209d9 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -22,11 +22,11 @@ #include #include -#include #include "pbd/xml++.h" #include "pbd/enumwriter.h" -#include "pbd/stacktrace.h" #include "pbd/memento_command.h" +#include "pbd/stacktrace.h" +#include "pbd/convert.h" #include "evoral/Curve.hpp" @@ -37,6 +37,7 @@ #include "ardour/buffer_set.h" #include "ardour/configuration.h" #include "ardour/cycle_timer.h" +#include "ardour/debug.h" #include "ardour/delivery.h" #include "ardour/dB.h" #include "ardour/internal_send.h" @@ -44,6 +45,7 @@ #include "ardour/ladspa_plugin.h" #include "ardour/meter.h" #include "ardour/mix.h" +#include "ardour/monitor_processor.h" #include "ardour/panner.h" #include "ardour/plugin_insert.h" #include "ardour/port.h" @@ -56,6 +58,7 @@ #include "ardour/session.h" #include "ardour/timestamps.h" #include "ardour/utils.h" +#include "ardour/graph.h" #include "i18n.h" @@ -64,112 +67,142 @@ using namespace ARDOUR; using namespace PBD; uint32_t Route::order_key_cnt = 0; -sigc::signal Route::SyncOrderKeys; +PBD::Signal1 Route::SyncOrderKeys; +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) + , _self_solo (false) + , _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)) + , _have_internal_generator (false) + , _solo_safe (false) , _default_type (default_type) + , _remote_control_id (0) + , _in_configure_processors (false) +{ + processor_max_streams.reset(); + order_keys[N_("signal")] = order_key_cnt++; +} +int +Route::init () { - init (); + /* add standard controls */ - /* add standard processors other than amp (added by ::init()) */ + _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle)); + _mute_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle)); - _meter.reset (new PeakMeter (_session)); - add_processor (_meter, PreFader); + add_control (_solo_control); + add_control (_mute_control); - if (_flags & ControlOut) { - /* where we listen to tracks */ - _intreturn.reset (new InternalReturn (_session)); - add_processor (_intreturn, PreFader); - } + /* input and output objects */ - _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main)); - add_processor (_main_outs, PostFader); + _input.reset (new IO (_session, _name, IO::Input, _default_type)); + _output.reset (new IO (_session, _name, IO::Output, _default_type)); - /* now that we have _meter, its safe to connect to this */ + _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)); - _meter_connection = Metering::connect (mem_fun (*this, &Route::meter)); -} + /* add amp processor */ -Route::Route (Session& sess, const XMLNode& node, DataType default_type) - : SessionObject (sess, "toBeReset") - , AutomatableControls (sess) - , _solo_control (new SoloControllable (X_("solo"), *this)) - , _mute_master (new MuteMaster (sess, "toBeReset")) - , _default_type (default_type) -{ - init (); + _amp.reset (new Amp (_session)); + add_processor (_amp, PostFader); - _set_state (node, Stateful::loading_state_version, false); + /* add standard processors: meter, main outs, monitor out */ - /* now that we have _meter, its safe to connect to this */ + _meter.reset (new PeakMeter (_session)); + _meter->set_display_to_user (false); - _meter_connection = Metering::connect (mem_fun (*this, &Route::meter)); -} + add_processor (_meter, PostFader); -void -Route::init () -{ - _solo_level = 0; - _solo_isolated = false; - _active = true; - processor_max_streams.reset(); - _solo_safe = false; - _recordable = true; - order_keys[N_("signal")] = order_key_cnt++; - _silent = false; - _meter_point = MeterPostFader; - _initial_delay = 0; - _roll_delay = 0; - _have_internal_generator = false; - _declickable = false; - _pending_declick = true; - _remote_control_id = 0; - _in_configure_processors = false; + _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main)); + + add_processor (_main_outs, PostFader); - _route_group = 0; + if (is_monitor()) { + /* where we listen to tracks */ + _intreturn.reset (new InternalReturn (_session)); + add_processor (_intreturn, PreFader); - _phase_invert = 0; - _denormal_protection = false; + ProcessorList::iterator i; - /* add standard controls */ + for (i = _processors.begin(); i != _processors.end(); ++i) { + if (*i == _intreturn) { + ++i; + break; + } + } - add_control (_solo_control); - add_control (_mute_master); + /* 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); - /* input and output objects */ + /* no panning on the monitor main outs */ - _input.reset (new IO (_session, _name, IO::Input, _default_type)); - _output.reset (new IO (_session, _name, IO::Output, _default_type)); + _main_outs->panner()->set_bypassed (true); + } - _input->changed.connect (mem_fun (this, &Route::input_change_handler)); - _output->changed.connect (mem_fun (this, &Route::output_change_handler)); + if (is_master() || is_monitor() || is_hidden()) { + _mute_master->set_solo_ignore (true); + } - /* add amp processor */ + /* now that we have _meter, its safe to connect to this */ - _amp.reset (new Amp (_session, _mute_master)); - add_processor (_amp, PostFader); + Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this))); + + return 0; } Route::~Route () { - Metering::disconnect (_meter_connection); + DEBUG_TRACE (DEBUG::Destruction, string_compose ("route %1 destructor\n", _name)); + + /* do this early so that we don't get incoming signals as we are going through destruction + */ + + drop_connections (); - clear_processors (PreFader); - clear_processors (PostFader); + /* don't use clear_processors here, as it depends on the session which may + be half-destroyed by now + */ + + Glib::RWLock::WriterLock lm (_processor_lock); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + (*i)->drop_references (); + } + + _processors.clear (); } void -Route::set_remote_control_id (uint32_t id) +Route::set_remote_control_id (uint32_t id, bool notify_class_listeners) { if (id != _remote_control_id) { _remote_control_id = id; RemoteControlIDChanged (); + if (notify_class_listeners) { + RemoteControlIDChange (); + } } } @@ -179,7 +212,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); @@ -191,17 +224,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 @@ -216,7 +266,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) */ @@ -229,8 +279,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 */ } } @@ -239,8 +298,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; @@ -256,7 +315,7 @@ Route::inc_gain (gain_t fraction, void *src) void Route::set_gain (gain_t val, void *src) { - if (src != 0 && _route_group && src != _route_group && _route_group->active_property (RouteGroup::Gain)) { + if (src != 0 && _route_group && src != _route_group && _route_group->is_active() && _route_group->is_gain()) { if (_route_group->is_relative()) { @@ -353,9 +412,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; @@ -364,7 +423,7 @@ Route::process_output_buffers (BufferSet& bufs, DENORMAL CONTROL/PHASE INVERT ----------------------------------------------------------------------------------------- */ - if (_phase_invert) { + if (_phase_invert.any ()) { int chn = 0; @@ -373,7 +432,7 @@ 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) { + if (_phase_invert[chn]) { for (nframes_t nx = 0; nx < nframes; ++nx) { sp[nx] = -sp[nx]; sp[nx] += 1.0e-27f; @@ -390,7 +449,7 @@ 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) { + if (_phase_invert[chn]) { for (nframes_t nx = 0; nx < nframes; ++nx) { sp[nx] = -sp[nx]; } @@ -416,23 +475,17 @@ Route::process_output_buffers (BufferSet& bufs, and go .... ----------------------------------------------------------------------------------------- */ - Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK); - - if (rm.locked()) { - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - 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()); - (*i)->run (bufs, start_frame, end_frame, nframes); - bufs.set_count (ChanCount::max(bufs.count(), (*i)->output_streams())); - } + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if (!_processors.empty()) { - bufs.set_count (ChanCount::max (bufs.count(), _processors.back()->output_streams())); + 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()); + + (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back()); + bufs.set_count ((*i)->output_streams()); } } @@ -449,21 +502,20 @@ Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, _silent = false; - assert (bufs.available() >= _input->n_ports()); + assert (bufs.available() >= input_streams()); if (_input->n_ports() == ChanCount::ZERO) { - silence (nframes); + silence_unlocked (nframes); } - bufs.set_count (_input->n_ports()); + bufs.set_count (input_streams()); - if (is_control() && _session.listening()) { + if (is_monitor() && _session.listening() && !_session.is_auditioning()) { /* control/monitor bus ignores input ports when something is feeding the listen "stream". data will "arrive" into the route from the intreturn processor element. */ - bufs.silence (nframes, 0); } else { @@ -495,12 +547,18 @@ Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t n void Route::set_listen (bool yn, void* src) { - if (_control_outs) { - if (yn != _control_outs->active()) { + if (_solo_safe) { + return; + } + + if (_monitor_send) { + if (yn != _monitor_send->active()) { if (yn) { - _control_outs->activate (); - } else { - _control_outs->deactivate (); + _monitor_send->activate (); + _mute_master->set_soloed (true); + } else { + _monitor_send->deactivate (); + _mute_master->set_soloed (false); } listen_changed (src); /* EMIT SIGNAL */ @@ -511,97 +569,227 @@ Route::set_listen (bool yn, void* src) bool Route::listening () const { - if (_control_outs) { - return _control_outs->active (); + if (_monitor_send) { + return _monitor_send->active (); } else { return false; } } +void +Route::set_solo_safe (bool yn, void *src) +{ + if (_solo_safe != yn) { + _solo_safe = yn; + solo_safe_changed (src); + } +} + +bool +Route::solo_safe() const +{ + return _solo_safe; +} + void Route::set_solo (bool yn, void *src) { - if (_solo_safe || _solo_isolated) { + if (_solo_safe) { return; } - if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Solo)) { + if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) { _route_group->apply (&Route::set_solo, yn, _route_group); return; } - if (soloed() != yn) { - mod_solo_level (yn ? 1 : -1); - solo_changed (src); /* EMIT SIGNAL */ + if (self_soloed() != yn) { + set_self_solo (yn); + set_mute_master_solo (); + solo_changed (true, src); /* EMIT SIGNAL */ _solo_control->Changed (); /* EMIT SIGNAL */ } } void -Route::mod_solo_level (int32_t delta) +Route::set_self_solo (bool yn) +{ + _self_solo = yn; +} + +void +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 (_solo_level >= (uint32_t) delta) { - _solo_level += delta; + if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) { + _soloed_by_others_upstream += delta; + } else { + _soloed_by_others_upstream = 0; + } + } else { + _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_mute_master_solo (); + solo_changed (false, this); +} + +void +Route::mod_solo_by_others_downstream (int32_t delta) +{ + if (_solo_safe) { + return; + } + + if (delta < 0) { + if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) { + _soloed_by_others_downstream += delta; } else { - _solo_level = 0; + _soloed_by_others_downstream = 0; } } else { - _solo_level += delta; + _soloed_by_others_downstream += delta; } - /* tell main outs what the solo situation is - */ + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream)); - _main_outs->set_solo_level (_solo_level); - _main_outs->set_solo_isolated (_solo_isolated); + 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 Route::set_solo_isolated (bool yn, void *src) { - if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Solo)) { + if (is_master() || is_monitor() || is_hidden()) { + return; + } + + if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) { _route_group->apply (&Route::set_solo_isolated, yn, _route_group); return; } + + /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */ - if (yn != _solo_isolated) { - _solo_isolated = yn; + 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 = 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()); + } + } - /* tell main outs what the solo situation is - */ + /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */ - _main_outs->set_solo_level (_solo_level); - _main_outs->set_solo_isolated (_solo_isolated); + bool changed = false; - solo_isolated_changed (src); + if (yn) { + if (_solo_isolated == 0) { + _mute_master->set_solo_ignore (true); + changed = true; + } + _solo_isolated++; + } else { + if (_solo_isolated > 0) { + _solo_isolated--; + if (_solo_isolated == 0) { + _mute_master->set_solo_ignore (false); + changed = true; + } + } } + + if (changed) { + solo_isolated_changed (src); + } } bool Route::solo_isolated () const { - return _solo_isolated; + return _solo_isolated > 0; +} + +void +Route::set_mute_points (MuteMaster::MutePoint mp) +{ + _mute_master->set_mute_points (mp); + mute_points_changed (); /* EMIT SIGNAL */ + + if (_mute_master->muted_by_self()) { + mute_changed (this); /* EMIT SIGNAL */ + } } void Route::set_mute (bool yn, void *src) { - if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Mute)) { + if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_mute()) { _route_group->apply (&Route::set_mute, yn, _route_group); return; } if (muted() != yn) { - _mute_master->mute (yn); - mute_changed (src); + _mute_master->set_muted_by_self (yn); + mute_changed (src); /* EMIT SIGNAL */ } } bool -Route::muted() const +Route::muted () const { - return _mute_master->muted (); + return _mute_master->muted_by_self(); } #if 0 @@ -639,12 +827,12 @@ Route::add_processor (boost::shared_ptr processor, Placement placemen /** Add a processor to the route. - * If @a iter is not NULL, it must point to an iterator in _processors and the new + * @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. */ int -Route::add_processor (boost::shared_ptr processor, ProcessorList::iterator iter, ProcessorStreams* err) +Route::add_processor (boost::shared_ptr processor, ProcessorList::iterator iter, ProcessorStreams* err, bool activation_allowed) { ChanCount old_pms = processor_max_streams; @@ -702,135 +890,29 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite } - if (_control_outs != processor) { - // XXX: do we want to emit the signal here ? change call order. - processor->activate (); - } - processor->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false)); + /* is this the monitor send ? if so, make sure we keep track of it */ - _output->set_user_latency (0); - } + boost::shared_ptr isend = boost::dynamic_pointer_cast (processor); - processors_changed (); /* EMIT SIGNAL */ + if (isend && _session.monitor_out() && (isend->target_id() == _session.monitor_out()->id())) { + _monitor_send = isend; + } - return 0; -} + if (activation_allowed && (processor != _monitor_send)) { + processor->activate (); + } -bool -Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter) -{ - const XMLProperty *prop; + processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false)); - if (node.name() != "Processor") { - return false; + _output->set_user_latency (0); } - try { - if ((prop = node.property ("type")) != 0) { - - boost::shared_ptr processor; - - if (prop->value() == "ladspa" || prop->value() == "Ladspa" || - prop->value() == "lv2" || - prop->value() == "vst" || - prop->value() == "audiounit") { - - processor.reset (new PluginInsert(_session, node)); - - } else if (prop->value() == "port") { - - processor.reset (new PortInsert (_session, _mute_master, node)); - - } else if (prop->value() == "send") { - - processor.reset (new Send (_session, _mute_master, node)); - - } else if (prop->value() == "meter") { - - if (_meter) { - if (_meter->set_state (node, Stateful::loading_state_version)) { - return false; - } else { - return true; - } - } - - _meter.reset (new PeakMeter (_session, node)); - processor = _meter; - - } else if (prop->value() == "amp") { - - /* amp always exists */ - - processor = _amp; - if (processor->set_state (node, Stateful::loading_state_version)) { - return false; - } else { - /* never any reason to add it */ - return true; - } - - } else if (prop->value() == "intsend") { - - processor.reset (new InternalSend (_session, _mute_master, node)); - - } else if (prop->value() == "intreturn") { - - if (_intreturn) { - if (_intreturn->set_state (node, Stateful::loading_state_version)) { - return false; - } else { - return true; - } - } - _intreturn.reset (new InternalReturn (_session, node)); - processor = _intreturn; - - } else if (prop->value() == "main-outs") { - - if (_main_outs) { - if (_main_outs->set_state (node, Stateful::loading_state_version)) { - return false; - } else { - return true; - } - } - - _main_outs.reset (new Delivery (_session, _output, _mute_master, node)); - processor = _main_outs; - - } else { - error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg; - return false; - } - - if (iter == _processors.end() && processor->visible() && !_processors.empty()) { - /* check for invisible processors stacked at the end and leave them there */ - ProcessorList::iterator p; - p = _processors.end(); - --p; - while (!(*p)->visible() && p != _processors.begin()) { - --p; - } - ++p; - iter = p; - } - - return (add_processor (processor, iter) == 0); - - } else { - error << _("Processor XML node has no type property") << endmsg; - return false; - } - } + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + set_processor_positions (); - catch (failed_constructor &err) { - warning << _("processor could not be created. Ignored.") << endmsg; - return false; - } + return 0; } - bool Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorList::iterator iter) { @@ -848,18 +930,18 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorLis prop->value() == "vst" || prop->value() == "audiounit") { - processor.reset (new PluginInsert (_session, node)); + processor.reset (new PluginInsert (_session)); } else { - processor.reset (new PortInsert (_session, _mute_master, node)); + processor.reset (new PortInsert (_session, _mute_master)); } } } else if (node.name() == "Send") { - processor.reset (new Send (_session, _mute_master, node, version)); + processor.reset (new Send (_session, _mute_master)); } else { @@ -867,12 +949,16 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorLis return false; } - if (iter == _processors.end() && processor->visible() && !_processors.empty()) { + if (processor->set_state (node, version)) { + 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)->visible() && p != _processors.begin()) { + while (!(*p)->display_to_user() && p != _processors.begin()) { --p; } ++p; @@ -923,8 +1009,6 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter { Glib::RWLock::WriterLock lm (_processor_lock); - ProcessorList::iterator existing_end = _processors.end(); - --existing_end; ChanCount potential_max_streams = ChanCount::max (_input->n_ports(), _output->n_ports()); @@ -950,22 +1034,26 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter } } - _processors.insert (iter, *i); + ProcessorList::iterator inserted = _processors.insert (iter, *i); + + if ((*i)->active()) { + (*i)->activate (); + } if (configure_processors_unlocked (err)) { - ++existing_end; - _processors.erase (existing_end, _processors.end()); + _processors.erase (inserted); configure_processors_unlocked (0); // it worked before we tried to add it ... return -1; } - (*i)->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false)); + (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false)); } _output->set_user_latency (0); } - processors_changed (); /* EMIT SIGNAL */ + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + set_processor_positions (); return 0; } @@ -1165,7 +1253,8 @@ Route::clear_processors (Placement p) processor_max_streams.reset(); _have_internal_generator = false; - processors_changed (); /* EMIT SIGNAL */ + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + set_processor_positions (); if (!already_deleting) { _session.clear_deletion_in_progress(); @@ -1256,7 +1345,8 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream } processor->drop_references (); - processors_changed (); /* EMIT SIGNAL */ + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + set_processor_positions (); return 0; } @@ -1265,7 +1355,6 @@ int Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* err) { ProcessorList deleted; - ProcessorList as_we_were; if (!_session.engine().connected()) { return 1; @@ -1278,7 +1367,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* ProcessorList::iterator i; boost::shared_ptr processor; - as_we_were = _processors; + ProcessorList as_we_were = _processors; for (i = _processors.begin(); i != _processors.end(); ) { @@ -1348,7 +1437,8 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* (*i)->drop_references (); } - processors_changed (); /* EMIT SIGNAL */ + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + set_processor_positions (); return 0; } @@ -1364,6 +1454,12 @@ Route::configure_processors (ProcessorStreams* err) return 0; } +ChanCount +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. */ @@ -1377,14 +1473,22 @@ Route::configure_processors_unlocked (ProcessorStreams* err) _in_configure_processors = true; // Check each processor in order to see if we can configure as requested - ChanCount in = _input->n_ports (); + ChanCount in = input_streams (); ChanCount out; list< pair > configuration; uint32_t index = 0; + DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configure processors\n", _name)); + 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"); + for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) { if ((*p)->can_support_io_configuration(in, out)) { + DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 in = %2 out = %3\n",(*p)->name(), in, out)); configuration.push_back(make_pair(in, out)); in = out; } else { @@ -1397,15 +1501,6 @@ Route::configure_processors_unlocked (ProcessorStreams* err) } } - /* Take the process lock so that if we add a processor which increases the required - number of scratch buffers, we create those scratch buffers before the process - thread has a chance to ask for them. - XXX: in an ideal world we'd perhaps use some RCU magic to avoid having to take - the lock here. - */ - - Glib::Mutex::Lock pl (_session.engine().process_lock ()); - // We can, so configure everything list< pair >::iterator c = configuration.begin(); for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) { @@ -1415,9 +1510,18 @@ Route::configure_processors_unlocked (ProcessorStreams* err) out = c->second; } + if (_meter) { + _meter->reset_max_channels (processor_max_streams); + } + /* make sure we have sufficient scratch buffers to cope with the new processor configuration */ - _session.ensure_buffers (n_process_buffers ()); + { + Glib::Mutex::Lock em (_session.engine().process_lock ()); + _session.ensure_buffers (n_process_buffers ()); + } + + DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration complete\n", _name)); _in_configure_processors = false; return 0; @@ -1507,7 +1611,7 @@ int Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err) { /* "new_order" is an ordered list of processors to be positioned according to "placement". - NOTE: all processors in "new_order" MUST be marked as visible. There maybe additional + NOTE: all processors in "new_order" MUST be marked as display_to_user(). There maybe additional processors in the current actual processor list that are hidden. Any visible processors in the current list but not in "new_order" will be assumed to be deleted. */ @@ -1546,7 +1650,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err } else { - if (!(*oiter)->visible()) { + if (!(*oiter)->display_to_user()) { as_it_will_be.push_back (*oiter); @@ -1578,7 +1682,10 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err } } - processors_changed (); /* EMIT SIGNAL */ + if (true) { + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + set_processor_positions (); + } return 0; } @@ -1612,7 +1719,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)); @@ -1638,6 +1747,11 @@ Route::state(bool full_state) order_string += ':'; } node->add_property ("order-keys", order_string); + node->add_property ("self-solo", (_self_solo ? "yes" : "no")); + 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_child_nocopy (_input->state (full_state)); node->add_child_nocopy (_output->state (full_state)); @@ -1703,6 +1817,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(); @@ -1731,9 +1849,18 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) set_processor_state (processor_state); - if ((prop = node.property ("solo_level")) != 0) { - _solo_level = 0; // needed for mod_solo_level() to work - mod_solo_level (atoi (prop->value())); + if ((prop = node.property ("self-solo")) != 0) { + set_self_solo (string_is_affirmative (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) { @@ -1741,7 +1868,7 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) } 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) { @@ -1754,30 +1881,17 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) set_active (yn); } - if ((prop = node.property (X_("soloed"))) != 0) { - bool yn = string_is_affirmative (prop->value()); - - /* XXX force reset of solo status */ - - set_solo (yn, this); - } - if ((prop = node.property (X_("meter-point"))) != 0) { - _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point)); - } - - if ((prop = node.property (X_("route-group"))) != 0) { - RouteGroup* route_group = _session.route_group_by_name(prop->value()); - if (route_group == 0) { - error << string_compose(_("Route %1: unknown route group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg; - } else { - set_route_group (route_group, this); + MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point)); + set_meter_point (mp); + if (_meter) { + _meter->set_display_to_user (_meter_point == MeterCustom); } } if ((prop = node.property (X_("order-keys"))) != 0) { - long n; + int32_t n; string::size_type colon, equal; string remaining = prop->value(); @@ -1788,7 +1902,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 { @@ -1853,11 +1967,6 @@ Route::_set_state_2X (const XMLNode& node, int version) /* 2X things which still remain to be handled: * default-type - * muted - * mute-affects-pre-fader - * mute-affects-post-fader - * mute-affects-control-outs - * mute-affects-main-outs * automation * controlouts */ @@ -1872,99 +1981,97 @@ Route::_set_state_2X (const XMLNode& node, int version) } else { _flags = Flag (0); } + + if ((prop = node.property (X_("phase-invert"))) != 0) { + boost::dynamic_bitset<> p (_input->n_ports().n_audio ()); + if (string_is_affirmative (prop->value ())) { + p.set (); + } + set_phase_invert (p); + } - /* add standard processors */ + if ((prop = node.property (X_("denormal-protection"))) != 0) { + set_denormal_protection (string_is_affirmative (prop->value())); + } - _meter.reset (new PeakMeter (_session)); - add_processor (_meter, PreFader); + if ((prop = node.property (X_("soloed"))) != 0) { + bool yn = string_is_affirmative (prop->value()); - if (_flags & ControlOut) { - /* where we listen to tracks */ - _intreturn.reset (new InternalReturn (_session)); - add_processor (_intreturn, PreFader); + /* XXX force reset of solo status */ + + set_solo (yn, this); } - _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main)); - add_processor (_main_outs, PostFader); + if ((prop = node.property (X_("muted"))) != 0) { + + bool first = true; + bool muted = string_is_affirmative (prop->value()); + + if (muted){ + + string mute_point; + + if ((prop = node.property (X_("mute-affects-pre-fader"))) != 0) { + + if (string_is_affirmative (prop->value())){ + mute_point = mute_point + "PreFader"; + first = false; + } + } + + if ((prop = node.property (X_("mute-affects-post-fader"))) != 0) { + + if (string_is_affirmative (prop->value())){ + + if (!first) { + mute_point = mute_point + ","; + } + + mute_point = mute_point + "PostFader"; + first = false; + } + } - /* IOs */ + if ((prop = node.property (X_("mute-affects-control-outs"))) != 0) { + + if (string_is_affirmative (prop->value())){ + + if (!first) { + mute_point = mute_point + ","; + } + + mute_point = mute_point + "Listen"; + first = false; + } + } - nlist = node.children (); - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - - child = *niter; - - if (child->name() == IO::state_node_name) { - - /* there is a note in IO::set_state_2X() about why we have to call - this directly. - */ - - _input->set_state_2X (*child, version, true); - _output->set_state_2X (*child, version, false); - - if ((prop = child->property (X_("name"))) != 0) { - set_name (prop->value ()); - } - - if ((prop = child->property (X_("id"))) != 0) { - _id = prop->value (); - } - - if ((prop = child->property (X_("active"))) != 0) { - bool yn = string_is_affirmative (prop->value()); - _active = !yn; // force switch - set_active (yn); + if ((prop = node.property (X_("mute-affects-main-outs"))) != 0) { + + if (string_is_affirmative (prop->value())){ + + if (!first) { + mute_point = mute_point + ","; + } + + mute_point = mute_point + "Main"; + } } + + _mute_master->set_mute_points (mute_point); } - - /* XXX: panners? */ - } - - if ((prop = node.property (X_("phase-invert"))) != 0) { - set_phase_invert (string_is_affirmative (prop->value())); - } - - if ((prop = node.property (X_("denormal-protection"))) != 0) { - set_denormal_protection (string_is_affirmative (prop->value())); - } - - if ((prop = node.property (X_("soloed"))) != 0) { - bool yn = string_is_affirmative (prop->value()); - - /* XXX force reset of solo status */ - - set_solo (yn, this); } if ((prop = node.property (X_("meter-point"))) != 0) { _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point)); } - /* XXX: if the route was in both a mix group and an edit group, it'll end up - just in the edit group. */ - - if ((prop = node.property (X_("mix-group"))) != 0) { - RouteGroup* route_group = _session.route_group_by_name(prop->value()); - if (route_group == 0) { - error << string_compose(_("Route %1: unknown route group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg; - } else { - set_route_group (route_group, this); - } - } - - if ((prop = node.property (X_("edit-group"))) != 0) { - RouteGroup* route_group = _session.route_group_by_name(prop->value()); - if (route_group == 0) { - error << string_compose(_("Route %1: unknown route group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg; - } else { - set_route_group (route_group, this); - } - } + /* do not carry over edit/mix groups from 2.X because (a) its hard (b) they + don't mean the same thing. + */ if ((prop = node.property (X_("order-keys"))) != 0) { - long n; + int32_t n; string::size_type colon, equal; string remaining = prop->value(); @@ -1975,7 +2082,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 { @@ -1993,6 +2100,78 @@ 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 (); + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + + child = *niter; + + if (child->name() == IO::state_node_name) { + + /* there is a note in IO::set_state_2X() about why we have to call + this directly. + */ + + _input->set_state_2X (*child, version, true); + _output->set_state_2X (*child, version, false); + + if ((prop = child->property (X_("name"))) != 0) { + Route::set_name (prop->value ()); + } + + if ((prop = child->property (X_("id"))) != 0) { + _id = prop->value (); + } + + if ((prop = child->property (X_("active"))) != 0) { + bool yn = string_is_affirmative (prop->value()); + _active = !yn; // force switch + set_active (yn); + } + + if ((prop = child->property (X_("gain"))) != 0) { + gain_t val; + + if (sscanf (prop->value().c_str(), "%f", &val) == 1) { + _amp->gain_control()->set_value (val); + } + } + + /* Set up Panners in the IO */ + XMLNodeList io_nlist = child->children (); + + XMLNodeConstIterator io_niter; + XMLNode *io_child; + + for (io_niter = io_nlist.begin(); io_niter != io_nlist.end(); ++io_niter) { + + io_child = *io_niter; + + if (io_child->name() == X_("Panner")) { + _main_outs->panner()->set_state(*io_child, version); + } + } + } + } + XMLNodeList redirect_nodes; for (niter = nlist.begin(); niter != nlist.end(); ++niter){ @@ -2071,101 +2250,95 @@ Route::set_processor_state (const XMLNode& node) { const XMLNodeList &nlist = node.children(); XMLNodeConstIterator niter; - ProcessorList::iterator i, o; - - // Iterate through existing processors, remove those which are not in the state list - - for (i = _processors.begin(); i != _processors.end(); ) { + ProcessorList new_order; + bool must_configure = false; - /* leave amp alone, always */ - - if ((*i) == _amp) { - ++i; - continue; - } - - ProcessorList::iterator tmp = i; - ++tmp; - - bool processorInStateList = false; - - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - - XMLProperty* id_prop = (*niter)->property(X_("id")); - - if (id_prop && (*i)->id() == id_prop->value()) { - processorInStateList = true; - break; - } - } - - if (!processorInStateList) { - remove_processor (*i); - } - - i = tmp; - } - - // 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) { + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { XMLProperty* prop = (*niter)->property ("type"); - o = i; - - // Check whether the next processor in the list is the right one, - // except for "amp" which is always there and may not have the - // old ID since it is always created anew in every Route - - if (prop->value() != "amp") { - while (o != _processors.end()) { + if (prop->value() == "amp") { + _amp->set_state (**niter, Stateful::current_state_version); + 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 { + ProcessorList::iterator o; + + for (o = _processors.begin(); o != _processors.end(); ++o) { XMLProperty* id_prop = (*niter)->property(X_("id")); if (id_prop && (*o)->id() == id_prop->value()) { + (*o)->set_state (**niter, Stateful::current_state_version); + new_order.push_back (*o); break; } - - ++o; } - } - - // If the processor (*niter) is not on the route, - // create it and move it to the correct location - if (o == _processors.end()) { - - if (add_processor_from_xml (**niter, i)) { - --i; // move iterator to the newly inserted processor - } else { - cerr << "Error restoring route: unable to restore processor" << endl; - } - - } else { - - // Otherwise, the processor already exists; just - // ensure it is at the location provided in the XML state - - if (i != o) { - boost::shared_ptr tmp = (*o); - _processors.erase (o); // remove the old copy - _processors.insert (i, tmp); // insert the processor at the correct location - --i; // move iterator to the correct processor - } - - // and make it (just) so - - (*i)->set_state (**niter, Stateful::current_state_version); - } - } - - /* note: there is no configure_processors() call because we figure that - the XML state represents a working signal route. - */ + // If the processor (*niter) is not on the route then create it + + if (o == _processors.end()) { + + boost::shared_ptr processor; + + if (prop->value() == "intsend") { + + processor.reset (new InternalSend (_session, _mute_master, boost::shared_ptr(), Delivery::Role (0))); + + } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" || + prop->value() == "lv2" || + prop->value() == "vst" || + prop->value() == "audiounit") { + + processor.reset (new PluginInsert(_session)); + + } else if (prop->value() == "port") { + + processor.reset (new PortInsert (_session, _mute_master)); + + } else if (prop->value() == "send") { + + processor.reset (new Send (_session, _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; + } + } + } + + { + Glib::RWLock::WriterLock lm (_processor_lock); + _processors = new_order; + if (must_configure) { + configure_processors_unlocked (0); + } + } - processors_changed (); + processors_changed (RouteProcessorChange ()); + set_processor_positions (); } void @@ -2178,31 +2351,37 @@ Route::curve_reallocate () void Route::silence (nframes_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 (nframes_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; } - } } @@ -2265,8 +2444,8 @@ Route::listen_via (boost::shared_ptr route, Placement placement, bool /*a we take note of which i-send is doing that. */ - if (route == _session.control_out()) { - _control_outs = boost::dynamic_pointer_cast(d); + if (route == _session.monitor_out()) { + _monitor_send = boost::dynamic_pointer_cast(d); } /* already listening via the specified IO: do nothing */ @@ -2279,17 +2458,35 @@ Route::listen_via (boost::shared_ptr route, Placement placement, bool /*a boost::shared_ptr listener; try { - listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen))); + + 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))); + } } catch (failed_constructor& err) { return -1; } - if (route == _session.control_out()) { - _control_outs = listener; + if (route == _session.monitor_out()) { + _monitor_send = listener; } - add_processor (listener, placement); + 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; } @@ -2323,78 +2520,108 @@ Route::drop_listen (boost::shared_ptr route) rl.release (); - if (route == _session.control_out()) { - _control_outs.reset (); + if (route == _session.monitor_out()) { + _monitor_send.reset (); } } void -Route::set_route_group (RouteGroup *rg, void *src) +Route::set_comment (string cmt, void *src) { - if (rg == _route_group) { - return; - } + _comment = cmt; + comment_changed (src); + _session.set_dirty (); +} - if (_route_group) { - _route_group->remove (this); - } +bool +Route::add_fed_by (boost::shared_ptr other, bool via_sends_only) +{ + FeedRecord fr (other, via_sends_only); - if ((_route_group = rg) != 0) { - _route_group->add (this); - } + pair result = _fed_by.insert (fr); - _session.set_dirty (); - route_group_changed (src); /* EMIT SIGNAL */ + 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::drop_route_group (void *src) +Route::clear_fed_by () { - _route_group = 0; - _session.set_dirty (); - route_group_changed (src); /* EMIT SIGNAL */ + _fed_by.clear (); } -void -Route::set_comment (string cmt, void *src) +bool +Route::feeds (boost::shared_ptr other, bool* via_sends_only) { - _comment = cmt; - comment_changed (src); - _session.set_dirty (); + 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::feeds (boost::shared_ptr other) +Route::direct_feeds (boost::shared_ptr other, bool* only_send) { - // cerr << _name << endl; + DEBUG_TRACE (DEBUG::Graph, string_compose ("Feeds? %1\n", _name)); if (_output->connected_to (other->input())) { - // cerr << "\tdirect FEEDS " << other->name() << endl; + DEBUG_TRACE (DEBUG::Graph, string_compose ("\tdirect FEEDS %2\n", other->name())); + if (only_send) { + *only_send = false; + } + return true; } - for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); r++) { + + for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) { boost::shared_ptr iop; if ((iop = boost::dynamic_pointer_cast(*r)) != 0) { if (iop->feeds (other)) { - // cerr << "\tIOP " << iop->name() << " feeds " << other->name() << endl; + DEBUG_TRACE (DEBUG::Graph, string_compose ("\tIOP %1 does feed %2\n", iop->name(), other->name())); + if (only_send) { + *only_send = true; + } return true; } else { - // cerr << "\tIOP " << iop->name() << " does NOT feeds " << other->name() << endl; + DEBUG_TRACE (DEBUG::Graph, string_compose ("\tIOP %1 does NOT feed %2\n", iop->name(), other->name())); } + } else { + DEBUG_TRACE (DEBUG::Graph, string_compose ("\tPROC %1 is not an IOP\n", (*r)->name())); } + } - // cerr << "\tdoes NOT FEED " << other->name() << endl; + DEBUG_TRACE (DEBUG::Graph, string_compose ("\tdoes NOT feed %1\n", other->name())); return false; } void Route::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); @@ -2403,11 +2630,14 @@ 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); @@ -2422,6 +2652,8 @@ Route::input_change_handler (IOChange change, void * /*src*/) { if ((change & ConfigurationChanged)) { configure_processors (0); + _phase_invert.resize (_input->n_ports().n_audio ()); + io_changed (); /* EMIT SIGNAL */ } } @@ -2450,14 +2682,33 @@ int Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_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 (session_state_changing || !_active || n_inputs() == ChanCount::ZERO) { - silence (nframes); + if (!_active || n_inputs() == ChanCount::ZERO) { + silence_unlocked (nframes); return 0; } + if (session_state_changing) { + if (_session.transport_speed() != 0.0f) { + /* we're rolling but some state is changing (e.g. our diskstream contents) + so we cannot use them. Be silent till this is over. + + XXX note the absurdity of ::no_roll() being called when we ARE rolling! + */ + silence_unlocked (nframes); + return 0; + } + /* we're really not rolling, so we're either delivery silence or actually + monitoring, both of which are safe to do while session_state_changing is true. + */ + } _amp->apply_gain_automation (false); passthru (start_frame, end_frame, nframes, 0); @@ -2471,14 +2722,14 @@ Route::check_initial_delay (nframes_t nframes, nframes_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. @@ -2494,24 +2745,21 @@ 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, - bool /*can_record*/, bool /*rec_monitors_input*/) + 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; } @@ -2530,7 +2778,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*/, - bool /*can_record*/, bool /*rec_monitors_input*/) + bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */) { silence (nframes); return 0; @@ -2582,66 +2830,115 @@ 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, void *src) +Route::set_meter_point (MeterPoint p) { - if (_meter_point != p) { - _meter_point = p; + /* CAN BE CALLED FROM PROCESS CONTEXT */ - // Move meter in the processors list - 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; - } - _processors.insert(loc, _meter); + if (_meter_point == p) { + return; + } - meter_change (src); /* EMIT SIGNAL */ - processors_changed (); /* EMIT SIGNAL */ - _session.set_dirty (); + 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; + } + + ChanCount m_in; + + if (loc == _processors.begin()) { + m_in = _input->n_ports(); + } else { + ProcessorList::iterator before = loc; + --before; + m_in = (*before)->output_streams (); + } + + _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 + + _meter->set_display_to_user (true); + } } + + _meter_point = p; + meter_change (); /* EMIT SIGNAL */ + + bool const meter_visibly_changed = (_meter->display_to_user() != meter_was_visible_to_user); + + processors_changed (RouteProcessorChange (RouteProcessorChange::MeterPointChange, meter_visibly_changed)); /* EMIT SIGNAL */ } + void -Route::put_control_outs_at (Placement p) +Route::put_monitor_send_at (Placement p) { - if (!_control_outs) { + if (!_monitor_send) { return; } - // Move meter in the processors list - ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _control_outs); - _processors.erase(loc); + { + 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); - switch (p) { - case PreFader: - loc = find(_processors.begin(), _processors.end(), _amp); - if (loc != _processors.begin()) { - --loc; + if (configure_processors_unlocked (0)) { + _processors = as_it_was; + configure_processors_unlocked (0); // it worked before we tried to add it ... + return; } - break; - case PostFader: - loc = find(_processors.begin(), _processors.end(), _amp); - assert (loc != _processors.end()); - loc++; - break; } - _processors.insert(loc, _control_outs); - - processors_changed (); /* EMIT SIGNAL */ + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ _session.set_dirty (); } @@ -2657,10 +2954,7 @@ Route::update_total_latency () } } -#undef DEBUG_LATENCY -#ifdef DEBUG_LATENCY - cerr << _name << ": internal redirect latency = " << own_latency << endl; -#endif + DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal redirect latency = %2\n", _name, own_latency)); _output->set_port_latency (own_latency); @@ -2681,10 +2975,7 @@ Route::update_total_latency () signal_latency_changed (); /* EMIT SIGNAL */ } -#ifdef DEBUG_LATENCY - cerr << _name << ": input latency = " << _input->signal_latency() << " total = " - << own_latency << endl; -#endif + DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: input latency = %2 total = %3\n", _name, _input->signal_latency(), own_latency)); return _output->effective_latency (); } @@ -2719,6 +3010,8 @@ Route::set_latency_delay (nframes_t longest_session_latency) void Route::automation_snapshot (nframes_t now, bool force) { + panner()->automation_snapshot (now, force); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->automation_snapshot (now, force); } @@ -2734,17 +3027,63 @@ Route::SoloControllable::SoloControllable (std::string name, Route& r) } 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); + + 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 +double Route::SoloControllable::get_value (void) const { - return route.soloed() ? 1.0f : 0.0f; + if (Config->get_solo_control_is_listen_control()) { + return route.listening() ? 1.0f : 0.0f; + } else { + return route.self_soloed() ? 1.0f : 0.0f; + } +} + +Route::MuteControllable::MuteControllable (std::string name, Route& r) + : AutomationControl (r.session(), Evoral::Parameter (MuteAutomation), + boost::shared_ptr(), name) + , route (r) +{ + boost::shared_ptr gl(new AutomationList(Evoral::Parameter(MuteAutomation))); + set_list (gl); +} + +void +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); + _session.set_mute (rl, bval); +#else + route.set_mute (bval, this); +#endif +} + +double +Route::MuteControllable::get_value (void) const +{ + return route.muted() ? 1.0f : 0.0f; } void @@ -2893,19 +3232,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 (uint32_t c, bool yn) +{ + if (_phase_invert[c] != yn) { + _phase_invert[c] = yn; + phase_invert_changed (); /* EMIT SIGNAL */ + _session.set_dirty (); + } +} + void -Route::set_phase_invert (bool yn) +Route::set_phase_invert (boost::dynamic_bitset<> p) { - if (_phase_invert != yn) { - _phase_invert = 0xffff; // XXX all channels + 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 @@ -2959,14 +3318,12 @@ Route::meter () boost::shared_ptr Route::panner() const { - return _main_outs->panner(); } boost::shared_ptr Route::gain_control() const { - return _amp->gain_control(); } @@ -2975,7 +3332,7 @@ Route::get_control (const Evoral::Parameter& param) { /* either we own the control or .... */ - boost::shared_ptr c = boost::dynamic_pointer_cast(data().control (param)); + boost::shared_ptr c = boost::dynamic_pointer_cast(control (param)); if (!c) { @@ -2983,7 +3340,7 @@ Route::get_control (const Evoral::Parameter& param) Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if ((c = boost::dynamic_pointer_cast((*i)->data().control (param))) != 0) { + if ((c = boost::dynamic_pointer_cast((*i)->control (param))) != 0) { break; } } @@ -2999,3 +3356,76 @@ Route::get_control (const Evoral::Parameter& param) return c; } + +boost::shared_ptr +Route::nth_plugin (uint32_t n) +{ + Glib::RWLock::ReaderLock lm (_processor_lock); + ProcessorList::iterator i; + + for (i = _processors.begin(); i != _processors.end(); ++i) { + if (boost::dynamic_pointer_cast (*i)) { + if (n-- == 0) { + return *i; + } + } + } + + return boost::shared_ptr (); +} + +boost::shared_ptr +Route::nth_send (uint32_t n) +{ + Glib::RWLock::ReaderLock lm (_processor_lock); + ProcessorList::iterator i; + + for (i = _processors.begin(); i != _processors.end(); ++i) { + if (boost::dynamic_pointer_cast (*i)) { + if (n-- == 0) { + return *i; + } + } + } + + 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; + } + } +} +