X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=95571bd3dab2f48254876fbcb8486547d7f8b479;hb=3ee550573c2ae3e37d0e3bd7eedb6c64e13c3ebc;hp=107fae25851ff05fe2db6dbf6d992fcf75308d61;hpb=199dce57f35014189f21c76fff32e0752bcc8c15;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 107fae2585..95571bd3da 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -15,7 +15,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ #include @@ -24,6 +23,7 @@ #include #include +#include #include #include @@ -48,26 +48,25 @@ using namespace std; using namespace ARDOUR; using namespace PBD; - uint32_t Route::order_key_cnt = 0; - +sigc::signal Route::SyncOrderKeys; Route::Route (Session& sess, string name, int input_min, int input_max, int output_min, int output_max, Flag flg, DataType default_type) : IO (sess, name, input_min, input_max, output_min, output_max, default_type), _flags (flg), - _solo_control (*this, ToggleControllable::SoloControl), - _mute_control (*this, ToggleControllable::MuteControl) + _solo_control (X_("solo"), *this, ToggleControllable::SoloControl), + _mute_control (X_("mute"), *this, ToggleControllable::MuteControl) { init (); } -Route::Route (Session& sess, const XMLNode& node) - : IO (sess, "route"), - _solo_control (*this, ToggleControllable::SoloControl), - _mute_control (*this, ToggleControllable::MuteControl) +Route::Route (Session& sess, const XMLNode& node, DataType default_type) + : IO (sess, *node.child ("IO"), default_type), + _solo_control (X_("solo"), *this, ToggleControllable::SoloControl), + _mute_control (X_("mute"), *this, ToggleControllable::MuteControl) { init (); - set_state (node); + _set_state (node, false); } void @@ -78,8 +77,8 @@ Route::init () _soloed = false; _solo_safe = false; _phase_invert = false; - order_keys[N_("signal")] = order_key_cnt++; - _active = true; + _denormal_protection = false; + order_keys[strdup (N_("signal"))] = order_key_cnt++; _silent = false; _meter_point = MeterPostFader; _initial_delay = 0; @@ -89,7 +88,8 @@ Route::init () _declickable = false; _pending_declick = true; _remote_control_id = 0; - + _ignore_gain_on_deliver = true; + _edit_group = 0; _mix_group = 0; @@ -111,7 +111,12 @@ Route::init () Route::~Route () { - clear_redirects (this); + clear_redirects (PreFader, this); + clear_redirects (PostFader, this); + + for (OrderKeys::iterator i = order_keys.begin(); i != order_keys.end(); ++i) { + free ((void*)(i->first)); + } if (_control_outs) { delete _control_outs; @@ -134,24 +139,65 @@ Route::remote_control_id() const } long -Route::order_key (string name) const +Route::order_key (const char* name) const { OrderKeys::const_iterator i; - if ((i = order_keys.find (name)) == order_keys.end()) { - return -1; + for (i = order_keys.begin(); i != order_keys.end(); ++i) { + if (!strcmp (name, i->first)) { + return i->second; + } } - return (*i).second; + return -1; } void -Route::set_order_key (string name, long n) +Route::set_order_key (const char* name, long n) { - order_keys[name] = n; + order_keys[strdup(name)] = n; + + if (Config->get_sync_all_route_ordering()) { + for (OrderKeys::iterator x = order_keys.begin(); x != order_keys.end(); ++x) { + x->second = n; + } + } + _session.set_dirty (); } +void +Route::sync_order_keys () +{ + uint32_t key; + + if (order_keys.empty()) { + return; + } + + OrderKeys::iterator x = order_keys.begin(); + key = x->second; + ++x; + + for (; x != order_keys.end(); ++x) { + x->second = key; + } +} + +string +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); + } + + return newname; +} + + void Route::inc_gain (gain_t fraction, void *src) { @@ -215,8 +261,8 @@ Route::set_gain (gain_t val, void *src) void Route::process_output_buffers (vector& bufs, uint32_t nbufs, - jack_nframes_t start_frame, jack_nframes_t end_frame, - jack_nframes_t nframes, jack_nframes_t offset, bool with_redirects, int declick, + nframes_t start_frame, nframes_t end_frame, + nframes_t nframes, nframes_t offset, bool with_redirects, int declick, bool meter) { uint32_t n; @@ -228,9 +274,18 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, IO *co; bool mute_audible; bool solo_audible; - bool no_monitor = (Config->get_use_hardware_monitoring() || !Config->get_use_sw_monitoring ()); + bool no_monitor; gain_t* gab = _session.gain_automation_buffer(); + switch (Config->get_monitoring_model()) { + case HardwareMonitoring: + case ExternalMonitoring: + no_monitor = true; + break; + default: + no_monitor = false; + } + declick = _pending_declick; { @@ -262,17 +317,17 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, -------------------------------------------------------------------------------------------------- */ if (declick > 0) { - apply_declick (bufs, nbufs, nframes, 0.0, 1.0, _phase_invert); + apply_declick (bufs, nbufs, nframes, 0.0, 1.0, false); _pending_declick = 0; } else if (declick < 0) { - apply_declick (bufs, nbufs, nframes, 1.0, 0.0, _phase_invert); + apply_declick (bufs, nbufs, nframes, 1.0, 0.0, false); _pending_declick = 0; } else { /* no global declick */ if (solo_gain != dsg) { - apply_declick (bufs, nbufs, nframes, solo_gain, dsg, _phase_invert); + apply_declick (bufs, nbufs, nframes, solo_gain, dsg, false); solo_gain = dsg; } } @@ -289,7 +344,7 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, } if (!_soloed && _mute_affects_pre_fader && (mute_gain != dmg)) { - apply_declick (bufs, nbufs, nframes, mute_gain, dmg, _phase_invert); + apply_declick (bufs, nbufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } @@ -311,7 +366,7 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, // TODO: this is probably wrong - (no_monitor && record_enabled() && (!_session.get_auto_input() || _session.actively_recording())) + (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording())) ) { @@ -324,6 +379,22 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, } } + /* ----------------------------------------------------------------------------------------------------- + DENORMAL CONTROL + -------------------------------------------------------------------------------------------------- */ + + if (_denormal_protection || Config->get_denormal_protection()) { + + for (n = 0; n < nbufs; ++n) { + Sample *sp = bufs[n]; + + for (nframes_t nx = offset; nx < nframes + offset; ++nx) { + sp[nx] += 1.0e-27f; + } + } + } + + /* ---------------------------------------------------------------------------------------------------- PRE-FADER REDIRECTS -------------------------------------------------------------------------------------------------- */ @@ -335,7 +406,13 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, for (i = _redirects.begin(); i != _redirects.end(); ++i) { switch ((*i)->placement()) { case PreFader: - (*i)->run (bufs, nbufs, nframes, offset); + if (dsg == 0) { + if (boost::dynamic_pointer_cast(*i) || boost::dynamic_pointer_cast(*i)) { + (*i)->silence (nframes, offset); + } + } else { + (*i)->run (bufs, nbufs, nframes, offset); + } break; case PostFader: post_fader_work = true; @@ -359,7 +436,7 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_post_fader) { - apply_declick (bufs, nbufs, nframes, mute_gain, dmg, _phase_invert); + apply_declick (bufs, nbufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } @@ -390,7 +467,7 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, // rec-enabled but not s/w monitoring - (no_monitor && record_enabled() && (!_session.get_auto_input() || _session.actively_recording())) + (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording())) ) { @@ -398,7 +475,7 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, } else { - co->deliver_output (bufs, nbufs, nframes, offset); + co->deliver_output_no_pan (bufs, nbufs, nframes, offset); } } @@ -415,13 +492,9 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, // OR recording - // h/w monitoring not in use + // AND software monitoring required - (!Config->get_use_hardware_monitoring() && - - // AND software monitoring required - - Config->get_use_sw_monitoring())) { + (Config->get_monitoring_model() == SoftwareMonitoring)) { if (apply_gain_automation) { @@ -429,7 +502,7 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, for (n = 0; n < nbufs; ++n) { Sample *sp = bufs[n]; - for (jack_nframes_t nx = 0; nx < nframes; ++nx) { + for (nframes_t nx = 0; nx < nframes; ++nx) { sp[nx] *= -gab[nx]; } } @@ -437,7 +510,7 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, for (n = 0; n < nbufs; ++n) { Sample *sp = bufs[n]; - for (jack_nframes_t nx = 0; nx < nframes; ++nx) { + for (nframes_t nx = 0; nx < nframes; ++nx) { sp[nx] *= gab[nx]; } } @@ -474,7 +547,7 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, for (n = 0; n < nbufs; ++n) { Sample *sp = bufs[n]; - apply_gain_to_buffer(sp,nframes,this_gain); + Session::apply_gain_to_buffer(sp,nframes,this_gain); } } else if (_gain == 0) { @@ -506,7 +579,13 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, case PreFader: break; case PostFader: - (*i)->run (bufs, nbufs, nframes, offset); + if (dsg == 0) { + if (boost::dynamic_pointer_cast(*i) || boost::dynamic_pointer_cast(*i)) { + (*i)->silence (nframes, offset); + } + } else { + (*i)->run (bufs, nbufs, nframes, offset); + } break; } } @@ -525,7 +604,7 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, } if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_control_outs) { - apply_declick (bufs, nbufs, nframes, mute_gain, dmg, _phase_invert); + apply_declick (bufs, nbufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } @@ -553,10 +632,10 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, // recording but not s/w monitoring - (no_monitor && record_enabled() && (!_session.get_auto_input() || _session.actively_recording())) + (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording())) ) { - + co->silence (nframes, offset); } else { @@ -570,7 +649,7 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, ----------------------------------------------------------------------*/ if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_main_outs) { - apply_declick (bufs, nbufs, nframes, mute_gain, dmg, _phase_invert); + apply_declick (bufs, nbufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } @@ -586,7 +665,7 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, /* relax */ - } else if (no_monitor && record_enabled() && (!_session.get_auto_input() || _session.actively_recording())) { + } else if (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording())) { IO::silence (nframes, offset); @@ -598,7 +677,7 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, // muted by solo of another track, but not using control outs for solo - (!solo_audible && (_session.solo_model() != Session::SoloBus)) || + (!solo_audible && (Config->get_solo_model() != SoloBus)) || // muted by mute of this track @@ -613,12 +692,14 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, if (_meter_point == MeterPostFader) { reset_peak_meters (); } - + IO::silence (nframes, offset); } else { - if (_session.transport_speed() > 1.5f || _session.transport_speed() < -1.5f) { + if ((_session.transport_speed() > 1.5f || + _session.transport_speed() < -1.5f) && + Config->get_quieten_at_speed()) { pan (bufs, nbufs, nframes, offset, speed_quietning); } else { // cerr << _name << " panner state = " << _panner->automation_state() << endl; @@ -639,7 +720,6 @@ Route::process_output_buffers (vector& bufs, uint32_t nbufs, -------------------------------------------------------------------------------------------------- */ if (meter && (_meter_point == MeterPostFader)) { -// cerr << "meter post" << endl; if ((_gain == 0 && !apply_gain_automation) || dmg == 0) { uint32_t no = n_outputs(); @@ -663,7 +743,7 @@ Route::n_process_buffers () void -Route::passthru (jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t nframes, jack_nframes_t offset, int declick, bool meter_first) +Route::passthru (nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset, int declick, bool meter_first) { vector& bufs = _session.get_passthru_buffers(); uint32_t limit = n_process_buffers (); @@ -693,8 +773,17 @@ Route::set_phase_invert (bool yn, void *src) { if (_phase_invert != yn) { _phase_invert = yn; + // phase_invert_changed (src); /* EMIT SIGNAL */ + } +} + +void +Route::set_denormal_protection (bool yn, void *src) +{ + if (_denormal_protection != yn) { + _denormal_protection = yn; + // denormal_protection_changed (src); /* EMIT SIGNAL */ } - // phase_invert_changed (src); /* EMIT SIGNAL */ } void @@ -805,7 +894,10 @@ Route::add_redirect (boost::shared_ptr redirect, void *src, uint32_t* _peak_power.push_back(0); } while (_visible_peak_power.size() < potential_max_streams) { - _visible_peak_power.push_back(0); + _visible_peak_power.push_back(-INFINITY); + } + while (_max_peak_power.size() < potential_max_streams) { + _max_peak_power.push_back(-INFINITY); } _redirects.push_back (redirect); @@ -834,6 +926,8 @@ Route::add_redirects (const RedirectList& others, void *src, uint32_t* err_strea { uint32_t old_rmo = redirect_max_outs; + assert (ports_legal); + if (!_session.engine().connected()) { return 1; } @@ -863,7 +957,10 @@ Route::add_redirects (const RedirectList& others, void *src, uint32_t* err_strea _peak_power.push_back(0); } while (_visible_peak_power.size() < potential_max_streams) { - _visible_peak_power.push_back(0); + _visible_peak_power.push_back(-INFINITY); + } + while (_max_peak_power.size() < potential_max_streams) { + _max_peak_power.push_back(-INFINITY); } _redirects.push_back (*i); @@ -888,10 +985,13 @@ Route::add_redirects (const RedirectList& others, void *src, uint32_t* err_strea return 0; } +/** Remove redirects with a given placement. + * @param p Placement of redirects to remove. + */ void -Route::clear_redirects (void *src) +Route::clear_redirects (Placement p, void *src) { - uint32_t old_rmo = redirect_max_outs; + const uint32_t old_rmo = redirect_max_outs; if (!_session.engine().connected()) { return; @@ -899,9 +999,22 @@ Route::clear_redirects (void *src) { Glib::RWLock::WriterLock lm (redirect_lock); - _redirects.clear (); + RedirectList new_list; + + for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + if ((*i)->placement() == p) { + /* it's the placement we want to get rid of */ + (*i)->drop_references (); + } else { + /* it's a different placement, so keep it */ + new_list.push_back (*i); + } + } + + _redirects = new_list; } + /* FIXME: can't see how this test can ever fire */ if (redirect_max_outs != old_rmo) { reset_panner (); } @@ -916,6 +1029,8 @@ Route::remove_redirect (boost::shared_ptr redirect, void *src, uint32_ { uint32_t old_rmo = redirect_max_outs; + assert (ports_legal); + if (!_session.engine().connected()) { return 1; } @@ -995,6 +1110,8 @@ Route::remove_redirect (boost::shared_ptr redirect, void *src, uint32_ reset_panner (); } + redirect->drop_references (); + redirects_changed (src); /* EMIT SIGNAL */ return 0; } @@ -1014,7 +1131,7 @@ Route::_reset_plugin_counts (uint32_t* err_streams) uint32_t i_cnt; uint32_t s_cnt; map > insert_map; - jack_nframes_t initial_streams; + nframes_t initial_streams; redirect_max_outs = 0; i_cnt = 0; @@ -1110,9 +1227,16 @@ Route::_reset_plugin_counts (uint32_t* err_streams) } else { s->expect_inputs ((*prev)->output_streams()); } - } - redirect_max_outs = max ((*r)->output_streams (), redirect_max_outs); + } else { + + /* don't pay any attention to send output configuration, since it doesn't + affect the route. + */ + + redirect_max_outs = max ((*r)->output_streams (), redirect_max_outs); + + } } /* we're done */ @@ -1152,7 +1276,13 @@ Route::check_some_plugin_counts (list& iclist, int32_t required_inp } (*i).in = required_inputs; - (*i).out = (*i).insert->compute_output_streams ((*i).cnt); + + if (((*i).out = (*i).insert->compute_output_streams ((*i).cnt)) < 0) { + if (err_streams) { + *err_streams = required_inputs; + } + return -1; + } required_inputs = (*i).out; } @@ -1260,8 +1390,12 @@ Route::all_redirects_flip () } } +/** Set all redirects with a given placement to a given active state. + * @param p Placement of redirects to change. + * @param state New active state for those redirects. + */ void -Route::all_redirects_active (bool state) +Route::all_redirects_active (Placement p, bool state) { Glib::RWLock::ReaderLock lm (redirect_lock); @@ -1270,7 +1404,9 @@ Route::all_redirects_active (bool state) } for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { - (*i)->set_active (state, this); + if ((*i)->placement() == p) { + (*i)->set_active (state, this); + } } } @@ -1323,21 +1459,19 @@ XMLNode& Route::state(bool full_state) { XMLNode *node = new XMLNode("Route"); - XMLNode *aevents; RedirectList:: iterator i; char buf[32]; if (_flags) { - snprintf (buf, sizeof (buf), "0x%x", _flags); - node->add_property("flags", buf); + node->add_property("flags", enum_2_string (_flags)); } node->add_property("default-type", _default_type.to_string()); - node->add_property("active", _active?"yes":"no"); node->add_property("muted", _muted?"yes":"no"); node->add_property("soloed", _soloed?"yes":"no"); node->add_property("phase-invert", _phase_invert?"yes":"no"); + node->add_property("denormal-protection", _denormal_protection?"yes":"no"); node->add_property("mute-affects-pre-fader", _mute_affects_pre_fader?"yes":"no"); node->add_property("mute-affects-post-fader", _mute_affects_post_fader?"yes":"no"); node->add_property("mute-affects-control-outs", _mute_affects_control_outs?"yes":"no"); @@ -1354,7 +1488,7 @@ Route::state(bool full_state) OrderKeys::iterator x = order_keys.begin(); while (x != order_keys.end()) { - order_string += (*x).first; + order_string += string ((*x).first); order_string += '='; snprintf (buf, sizeof(buf), "%ld", (*x).second); order_string += buf; @@ -1370,6 +1504,13 @@ Route::state(bool full_state) node->add_property ("order-keys", order_string); node->add_child_nocopy (IO::state (full_state)); + node->add_child_nocopy (_solo_control.get_state ()); + node->add_child_nocopy (_mute_control.get_state ()); + + XMLNode* remote_control_node = new XMLNode (X_("remote_control")); + snprintf (buf, sizeof (buf), "%d", _remote_control_id); + remote_control_node->add_property (X_("id"), buf); + node->add_child_nocopy (*remote_control_node); if (_control_outs) { XMLNode* cnode = new XMLNode (X_("ControlOuts")); @@ -1382,26 +1523,6 @@ Route::state(bool full_state) cmt->add_content (_comment); } - if (full_state) { - string path; - - path = _session.snap_name(); - path += "-gain-"; - path += legalize_for_path (_name); - path += ".automation"; - - /* XXX we didn't ask for a state save, we asked for the current state. - FIX ME! - */ - - if (save_automation (path)) { - error << _("Could not get state of route. Problem with save_automation") << endmsg; - } - - aevents = node->add_child ("Automation"); - aevents->add_property ("path", path); - } - for (i = _redirects.begin(); i != _redirects.end(); ++i) { node->add_child_nocopy((*i)->state (full_state)); } @@ -1457,22 +1578,30 @@ Route::add_redirect_from_xml (const XMLNode& node) if ((prop = node.property ("type")) != 0) { boost::shared_ptr insert; + bool have_insert = false; - if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "vst") { - + if (prop->value() == "ladspa" || prop->value() == "Ladspa" || + prop->value() == "lv2" || + prop->value() == "vst" || + prop->value() == "audiounit") { + insert.reset (new PluginInsert(_session, node)); + have_insert = true; } else if (prop->value() == "port") { insert.reset (new PortInsert (_session, node)); + have_insert = true; } else { error << string_compose(_("unknown Insert type \"%1\"; ignored"), prop->value()) << endmsg; } - add_redirect (insert, this); + if (have_insert) { + add_redirect (insert, this); + } } else { error << _("Insert XML node has no type property") << endmsg; @@ -1488,6 +1617,12 @@ Route::add_redirect_from_xml (const XMLNode& node) int Route::set_state (const XMLNode& node) +{ + return _set_state (node, true); +} + +int +Route::_set_state (const XMLNode& node, bool call_base) { XMLNodeList nlist; XMLNodeConstIterator niter; @@ -1500,28 +1635,26 @@ Route::set_state (const XMLNode& node) return -1; } - if ((prop = node.property ("flags")) != 0) { - int x; - sscanf (prop->value().c_str(), "0x%x", &x); - _flags = Flag (x); + if ((prop = node.property (X_("flags"))) != 0) { + _flags = Flag (string_2_enum (prop->value(), _flags)); } else { _flags = Flag (0); } - if ((prop = node.property ("default-type")) != 0) { + if ((prop = node.property (X_("default-type"))) != 0) { _default_type = DataType(prop->value()); assert(_default_type != DataType::NIL); } - if ((prop = node.property ("phase-invert")) != 0) { - set_phase_invert(prop->value()=="yes"?true:false, this); + if ((prop = node.property (X_("phase-invert"))) != 0) { + set_phase_invert (prop->value()=="yes"?true:false, this); } - if ((prop = node.property ("active")) != 0) { - set_active (prop->value() == "yes"); + if ((prop = node.property (X_("denormal-protection"))) != 0) { + set_denormal_protection (prop->value()=="yes"?true:false, this); } - if ((prop = node.property ("muted")) != 0) { + if ((prop = node.property (X_("muted"))) != 0) { bool yn = prop->value()=="yes"?true:false; /* force reset of mute status */ @@ -1531,7 +1664,7 @@ Route::set_state (const XMLNode& node) mute_gain = desired_mute_gain; } - if ((prop = node.property ("soloed")) != 0) { + if ((prop = node.property (X_("soloed"))) != 0) { bool yn = prop->value()=="yes"?true:false; /* force reset of solo status */ @@ -1541,23 +1674,23 @@ Route::set_state (const XMLNode& node) solo_gain = desired_solo_gain; } - if ((prop = node.property ("mute-affects-pre-fader")) != 0) { + if ((prop = node.property (X_("mute-affects-pre-fader"))) != 0) { _mute_affects_pre_fader = (prop->value()=="yes")?true:false; } - if ((prop = node.property ("mute-affects-post-fader")) != 0) { + if ((prop = node.property (X_("mute-affects-post-fader"))) != 0) { _mute_affects_post_fader = (prop->value()=="yes")?true:false; } - if ((prop = node.property ("mute-affects-control-outs")) != 0) { + if ((prop = node.property (X_("mute-affects-control-outs"))) != 0) { _mute_affects_control_outs = (prop->value()=="yes")?true:false; } - if ((prop = node.property ("mute-affects-main-outs")) != 0) { + if ((prop = node.property (X_("mute-affects-main-outs"))) != 0) { _mute_affects_main_outs = (prop->value()=="yes")?true:false; } - if ((prop = node.property ("edit-group")) != 0) { + if ((prop = node.property (X_("edit-group"))) != 0) { RouteGroup* edit_group = _session.edit_group_by_name(prop->value()); if(edit_group == 0) { error << string_compose(_("Route %1: unknown edit group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg; @@ -1566,7 +1699,7 @@ Route::set_state (const XMLNode& node) } } - if ((prop = node.property ("order-keys")) != 0) { + if ((prop = node.property (X_("order-keys"))) != 0) { long n; @@ -1583,7 +1716,7 @@ Route::set_state (const XMLNode& node) error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining) << endmsg; } else { - set_order_key (remaining.substr (0, equal), n); + set_order_key (remaining.substr (0, equal).c_str(), n); } } @@ -1603,7 +1736,7 @@ Route::set_state (const XMLNode& node) delete deferred_state; } - deferred_state = new XMLNode("deferred state"); + deferred_state = new XMLNode(X_("deferred state")); /* set parent class properties before anything else */ @@ -1611,54 +1744,39 @@ Route::set_state (const XMLNode& node) child = *niter; - if (child->name() == IO::state_node_name) { + if (child->name() == IO::state_node_name && call_base) { IO::set_state (*child); break; } } - - for (niter = nlist.begin(); niter != nlist.end(); ++niter){ - - child = *niter; - - if (child->name() == "Send") { - - - if (!IO::ports_legal) { - deferred_state->add_child_copy (*child); - } else { - add_redirect_from_xml (*child); - } - - } else if (child->name() == "Insert") { - - if (!IO::ports_legal) { - - deferred_state->add_child_copy (*child); - - } else { - - add_redirect_from_xml (*child); - } + XMLNodeList redirect_nodes; + + for (niter = nlist.begin(); niter != nlist.end(); ++niter){ + + child = *niter; + + if (child->name() == X_("Send") || child->name() == X_("Insert")) { + redirect_nodes.push_back(child); + } + + } + + _set_redirect_states (redirect_nodes); - } else if (child->name() == "Automation") { + for (niter = nlist.begin(); niter != nlist.end(); ++niter){ + child = *niter; + // All redirects (sends and inserts) have been applied already - XMLPropertyList plist; - XMLPropertyConstIterator piter; - XMLProperty *prop; + if (child->name() == X_("Automation")) { - plist = child->properties(); - for (piter = plist.begin(); piter != plist.end(); ++piter) { - prop = *piter; - if (prop->name() == "path") { - load_automation (prop->value()); - } + if ((prop = child->property (X_("path"))) != 0) { + load_automation (prop->value()); } - } else if (child->name() == "ControlOuts") { + } else if (child->name() == X_("ControlOuts")) { string coutname = _name; coutname += _("[control]"); @@ -1666,19 +1784,38 @@ Route::set_state (const XMLNode& node) _control_outs = new IO (_session, coutname); _control_outs->set_state (**(child->children().begin())); - } else if (child->name() == "Comment") { + } else if (child->name() == X_("Comment")) { /* XXX this is a terrible API design in libxml++ */ XMLNode *cmt = *(child->children().begin()); _comment = cmt->content(); - } else if (child->name() == "extra") { + } else if (child->name() == X_("extra")) { + _extra_xml = new XMLNode (*child); + + } else if (child->name() == X_("controllable") && (prop = child->property("name")) != 0) { + + if (prop->value() == "solo") { + _solo_control.set_state (*child); + _session.add_controllable (&_solo_control); + } + else if (prop->value() == "mute") { + _mute_control.set_state (*child); + _session.add_controllable (&_mute_control); + } + } + else if (child->name() == X_("remote_control")) { + if ((prop = child->property (X_("id"))) != 0) { + int32_t x; + sscanf (prop->value().c_str(), "%d", &x); + set_remote_control_id (x); + } } } - if ((prop = node.property ("mix-group")) != 0) { + if ((prop = node.property (X_("mix-group"))) != 0) { RouteGroup* mix_group = _session.mix_group_by_name(prop->value()); if (mix_group == 0) { error << string_compose(_("Route %1: unknown mix group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg; @@ -1690,6 +1827,110 @@ Route::set_state (const XMLNode& node) return 0; } +void +Route::_set_redirect_states(const XMLNodeList &nlist) +{ + XMLNodeConstIterator niter; + char buf[64]; + + RedirectList::iterator i, o; + + if (!ports_legal) { + + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + deferred_state->add_child_copy (**niter); + } + + return; + } + + // Iterate through existing redirects, remove those which are not in the state list + for (i = _redirects.begin(); i != _redirects.end(); ) { + RedirectList::iterator tmp = i; + ++tmp; + + bool redirectInStateList = false; + + (*i)->id().print (buf, sizeof (buf)); + + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + + if (strncmp (buf,(*niter)->child(X_("Redirect"))->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) { + redirectInStateList = true; + break; + } + } + + if (!redirectInStateList) { + remove_redirect ( *i, this); + } + + + i = tmp; + } + + + // Iterate through state list and make sure all redirects are on the track and in the correct order, + // set the state of existing redirects according to the new state on the same go + i = _redirects.begin(); + for (niter = nlist.begin(); niter != nlist.end(); ++niter, ++i) { + + // Check whether the next redirect in the list + o = i; + + while (o != _redirects.end()) { + (*o)->id().print (buf, sizeof (buf)); + if ( strncmp(buf, (*niter)->child(X_("Redirect"))->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) + break; + ++o; + } + + if (o == _redirects.end()) { + // If the redirect (*niter) is not on the route, we need to create it + // and move it to the correct location + + RedirectList::iterator prev_last = _redirects.end(); + --prev_last; // We need this to check whether adding succeeded + + add_redirect_from_xml (**niter); + + RedirectList::iterator last = _redirects.end(); + --last; + + if (prev_last == last) { + warning << _name << ": could not fully restore state as some redirects were not possible to create" << endmsg; + continue; + + } + + boost::shared_ptr tmp = (*last); + // remove the redirect from the wrong location + _redirects.erase(last); + // insert the new redirect at the current location + _redirects.insert(i, tmp); + + --i; // move pointer to the newly inserted redirect + continue; + } + + // We found the redirect (*niter) on the route, first we must make sure the redirect + // is at the location provided in the XML state + if (i != o) { + boost::shared_ptr tmp = (*o); + // remove the old copy + _redirects.erase(o); + // insert the redirect at the correct location + _redirects.insert(i, tmp); + + --i; // move pointer so it points to the right redirect + } + + (*i)->set_state( (**niter) ); + } + + redirects_changed(this); +} + void Route::curve_reallocate () { @@ -1698,7 +1939,7 @@ Route::curve_reallocate () } void -Route::silence (jack_nframes_t nframes, jack_nframes_t offset) +Route::silence (nframes_t nframes, nframes_t offset) { if (!_silent) { @@ -1738,11 +1979,17 @@ Route::set_control_outs (const vector& ports) { Glib::Mutex::Lock lm (control_outs_lock); vector::const_iterator i; + uint32_t limit; if (_control_outs) { delete _control_outs; _control_outs = 0; } + + if (control() || master()) { + /* no control outs for these two special busses */ + return 0; + } if (ports.empty()) { return 0; @@ -1757,7 +2004,20 @@ Route::set_control_outs (const vector& ports) have outputs. we track the changes in ::output_change_handler(). */ - _control_outs->ensure_io (0, n_outputs(), true, this); + limit = n_outputs (); + + if (_control_outs->ensure_io (0, limit, true, this)) { + return -1; + } + + /* now connect to the named ports */ + + for (uint32_t n = 0; n < limit; ++n) { + if (_control_outs->connect_output (_control_outs->output (n), ports[n % ports.size()], this)) { + error << string_compose (_("could not connect %1 to %2"), _control_outs->output(n)->name(), ports[n]) << endmsg; + return -1; + } + } return 0; } @@ -1925,17 +2185,10 @@ Route::get_mute_config (mute_type t) return onoff; } -void -Route::set_active (bool yn) -{ - _active = yn; - active_changed(); /* EMIT SIGNAL */ -} - void Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_flush_redirects) { - jack_nframes_t now = _session.transport_frame(); + nframes_t now = _session.transport_frame(); { Glib::RWLock::ReaderLock lm (redirect_lock); @@ -1960,19 +2213,6 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f _roll_delay = _initial_delay; } -UndoAction -Route::get_memento() const -{ - void (Route::*pmf)(state_id_t) = &Route::set_state; - return sigc::bind (mem_fun (*(const_cast(this)), pmf), _current_state_id); -} - -void -Route::set_state (state_id_t id) -{ - return; -} - void Route::input_change_handler (IOChange change, void *ignored) { @@ -2004,7 +2244,7 @@ Route::pans_required () const } int -Route::no_roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t offset, +Route::no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset, bool session_state_changing, bool can_record, bool rec_monitors_input) { if (n_outputs() == 0) { @@ -2027,8 +2267,8 @@ Route::no_roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes return 0; } -jack_nframes_t -Route::check_initial_delay (jack_nframes_t nframes, jack_nframes_t& offset, jack_nframes_t& transport_frame) +nframes_t +Route::check_initial_delay (nframes_t nframes, nframes_t& offset, nframes_t& transport_frame) { if (_roll_delay > nframes) { @@ -2053,7 +2293,7 @@ Route::check_initial_delay (jack_nframes_t nframes, jack_nframes_t& offset, jack } int -Route::roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t offset, int declick, +Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset, int declick, bool can_record, bool rec_monitors_input) { { @@ -2064,13 +2304,13 @@ Route::roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t automation_snapshot (_session.transport_frame()); } } - + if ((n_outputs() == 0 && _redirects.empty()) || n_inputs() == 0 || !_active) { silence (nframes, offset); return 0; } - jack_nframes_t unused = 0; + nframes_t unused = 0; if ((nframes = check_initial_delay (nframes, offset, unused)) == 0) { return 0; @@ -2085,7 +2325,7 @@ Route::roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t if (am.locked() && _session.transport_rolling()) { - jack_nframes_t start_frame = end_frame - nframes; + nframes_t start_frame = end_frame - nframes; if (gain_automation_playback()) { apply_gain_automation = _gain_automation_curve.rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes); @@ -2099,7 +2339,7 @@ Route::roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t } int -Route::silent_roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t offset, +Route::silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset, bool can_record, bool rec_monitors_input) { silence (nframes, offset); @@ -2110,7 +2350,7 @@ void Route::toggle_monitor_input () { for (vector::iterator i = _inputs.begin(); i != _inputs.end(); ++i) { - (*i)->request_monitor_input(!(*i)->monitoring_input()); + (*i)->ensure_monitor_input(!(*i)->monitoring_input()); } } @@ -2166,7 +2406,7 @@ Route::set_meter_point (MeterPoint p, void *src) } } -jack_nframes_t +nframes_t Route::update_total_latency () { _own_latency = 0; @@ -2192,7 +2432,7 @@ Route::update_total_latency () } void -Route::set_latency_delay (jack_nframes_t longest_session_latency) +Route::set_latency_delay (nframes_t longest_session_latency) { _initial_delay = longest_session_latency - _own_latency; @@ -2202,7 +2442,7 @@ Route::set_latency_delay (jack_nframes_t longest_session_latency) } void -Route::automation_snapshot (jack_nframes_t now) +Route::automation_snapshot (nframes_t now) { IO::automation_snapshot (now); @@ -2211,8 +2451,8 @@ Route::automation_snapshot (jack_nframes_t now) } } -Route::ToggleControllable::ToggleControllable (Route& s, ToggleType tp) - : route (s), type(tp) +Route::ToggleControllable::ToggleControllable (std::string name, Route& s, ToggleType tp) + : Controllable (name), route (s), type(tp) { } @@ -2254,7 +2494,7 @@ Route::ToggleControllable::get_value (void) const } void -Route::set_block_size (jack_nframes_t nframes) +Route::set_block_size (nframes_t nframes) { for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { (*i)->set_block_size (nframes); @@ -2272,8 +2512,9 @@ Route::protect_automation () { switch (gain_automation_state()) { case Write: - case Touch: set_gain_automation_state (Off); + case Touch: + set_gain_automation_state (Play); break; default: break; @@ -2281,9 +2522,11 @@ Route::protect_automation () switch (panner().automation_state ()) { case Write: - case Touch: panner().set_automation_state (Off); break; + case Touch: + panner().set_automation_state (Play); + break; default: break; }