X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=89dbf3fcd27138806526d1faa1c544097bbf18cf;hb=6dac4da98344ed8ce609a4e7d567ef2f9fbb9b31;hp=eeb8ae83c17b28b77134f2033756591cf6c87898;hpb=aae367b63c9b619db1e40f27dc334c6987219481;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index eeb8ae83c1..89dbf3fcd2 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -25,6 +25,8 @@ #include "pbd/xml++.h" #include "pbd/enumwriter.h" #include "pbd/memento_command.h" +#include "pbd/stacktrace.h" +#include "pbd/convert.h" #include "evoral/Curve.hpp" @@ -43,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" @@ -64,104 +67,119 @@ using namespace PBD; uint32_t Route::order_key_cnt = 0; PBD::Signal1 Route::SyncOrderKeys; +PBD::Signal0 Route::RemoteControlIDChange; Route::Route (Session& sess, string name, Flag flg, DataType default_type) : SessionObject (sess, name) , AutomatableControls (sess) + , _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) + , _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) + , _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)); + + add_control (_solo_control); + add_control (_mute_control); - _meter.reset (new PeakMeter (_session)); - _meter->set_display_to_user (false); - add_processor (_meter, PostFader); + /* input and output objects */ - if (_flags & ControlOut) { - /* where we listen to tracks */ - _intreturn.reset (new InternalReturn (_session)); - add_processor (_intreturn, PreFader); - } + _input.reset (new IO (_session, _name, IO::Input, _default_type)); + _output.reset (new IO (_session, _name, IO::Output, _default_type)); - _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main)); - add_processor (_main_outs, PostFader); + _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)); - /* now that we have _meter, its safe to connect to this */ + /* add amp processor */ - Metering::Meter.connect (*this, (boost::bind (&Route::meter, this))); -} + _amp.reset (new Amp (_session, _mute_master)); + add_processor (_amp, PostFader); -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 (); + /* add standard processors: meter, main outs, monitor out */ - _set_state (node, Stateful::loading_state_version, false); + _meter.reset (new PeakMeter (_session)); + _meter->set_display_to_user (false); - /* now that we have _meter, its safe to connect to this */ + add_processor (_meter, PostFader); - Metering::Meter.connect (*this, (boost::bind (&Route::meter, this))); -} + _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main)); -void -Route::init () -{ - _self_solo = false; - _soloed_by_others = 0; - _solo_isolated = 0; - _solo_safe = false; - _active = true; - processor_max_streams.reset(); - _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; - _mute_points = MuteMaster::AllPoints; + add_processor (_main_outs, PostFader); - _phase_invert = 0; - _denormal_protection = false; + if (is_monitor()) { + /* where we listen to tracks */ + _intreturn.reset (new InternalReturn (_session)); + add_processor (_intreturn, PreFader); - /* add standard controls */ + ProcessorList::iterator i; - add_control (_solo_control); - add_control (_mute_master); + for (i = _processors.begin(); i != _processors.end(); ++i) { + if (*i == _intreturn) { + ++i; + break; + } + } - /* input and output objects */ + /* 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.reset (new IO (_session, _name, IO::Input, _default_type)); - _output.reset (new IO (_session, _name, IO::Output, _default_type)); + /* no panning on the monitor main outs */ - _input->changed.connect (*this, boost::bind (&Route::input_change_handler, this, _1, _2)); - _output->changed.connect (*this, boost::bind (&Route::output_change_handler, this, _1, _2)); + _main_outs->panner()->set_bypassed (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 () { 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 (); + /* don't use clear_processors here, as it depends on the session which may - be half-destroyed by now */ + be half-destroyed by now + */ Glib::RWLock::WriterLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { @@ -172,11 +190,14 @@ Route::~Route () } 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 (); + } } } @@ -246,7 +267,7 @@ Route::ensure_track_or_route_name(string name, Session &session) { string newname = name; - while (session.route_by_name (newname) != NULL) { + while (!session.io_name_is_legal (newname)) { newname = bump_name_once (newname); } @@ -263,7 +284,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()) { @@ -397,7 +418,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 & (1<input_streams()); (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back()); - bufs.set_count (ChanCount::max(bufs.count(), (*i)->output_streams())); - } - - if (!_processors.empty()) { - bufs.set_count (ChanCount::max (bufs.count(), _processors.back()->output_streams())); + bufs.set_count ((*i)->output_streams()); } } } @@ -458,21 +475,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); } - 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 { @@ -504,12 +520,16 @@ 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 (); + _monitor_send->activate (); } else { - _control_outs->deactivate (); + _monitor_send->deactivate (); } listen_changed (src); /* EMIT SIGNAL */ @@ -520,8 +540,8 @@ 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; } @@ -549,15 +569,15 @@ Route::set_solo (bool yn, void *src) 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 (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 */ } } @@ -565,14 +585,18 @@ 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) { + if (_solo_safe) { + return; + } + if (delta < 0) { - if (_soloed_by_others >= (uint32_t) delta) { + if (_soloed_by_others >= (uint32_t) abs (delta)) { _soloed_by_others += delta; } else { _soloed_by_others = 0; @@ -581,36 +605,34 @@ Route::mod_solo_by_others (int32_t delta) _soloed_by_others += delta; } - set_delivery_solo (); + set_mute_master_solo (); + solo_changed (false, this); } void -Route::set_delivery_solo () +Route::set_mute_master_solo () { - /* 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. - */ + int32_t level; - 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 (self_soloed()) { + level = 2; + } else if (soloed_by_others()) { + level = 1; + } else { + level = 0; + } + + _mute_master->set_solo_level (level); } void Route::set_solo_isolated (bool yn, void *src) { - if (is_master() || is_control() || is_hidden()) { + if (is_master() || is_monitor() || is_hidden()) { 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_isolated, yn, _route_group); return; } @@ -619,32 +641,30 @@ 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()); } } - bool changed = false; + /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */ if (yn) { - if (_solo_isolated == 0) { - changed = true; - } _solo_isolated++; + _mute_master->clear_muted_by_others (); } else { - changed = (_solo_isolated == 1); if (_solo_isolated > 0) { _solo_isolated--; } } - if (changed) { - set_delivery_solo (); - solo_isolated_changed (src); - } } bool @@ -656,38 +676,61 @@ 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_points = mp; + _mute_master->set_mute_points (MuteMaster::AllPoints); + mute_points_changed (); /* EMIT SIGNAL */ + + if (_mute_master->muted()) { + 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) { - if (yn) { - _mute_master->mute_at (_mute_points); - } else { - _mute_master->clear_mute (); - } - + if (self_muted() != yn) { + _mute_master->set_self_muted (yn); mute_changed (src); /* EMIT SIGNAL */ } } bool -Route::muted() const +Route::muted () const +{ + return self_muted() || muted_by_others(); +} + +bool +Route::self_muted() const +{ + return _mute_master->self_muted (); +} + +bool +Route::muted_by_others() const { - return _mute_master->muted (); + return _mute_master->muted_by_others (); +} + +void +Route::mod_muted_by_others (int delta) +{ + bool old = muted (); + + if (_solo_isolated) { + return; + } + + _mute_master->mod_muted_by_others (delta); + + if (old != muted()) { + mute_changed (this); + } } #if 0 @@ -725,12 +768,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; @@ -788,11 +831,19 @@ 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. + /* 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 (activation_allowed && (processor != _monitor_send)) { processor->activate (); } - processor->ActiveChanged.connect (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false)); + + processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false)); _output->set_user_latency (0); } @@ -802,122 +853,6 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite return 0; } -bool -Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter) -{ - const XMLProperty *prop; - - if (node.name() != "Processor") { - return false; - } - - 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)); - _meter->set_display_to_user (_meter_point == MeterCustom); - 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->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); - - } else { - error << _("Processor XML node has no type property") << endmsg; - return false; - } - } - - catch (failed_constructor &err) { - warning << _("processor could not be created. Ignored.") << endmsg; - return false; - } -} - - bool Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorList::iterator iter) { @@ -935,18 +870,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 { @@ -954,6 +889,10 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorLis return false; } + 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; @@ -1047,7 +986,7 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter return -1; } - (*i)->ActiveChanged.connect (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false)); + (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false)); } _output->set_user_latency (0); @@ -1353,7 +1292,6 @@ int Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* err) { ProcessorList deleted; - ProcessorList as_we_were; if (!_session.engine().connected()) { return 1; @@ -1366,7 +1304,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(); ) { @@ -1452,6 +1390,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. */ @@ -1465,7 +1409,7 @@ 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; @@ -1510,7 +1454,10 @@ Route::configure_processors_unlocked (ProcessorStreams* err) /* 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 ()); + } _in_configure_processors = false; return 0; @@ -1671,7 +1618,9 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err } } - processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + if (true) { + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + } return 0; } @@ -1855,7 +1804,8 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) } if ((prop = node.property (X_("meter-point"))) != 0) { - _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point)); + MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point)); + set_meter_point (mp); if (_meter) { _meter->set_display_to_user (_meter_point == MeterCustom); } @@ -1939,11 +1889,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 */ @@ -1958,55 +1903,7 @@ Route::_set_state_2X (const XMLNode& node, int version) } else { _flags = Flag (0); } - - /* add standard processors */ - - _meter.reset (new PeakMeter (_session)); - add_processor (_meter, PreFader); - - if (_flags & ControlOut) { - /* where we listen to tracks */ - _intreturn.reset (new InternalReturn (_session)); - add_processor (_intreturn, PreFader); - } - - _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) { - 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); - } - } - - /* XXX: panners? */ - } - + if ((prop = node.property (X_("phase-invert"))) != 0) { set_phase_invert (string_is_affirmative (prop->value())); } @@ -2023,6 +1920,65 @@ Route::_set_state_2X (const XMLNode& node, int version) set_solo (yn, this); } + 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; + } + } + + 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; + } + } + + 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); + } + } + if ((prop = node.property (X_("meter-point"))) != 0) { _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point)); } @@ -2062,6 +2018,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){ @@ -2140,101 +2168,94 @@ 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(); ) { - - /* leave amp alone, always */ - - if ((*i) == _amp) { - ++i; - continue; - } - - ProcessorList::iterator tmp = i; - ++tmp; - - bool processorInStateList = false; + ProcessorList new_order; + bool must_configure = 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 (RouteProcessorChange ()); + processors_changed (RouteProcessorChange ()); } void @@ -2334,8 +2355,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 */ @@ -2348,17 +2369,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; } @@ -2392,8 +2431,8 @@ 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 (); } } @@ -2406,7 +2445,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)); @@ -2420,7 +2505,7 @@ Route::feeds (boost::shared_ptr other, bool* only_send) } - for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); r++) { + for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) { boost::shared_ptr iop; @@ -2507,10 +2592,24 @@ Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, return 0; } - if (session_state_changing || !_active || n_inputs() == ChanCount::ZERO) { + if (!_active || n_inputs() == ChanCount::ZERO) { silence (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 (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); @@ -2547,7 +2646,7 @@ 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 @@ -2583,7 +2682,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; @@ -2641,7 +2740,7 @@ Route::flush_processors () } void -Route::set_meter_point (MeterPoint p, void *src) +Route::set_meter_point (MeterPoint p) { /* CAN BE CALLED FROM PROCESS CONTEXT */ @@ -2703,7 +2802,7 @@ Route::set_meter_point (MeterPoint p, void *src) } _meter_point = p; - meter_change (src); /* EMIT SIGNAL */ + meter_change (); /* EMIT SIGNAL */ bool const meter_visibly_changed = (_meter->display_to_user() != meter_was_visible_to_user); @@ -2711,17 +2810,16 @@ Route::set_meter_point (MeterPoint p, void *src) } void -Route::put_control_outs_at (Placement p) +Route::put_monitor_send_at (Placement p) { - if (!_control_outs) { + if (!_monitor_send) { return; } { Glib::RWLock::WriterLock lm (_processor_lock); ProcessorList as_it_was (_processors); - // Move meter in the processors list - ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _control_outs); + ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _monitor_send); _processors.erase(loc); switch (p) { @@ -2732,13 +2830,11 @@ Route::put_control_outs_at (Placement p) } break; case PostFader: - loc = find(_processors.begin(), _processors.end(), _amp); - assert (loc != _processors.end()); - loc++; + loc = _processors.end(); break; } - _processors.insert(loc, _control_outs); + _processors.insert (loc, _monitor_send); if (configure_processors_unlocked (0)) { _processors = as_it_was; @@ -2819,6 +2915,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); } @@ -2837,14 +2935,60 @@ void Route::SoloControllable::set_value (float 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 Route::SoloControllable::get_value (void) const { - return route.self_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 (float 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 +} + +float +Route::MuteControllable::get_value (void) const +{ + return route.self_muted() ? 1.0f : 0.0f; } void @@ -2997,8 +3141,14 @@ void Route::set_phase_invert (bool yn) { if (_phase_invert != yn) { - _phase_invert = 0xffff; // XXX all channels + if (yn) { + _phase_invert = 0xffff; // XXX all channels + } else { + _phase_invert = 0; // XXX no channels + } + phase_invert_changed (); /* EMIT SIGNAL */ + _session.set_dirty (); } } @@ -3059,14 +3209,12 @@ Route::meter () boost::shared_ptr Route::panner() const { - return _main_outs->panner(); } boost::shared_ptr Route::gain_control() const { - return _amp->gain_control(); } @@ -3075,7 +3223,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) { @@ -3083,7 +3231,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; } } @@ -3099,3 +3247,55 @@ 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; +}