X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=02005fc269e4ab67aa572108af0e10cfc3302ba1;hb=3a27410dcbaaca10480ffc14770b41c11e9ca1da;hp=67ff44a70990bdf8d6c862395f0146cd0a56f7f5;hpb=ace07c80a81a631eb8ff95155e5ace2751838809;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 67ff44a709..02005fc269 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2000 Paul Davis + Copyright (C) 2000 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -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,7 +37,11 @@ #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" +#include "ardour/internal_return.h" #include "ardour/ladspa_plugin.h" #include "ardour/meter.h" #include "ardour/mix.h" @@ -61,94 +65,135 @@ 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) - : IO (sess, name, default_type) + : SessionObject (sess, name) + , AutomatableControls (sess) , _flags (flg) - , _solo_control (new ToggleControllable (X_("solo"), *this, ToggleControllable::SoloControl)) - , _mute_control (new ToggleControllable (X_("mute"), *this, ToggleControllable::MuteControl)) + , _solo_control (new SoloControllable (X_("solo"), *this)) + , _mute_control (new MuteControllable (X_("mute"), *this)) + , _mute_master (new MuteMaster (sess, name)) + , _default_type (default_type) + { init (); + /* add standard processors other than amp (added by ::init()) */ + + _meter.reset (new PeakMeter (_session)); + _meter->set_display_to_user (false); + add_processor (_meter, PostFader); + + 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); + + /* now that we have _meter, its safe to connect to this */ + + Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this))); } Route::Route (Session& sess, const XMLNode& node, DataType default_type) - : IO (sess, *node.child ("IO"), default_type) - , _solo_control (new ToggleControllable (X_("solo"), *this, ToggleControllable::SoloControl)) - , _mute_control (new ToggleControllable (X_("mute"), *this, ToggleControllable::MuteControl)) + : SessionObject (sess, "toBeReset") + , AutomatableControls (sess) + , _solo_control (new SoloControllable (X_("solo"), *this)) + , _mute_control (new MuteControllable (X_("mute"), *this)) + , _mute_master (new MuteMaster (sess, "toBeReset")) + , _default_type (default_type) { init (); - _set_state (node, false); + + _set_state (node, Stateful::loading_state_version, false); + + /* now that we have _meter, its safe to connect to this */ + + Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this))); } void Route::init () { - processor_max_streams.reset(); - _muted = false; - _soloed = false; + _self_solo = false; + _soloed_by_others = 0; + _solo_isolated = 0; _solo_safe = false; - _recordable = true; _active = true; - _phase_invert = false; - _denormal_protection = false; - order_keys[strdup (N_("signal"))] = order_key_cnt++; + processor_max_streams.reset(); + _recordable = true; + order_keys[N_("signal")] = order_key_cnt++; _silent = false; _meter_point = MeterPostFader; _initial_delay = 0; _roll_delay = 0; - _own_latency = 0; - _user_latency = 0; _have_internal_generator = false; _declickable = false; _pending_declick = true; _remote_control_id = 0; _in_configure_processors = false; - - _edit_group = 0; - _mix_group = 0; + _mute_points = MuteMaster::AllPoints; - _mute_affects_pre_fader = Config->get_mute_affects_pre_fader(); - _mute_affects_post_fader = Config->get_mute_affects_post_fader(); - _mute_affects_control_outs = Config->get_mute_affects_control_outs(); - _mute_affects_main_outs = Config->get_mute_affects_main_outs(); - - solo_gain = 1.0; - desired_solo_gain = 1.0; - mute_gain = 1.0; - desired_mute_gain = 1.0; + _phase_invert = 0; + _denormal_protection = false; + + /* add standard controls */ - input_changed.connect (mem_fun (this, &Route::input_change_handler)); - output_changed.connect (mem_fun (this, &Route::output_change_handler)); + _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle)); + _mute_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle)); - /* add standard processors: amp, meter, main outs */ + add_control (_solo_control); + add_control (_mute_control); + + /* input and output objects */ + + _input.reset (new IO (_session, _name, IO::Input, _default_type)); + _output.reset (new IO (_session, _name, IO::Output, _default_type)); - /* amp & meter belong to IO but need to be added to our processor list */ + _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)); + /* add amp processor */ + + _amp.reset (new Amp (_session, _mute_master)); add_processor (_amp, PostFader); - add_processor (_meter, PreFader); - - _main_outs.reset (new Delivery (_session, this, _name, Delivery::Main)); - add_processor (_main_outs, PostFader); } Route::~Route () { - clear_processors (PreFader); - clear_processors (PostFader); + 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 + */ - for (OrderKeys::iterator i = order_keys.begin(); i != order_keys.end(); ++i) { - free ((void*)(i->first)); + 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 (); + } } } @@ -159,35 +204,36 @@ Route::remote_control_id() const } long -Route::order_key (const char* name) const +Route::order_key (std::string const & name) const { - OrderKeys::const_iterator i; - - for (i = order_keys.begin(); i != order_keys.end(); ++i) { - if (!strcmp (name, i->first)) { - return i->second; - } + OrderKeys::const_iterator i = order_keys.find (name); + if (i == order_keys.end()) { + return -1; } - return -1; + return i->second; } void -Route::set_order_key (const char* name, long n) +Route::set_order_key (std::string const & name, long n) { - order_keys[strdup(name)] = n; + order_keys[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 (); } +/** Set all order keys to be the same as that for `base', if such a key + * exists in this route. + * @param base Base key. + */ void -Route::sync_order_keys (const char* base) +Route::sync_order_keys (std::string const & base) { if (order_keys.empty()) { return; @@ -224,24 +270,25 @@ Route::ensure_track_or_route_name(string name, Session &session) return newname; } + void Route::inc_gain (gain_t fraction, void *src) { - IO::inc_gain (fraction, src); + _amp->inc_gain (fraction, src); } void Route::set_gain (gain_t val, void *src) { - if (src != 0 && _mix_group && src != _mix_group && _mix_group->is_active()) { - - if (_mix_group->is_relative()) { - - gain_t usable_gain = gain(); + if (src != 0 && _route_group && src != _route_group && _route_group->active_property (RouteGroup::Gain)) { + + if (_route_group->is_relative()) { + + gain_t usable_gain = _amp->gain(); if (usable_gain < 0.000001f) { usable_gain = 0.000001f; } - + gain_t delta = val; if (delta < 0.000001f) { delta = 0.000001f; @@ -255,40 +302,40 @@ Route::set_gain (gain_t val, void *src) gain_t factor = delta / usable_gain; if (factor > 0.0f) { - factor = _mix_group->get_max_factor(factor); + factor = _route_group->get_max_factor(factor); if (factor == 0.0f) { - _gain_control->Changed(); /* EMIT SIGNAL */ + _amp->gain_control()->Changed(); /* EMIT SIGNAL */ return; } } else { - factor = _mix_group->get_min_factor(factor); + factor = _route_group->get_min_factor(factor); if (factor == 0.0f) { - _gain_control->Changed(); /* EMIT SIGNAL */ + _amp->gain_control()->Changed(); /* EMIT SIGNAL */ return; } } - - _mix_group->apply (&Route::inc_gain, factor, _mix_group); + + _route_group->apply (&Route::inc_gain, factor, _route_group); } else { - - _mix_group->apply (&Route::set_gain, val, _mix_group); + + _route_group->apply (&Route::set_gain, val, _route_group); } return; - } + } - if (val == gain()) { + if (val == _amp->gain()) { return; } - IO::set_gain (val, src); + _amp->set_gain (val, src); } /** Process this route for one (sub) cycle (process thread) * * @param bufs Scratch buffers to use for the signal path - * @param start_frame Initial transport frame + * @param start_frame Initial transport frame * @param end_frame Final transport frame * @param nframes Number of frames to output (to ports) * @@ -298,204 +345,214 @@ Route::set_gain (gain_t val, void *src) void Route::process_output_buffers (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, - bool with_processors, int declick) + bool /*with_processors*/, int declick) { - ProcessorList::iterator i; - bool mute_declick_applied = false; - gain_t dmg, dsg, dg; - bool no_monitor; + bool monitor; - bufs.is_silent(false); + bufs.is_silent (false); switch (Config->get_monitoring_model()) { case HardwareMonitoring: case ExternalMonitoring: - no_monitor = true; + monitor = !record_enabled() || (_session.config.get_auto_input() && !_session.actively_recording()); break; default: - no_monitor = false; + monitor = true; } - declick = _pending_declick; - - const bool recording_without_monitoring = no_monitor && record_enabled() - && (!_session.config.get_auto_input() || _session.actively_recording()); - - - /* ------------------------------------------------------------------------------------------- - SET UP GAIN (FADER) - ----------------------------------------------------------------------------------------- */ - - { - Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK); - - if (dm.locked()) { - dmg = desired_mute_gain; - dsg = desired_solo_gain; - dg = _gain_control->user_float(); - } else { - dmg = mute_gain; - dsg = solo_gain; - dg = _gain; - } + if (!declick) { + declick = _pending_declick; } - - // apply gain at the amp if... - _amp->apply_gain( - // we're not recording - !(record_enabled() && _session.actively_recording()) - // or (we are recording, and) software monitoring is required - || Config->get_monitoring_model() == SoftwareMonitoring); - - // mute at the amp if... - _amp->apply_mute (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_post_fader, - mute_gain, dmg); - - _amp->set_gain (_gain, dg); - - - /* ------------------------------------------------------------------------------------------- - SET UP CONTROL OUTPUTS - ----------------------------------------------------------------------------------------- */ - boost::shared_ptr co = _control_outs; - if (co) { - // deliver control outputs unless we're ... - bool self_mute = ((dmg == 0 && _mute_affects_control_outs) || // or muted by mute of this track - !recording_without_monitoring); // or rec-enabled w/o s/w monitoring - bool other_mute = (dsg == 0); // muted by solo of another track - - co->set_self_mute (self_mute); - co->set_nonself_mute (other_mute); - } + /* figure out if we're going to use gain automation */ + _amp->setup_gain_automation (start_frame, end_frame, nframes); - /* ------------------------------------------------------------------------------------------- - SET UP MAIN OUTPUT STAGE - ----------------------------------------------------------------------------------------- */ - bool solo_audible = dsg > 0; - bool mute_audible = dmg > 0 || !_mute_affects_main_outs; + /* tell main outs what to do about monitoring */ + _main_outs->no_outs_cuz_we_no_monitor (!monitor); - bool silent_anyway = (_gain == 0 && !_amp->apply_gain_automation()); - bool muted_by_other_solo = (!solo_audible && (Config->get_solo_model() != SoloBus)); - bool muted_by_self = !mute_audible; - _main_outs->set_nonself_mute (recording_without_monitoring || muted_by_other_solo || silent_anyway); - _main_outs->set_self_mute (muted_by_self); - /* ------------------------------------------------------------------------------------------- GLOBAL DECLICK (for transport changes etc.) ----------------------------------------------------------------------------------------- */ if (declick > 0) { - Amp::apply_gain (bufs, nframes, 0.0, 1.0, false); - _pending_declick = 0; + Amp::apply_gain (bufs, nframes, 0.0, 1.0); } else if (declick < 0) { - Amp::apply_gain (bufs, nframes, 1.0, 0.0, false); - _pending_declick = 0; - } else { // no global declick - if (solo_gain != dsg) { - Amp::apply_gain (bufs, nframes, solo_gain, dsg, false); - solo_gain = dsg; - } + Amp::apply_gain (bufs, nframes, 1.0, 0.0); } + _pending_declick = 0; /* ------------------------------------------------------------------------------------------- - PRE-FADER MUTING + DENORMAL CONTROL/PHASE INVERT ----------------------------------------------------------------------------------------- */ - if (!_soloed && _mute_affects_pre_fader && (mute_gain != dmg)) { - Amp::apply_gain (bufs, nframes, mute_gain, dmg, false); - mute_gain = dmg; - mute_declick_applied = true; - } - if (mute_gain == 0.0f && dmg == 0.0f) { - bufs.is_silent(true); - } + if (_phase_invert) { + int chn = 0; - /* ------------------------------------------------------------------------------------------- - DENORMAL CONTROL - ----------------------------------------------------------------------------------------- */ + if (_denormal_protection || Config->get_denormal_protection()) { - if (_denormal_protection || Config->get_denormal_protection()) { + for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) { + Sample* const sp = i->data(); - for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) { - Sample* const sp = i->data(); - - for (nframes_t nx = 0; nx < nframes; ++nx) { - sp[nx] += 1.0e-27f; + if (_phase_invert & chn) { + for (nframes_t nx = 0; nx < nframes; ++nx) { + sp[nx] = -sp[nx]; + sp[nx] += 1.0e-27f; + } + } else { + for (nframes_t nx = 0; nx < nframes; ++nx) { + sp[nx] += 1.0e-27f; + } + } } - } - } - /* ------------------------------------------------------------------------------------------- - and go .... - ----------------------------------------------------------------------------------------- */ + } else { - Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK); + for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) { + Sample* const sp = i->data(); - if (rm.locked()) { - for (i = _processors.begin(); i != _processors.end(); ++i) { - bufs.set_count(ChanCount::max(bufs.count(), (*i)->input_streams())); - (*i)->run_in_place (bufs, start_frame, end_frame, nframes); - bufs.set_count(ChanCount::max(bufs.count(), (*i)->output_streams())); + if (_phase_invert & chn) { + for (nframes_t nx = 0; nx < nframes; ++nx) { + sp[nx] = -sp[nx]; + } + } + } } - if (!_processors.empty()) { - bufs.set_count(ChanCount::max(bufs.count(), _processors.back()->output_streams())); + } else { + + if (_denormal_protection || Config->get_denormal_protection()) { + + for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) { + Sample* const sp = i->data(); + for (nframes_t nx = 0; nx < nframes; ++nx) { + sp[nx] += 1.0e-27f; + } + } + } } /* ------------------------------------------------------------------------------------------- - POST-FADER MUTING + and go .... ----------------------------------------------------------------------------------------- */ - if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_main_outs) { - Amp::apply_gain (bufs, nframes, mute_gain, dmg, false); - mute_gain = dmg; - mute_declick_applied = true; - } + 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()); - if (mute_gain == 0.0f && dmg == 0.0f) { - bufs.is_silent(true); + (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back()); + bufs.set_count ((*i)->output_streams()); + } } - - // at this point we've reached the desired mute gain regardless - mute_gain = dmg; } ChanCount Route::n_process_buffers () { - return max (n_inputs(), processor_max_streams); -} - -void -Route::setup_peak_meters() -{ - ChanCount max_streams = std::max (_inputs.count(), _outputs.count()); - max_streams = std::max (max_streams, processor_max_streams); - _meter->configure_io (max_streams, max_streams); + return max (_input->n_ports(), processor_max_streams); } void Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick) { - BufferSet& bufs = _session.get_scratch_buffers(n_process_buffers()); + BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); _silent = false; - collect_input (bufs, nframes); + assert (bufs.available() >= _input->n_ports()); + + if (_input->n_ports() == ChanCount::ZERO) { + silence (nframes); + } + + bufs.set_count (_input->n_ports()); + + if (is_control() && _session.listening()) { + + /* 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 { + + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + + BufferSet::iterator o = bufs.begin(*t); + PortSet& ports (_input->ports()); + + for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) { + o->read_from (i->get_buffer(nframes), nframes); + } + } + } + write_out_of_band_data (bufs, start_frame, end_frame, nframes); process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick); } void Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick) { - process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick); + BufferSet& bufs (_session.get_silent_buffers (n_process_buffers())); + bufs.set_count (_input->n_ports()); + write_out_of_band_data (bufs, start_frame, end_frame, nframes); + process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick); +} + +void +Route::set_listen (bool yn, void* src) +{ + if (_control_outs) { + if (yn != _control_outs->active()) { + if (yn) { + _control_outs->activate (); + } else { + _control_outs->deactivate (); + } + + listen_changed (src); /* EMIT SIGNAL */ + } + } +} + +bool +Route::listening () const +{ + if (_control_outs) { + return _control_outs->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 @@ -505,104 +562,159 @@ Route::set_solo (bool yn, void *src) return; } - if (_mix_group && src != _mix_group && _mix_group->is_active()) { - _mix_group->apply (&Route::set_solo, yn, _mix_group); + if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Solo)) { + _route_group->apply (&Route::set_solo, yn, _route_group); return; } - if (_soloed != yn) { - _soloed = yn; + if (self_soloed() != yn) { + set_self_solo (yn); + set_delivery_solo (); solo_changed (src); /* EMIT SIGNAL */ _solo_control->Changed (); /* EMIT SIGNAL */ - } - - catch_up_on_solo_mute_override (); + } } void -Route::catch_up_on_solo_mute_override () +Route::set_self_solo (bool yn) { - if (Config->get_solo_model() != InverseMute) { - return; + _self_solo = yn; +} + +void +Route::mod_solo_by_others (int32_t delta) +{ + if (delta < 0) { + if (_soloed_by_others >= (uint32_t) delta) { + _soloed_by_others += delta; + } else { + _soloed_by_others = 0; + } + } else { + _soloed_by_others += delta; } - - { - Glib::Mutex::Lock lm (declick_lock); + + set_delivery_solo (); +} + +void +Route::set_delivery_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. + */ + + Glib::RWLock::ReaderLock rm (_processor_lock); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + boost::shared_ptr d; - if (_muted) { - if (Config->get_solo_mute_override()) { - desired_mute_gain = (_soloed?1.0:0.0); - } else { - desired_mute_gain = 0.0; - } - } else { - desired_mute_gain = 1.0; + if ((d = boost::dynamic_pointer_cast (*i)) != 0) { + d->set_solo_level (soloed ()); + d->set_solo_isolated (solo_isolated()); } } } void -Route::set_solo_mute (bool yn) +Route::set_solo_isolated (bool yn, void *src) { - Glib::Mutex::Lock lm (declick_lock); + if (is_master() || is_control() || is_hidden()) { + return; + } - /* Called by Session in response to another Route being soloed. - */ - - desired_solo_gain = (yn?0.0:1.0); + if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::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 */ + + boost::shared_ptr routes = _session.get_routes (); + for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { + bool sends_only; + bool does_feed = feeds (*i, &sends_only); + + if (does_feed && !sends_only) { + (*i)->set_solo_isolated (yn, (*i)->route_group()); + } + } + + bool changed = false; + + if (yn) { + if (_solo_isolated == 0) { + changed = true; + } + _solo_isolated++; + } else { + changed = (_solo_isolated == 1); + if (_solo_isolated > 0) { + _solo_isolated--; + } + } + + if (changed) { + set_delivery_solo (); + solo_isolated_changed (src); + } +} + +bool +Route::solo_isolated () const +{ + return _solo_isolated > 0; } void -Route::set_solo_safe (bool yn, void *src) +Route::set_mute_points (MuteMaster::MutePoint mp) { - if (_solo_safe != yn) { - _solo_safe = yn; - solo_safe_changed (src); /* EMIT SIGNAL */ + _mute_points = mp; + mute_points_changed (); /* EMIT SIGNAL */ + + if (_mute_master->muted()) { + _mute_master->mute_at (_mute_points); + mute_changed (this); /* EMIT SIGNAL */ } } void Route::set_mute (bool yn, void *src) - { - if (_mix_group && src != _mix_group && _mix_group->is_active()) { - _mix_group->apply (&Route::set_mute, yn, _mix_group); + if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Mute)) { + _route_group->apply (&Route::set_mute, yn, _route_group); return; } - if (_muted != yn) { - _muted = yn; - mute_changed (src); /* EMIT SIGNAL */ - - _mute_control->Changed (); /* EMIT SIGNAL */ - - Glib::Mutex::Lock lm (declick_lock); - - if (_soloed && Config->get_solo_mute_override()) { - desired_mute_gain = 1.0f; + if (muted() != yn) { + if (yn) { + _mute_master->mute_at (_mute_points); } else { - desired_mute_gain = (yn?0.0f:1.0f); + _mute_master->clear_mute (); } + + mute_changed (src); /* EMIT SIGNAL */ } } +bool +Route::muted() const +{ + return _mute_master->muted (); +} + +#if 0 static void dump_processors(const string& name, const list >& procs) { cerr << name << " {" << endl; for (list >::const_iterator p = procs.begin(); p != procs.end(); ++p) { - cerr << "\t" << (*p)->name() << endl; + cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << endl; } cerr << "}" << endl; } - -Route::ProcessorList::iterator -Route::prefader_iterator() -{ - Glib::RWLock::ReaderLock lm (_processor_lock); - return find (_processors.begin(), _processors.end(), _amp); -} +#endif int Route::add_processor (boost::shared_ptr processor, Placement placement, ProcessorStreams* err) @@ -615,23 +727,10 @@ Route::add_processor (boost::shared_ptr processor, Placement placemen if (placement == PreFader) { /* generic pre-fader: insert immediately before the amp */ - loc = find(_processors.begin(), _processors.end(), _amp); + loc = find (_processors.begin(), _processors.end(), _amp); } else { - /* generic post-fader: insert at end */ - loc = _processors.end(); - - if (processor->visible() && !_processors.empty()) { - /* check for invisible processors stacked at the end and leave them there */ - ProcessorList::iterator p; - p = _processors.end(); - --p; - cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl; - while (!(*p)->visible() && p != _processors.begin()) { - --p; - } - ++p; - loc = p; - } + /* generic post-fader: insert right before the main outs */ + loc = find (_processors.begin(), _processors.end(), _main_outs); } return add_processor (processor, loc, err); @@ -652,8 +751,6 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite return 1; } - cerr << "Adding a processor called " << processor->name() << endl; - { Glib::RWLock::WriterLock lm (_processor_lock); @@ -681,194 +778,227 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite loc = iter; } - cerr << "Adding " << processor->name() << endl; - _processors.insert (loc, processor); // Set up processor list channels. This will set processor->[input|output]_streams(), // configure redirect ports properly, etc. - - ProcessorStreams rerr; - if (configure_processors_unlocked (&rerr)) { - - dump_processors(_name + "bad config", _processors); - if (err) { - *err = rerr; - } - cerr << "Error at proc " << rerr.index << " with input of " << rerr.count << endl; + if (configure_processors_unlocked (err)) { ProcessorList::iterator ploc = loc; --ploc; _processors.erase(ploc); configure_processors_unlocked (0); // it worked before we tried to add it ... - cerr << "Bad IO config\n"; + cerr << "configure failed\n"; return -1; } - + if ((pi = boost::dynamic_pointer_cast(processor)) != 0) { - + if (pi->natural_input_streams() == ChanCount::ZERO) { /* generator plugin */ _have_internal_generator = true; } - + } - - // Ensure peak vector sizes before the plugin is activated - ChanCount potential_max_streams = ChanCount::max (processor->input_streams(), processor->output_streams()); - _meter->configure_io (potential_max_streams, potential_max_streams); + if (_control_outs != processor) { + // XXX: do we want to emit the signal here ? change call order. + processor->activate (); + } - // 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)); + processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false)); - _user_latency = 0; - } - - if (processor_max_streams != old_pms || old_pms == ChanCount::ZERO) { - reset_panner (); + _output->set_user_latency (0); } - dump_processors (_name + " added one", _processors); - processors_changed (); /* EMIT SIGNAL */ - + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + return 0; } bool -Route::add_processor_from_xml (const XMLNode& node, Placement placement) +Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter) { - ProcessorList::iterator loc; - if (placement == PreFader) { - /* generic pre-fader: insert immediately before the amp */ - loc = find(_processors.begin(), _processors.end(), _amp); - } else { - /* generic post-fader: insert at end */ - loc = _processors.end(); + const XMLProperty *prop; + + if (node.name() != "Processor") { + return false; } - return add_processor_from_xml (node, loc); -} + try { + if ((prop = node.property ("type")) != 0) { -bool -Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter) -{ - const XMLProperty *prop; + boost::shared_ptr processor; - // legacy sessions use a different node name for sends - if (node.name() == "Send") { - - try { - boost::shared_ptr send (new Send (_session, node)); - add_processor (send, iter); - return true; - } - - catch (failed_constructor &err) { - error << _("Send construction failed") << endmsg; - return false; - } - - } else if (node.name() == "Processor") { - - try { - if ((prop = node.property ("type")) != 0) { + if (prop->value() == "ladspa" || prop->value() == "Ladspa" || + prop->value() == "lv2" || + prop->value() == "vst" || + prop->value() == "audiounit") { - boost::shared_ptr processor; - bool have_insert = false; + processor.reset (new PluginInsert(_session, node)); - if (prop->value() == "ladspa" || prop->value() == "Ladspa" || - prop->value() == "lv2" || - prop->value() == "vst" || - prop->value() == "audiounit") { - - processor.reset (new PluginInsert(_session, node)); - have_insert = true; - - } else if (prop->value() == "port") { - - processor.reset (new PortInsert (_session, node)); - - } else if (prop->value() == "send") { - - processor.reset (new Send (_session, node)); - have_insert = true; - - } else if (prop->value() == "meter") { - - processor = _meter; - - } else if (prop->value() == "amp") { - - processor = _amp; - - } else if (prop->value() == "listen" || prop->value() == "deliver") { - - /* XXX need to generalize */ - - processor = _control_outs; - - } else if (prop->value() == "main-outs") { - - processor = _main_outs; - + } 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; + } - error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg; + } 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; + } } - - 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; - cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl; - while (!(*p)->visible() && p != _processors.begin()) { - --p; + _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; } - ++p; - iter = p; } - return (add_processor (processor, iter) == 0); - + _main_outs.reset (new Delivery (_session, _output, _mute_master, node)); + processor = _main_outs; + } else { - error << _("Processor XML node has no type property") << endmsg; + 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; } - } - catch (failed_constructor &err) { - warning << _("processor could not be created. Ignored.") << endmsg; + return (add_processor (processor, iter) == 0); + + } else { + error << _("Processor XML node has no type property") << endmsg; return false; } } - return false; + + catch (failed_constructor &err) { + warning << _("processor could not be created. Ignored.") << endmsg; + return false; + } } -int -Route::add_processors (const ProcessorList& others, Placement placement, ProcessorStreams* err) + +bool +Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorList::iterator iter) { - ProcessorList::iterator loc; - if (placement == PreFader) { - /* generic pre-fader: insert immediately before the amp */ - loc = find(_processors.begin(), _processors.end(), _amp); - } else { - /* generic post-fader: insert at end */ - loc = _processors.end(); + const XMLProperty *prop; + + try { + boost::shared_ptr processor; + + if (node.name() == "Insert") { + + if ((prop = node.property ("type")) != 0) { + + if (prop->value() == "ladspa" || prop->value() == "Ladspa" || + prop->value() == "lv2" || + prop->value() == "vst" || + prop->value() == "audiounit") { + + processor.reset (new PluginInsert (_session, node)); + + } else { + + processor.reset (new PortInsert (_session, _mute_master, node)); + } + + } + + } else if (node.name() == "Send") { + + processor.reset (new Send (_session, _mute_master, node, version)); - if (!_processors.empty()) { + } else { + + error << string_compose(_("unknown Processor type \"%1\"; ignored"), node.name()) << 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; - cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl; - while (!(*p)->visible() && p != _processors.begin()) { + while (!(*p)->display_to_user() && p != _processors.begin()) { --p; } ++p; - loc = p; + iter = p; } + + return (add_processor (processor, iter) == 0); + } + + catch (failed_constructor &err) { + warning << _("processor could not be created. Ignored.") << endmsg; + return false; + } +} + +int +Route::add_processors (const ProcessorList& others, boost::shared_ptr before, ProcessorStreams* err) +{ + ProcessorList::iterator loc; + + if (before) { + loc = find(_processors.begin(), _processors.end(), before); + } else { + /* nothing specified - at end but before main outs */ + loc = find (_processors.begin(), _processors.end(), _main_outs); } return add_processors (others, loc, err); @@ -894,13 +1024,11 @@ 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 (n_inputs(), n_outputs()); + ChanCount potential_max_streams = ChanCount::max (_input->n_ports(), _output->n_ports()); for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) { - + // Ensure meter only appears in the list once if (*i == _meter) { ProcessorList::iterator m = find(_processors.begin(), _processors.end(), *i); @@ -908,41 +1036,38 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter _processors.erase(m); } } - + boost::shared_ptr pi; - + if ((pi = boost::dynamic_pointer_cast(*i)) != 0) { pi->set_count (1); - - ChanCount m = max(pi->input_streams(), pi->output_streams()); - if (m > potential_max_streams) + + ChanCount m = max (pi->input_streams(), pi->output_streams()); + + if (m > potential_max_streams) { potential_max_streams = m; + } + } + + ProcessorList::iterator inserted = _processors.insert (iter, *i); + + if ((*i)->active()) { + (*i)->activate (); } - // Ensure peak vector sizes before the plugin is activated - _meter->configure_io (potential_max_streams, potential_max_streams); - - _processors.insert (iter, *i); - 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)); } - _user_latency = 0; - } - - if (processor_max_streams != old_pms || old_pms == ChanCount::ZERO) { - reset_panner (); + _output->set_user_latency (0); } - - dump_processors (_name + " added several", _processors); - processors_changed (); /* EMIT SIGNAL */ + + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ return 0; } @@ -967,10 +1092,10 @@ void Route::disable_processors (Placement p) { Glib::RWLock::ReaderLock lm (_processor_lock); - + ProcessorList::iterator start, end; placement_range(p, start, end); - + for (ProcessorList::iterator i = start; i != end; ++i) { (*i)->deactivate (); } @@ -978,17 +1103,17 @@ Route::disable_processors (Placement p) _session.set_dirty (); } -/** Turn off all redirects +/** Turn off all redirects */ void Route::disable_processors () { Glib::RWLock::ReaderLock lm (_processor_lock); - + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->deactivate (); } - + _session.set_dirty (); } @@ -999,16 +1124,16 @@ void Route::disable_plugins (Placement p) { Glib::RWLock::ReaderLock lm (_processor_lock); - + ProcessorList::iterator start, end; placement_range(p, start, end); - + for (ProcessorList::iterator i = start; i != end; ++i) { if (boost::dynamic_pointer_cast (*i)) { (*i)->deactivate (); } } - + _session.set_dirty (); } @@ -1018,13 +1143,13 @@ void Route::disable_plugins () { Glib::RWLock::ReaderLock lm (_processor_lock); - + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (boost::dynamic_pointer_cast (*i)) { (*i)->deactivate (); } } - + _session.set_dirty (); } @@ -1033,7 +1158,7 @@ void Route::ab_plugins (bool forward) { Glib::RWLock::ReaderLock lm (_processor_lock); - + if (forward) { /* forward = turn off all active redirects, and mark them so that the next time @@ -1070,33 +1195,8 @@ Route::ab_plugins (bool forward) } } } - - _session.set_dirty (); -} - - -/* Figure out the streams that will feed into PreFader */ -ChanCount -Route::pre_fader_streams() const -{ - boost::shared_ptr processor; - /* Find the last pre-fader redirect that isn't a send; sends don't affect the number - * of streams. */ - for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { - if ((*i) == _amp) { - break; - } - if (boost::dynamic_pointer_cast (*i) == 0) { - processor = *i; - } - } - - if (processor) { - return processor->output_streams(); - } else { - return n_inputs (); - } + _session.set_dirty (); } @@ -1111,7 +1211,7 @@ Route::clear_processors (Placement p) if (!_session.engine().connected()) { return; } - + bool already_deleting = _session.deletion_in_progress(); if (!already_deleting) { _session.set_deletion_in_progress(); @@ -1121,26 +1221,43 @@ Route::clear_processors (Placement p) Glib::RWLock::WriterLock lm (_processor_lock); ProcessorList new_list; ProcessorStreams err; + bool seen_amp = false; - ProcessorList::iterator amp_loc = find(_processors.begin(), _processors.end(), _amp); - if (p == PreFader) { - // Get rid of PreFader processors - for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) { - (*i)->drop_references (); - } - // Keep the rest - for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) { - new_list.push_back (*i); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + + if (*i == _amp) { + seen_amp = true; } - } else { - // Keep PreFader processors - for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) { + + if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs) { + + /* you can't remove these */ + new_list.push_back (*i); - } - new_list.push_back (_amp); - // Get rid of PostFader processors - for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) { - (*i)->drop_references (); + + } else { + if (seen_amp) { + + switch (p) { + case PreFader: + new_list.push_back (*i); + break; + case PostFader: + (*i)->drop_references (); + break; + } + + } else { + + switch (p) { + case PreFader: + (*i)->drop_references (); + break; + case PostFader: + new_list.push_back (*i); + break; + } + } } } @@ -1148,13 +1265,9 @@ Route::clear_processors (Placement p) configure_processors_unlocked (&err); // this can't fail } - if (processor_max_streams != old_pms) { - reset_panner (); - } - processor_max_streams.reset(); _have_internal_generator = false; - processors_changed (); /* EMIT SIGNAL */ + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ if (!already_deleting) { _session.clear_deletion_in_progress(); @@ -1187,7 +1300,7 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream if (*i == processor) { /* move along, see failure case for configure_processors() - where we may need to reprocessor the processor. + where we may need to reconfigure the processor. */ /* stop redirects that send signals to JACK ports @@ -1195,13 +1308,17 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream run. */ - boost::shared_ptr redirect; - - if ((redirect = boost::dynamic_pointer_cast (*i)) != 0) { - redirect->io()->disconnect_inputs (this); - redirect->io()->disconnect_outputs (this); + boost::shared_ptr iop; + + if ((iop = boost::dynamic_pointer_cast (*i)) != 0) { + if (iop->input()) { + iop->input()->disconnect (this); + } + if (iop->output()) { + iop->output()->disconnect (this); + } } - + i = _processors.erase (i); removed = true; break; @@ -1210,7 +1327,7 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream ++i; } - _user_latency = 0; + _output->set_user_latency (0); } if (!removed) { @@ -1230,7 +1347,94 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream for (i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr pi; - + + if ((pi = boost::dynamic_pointer_cast(*i)) != 0) { + if (pi->is_generator()) { + _have_internal_generator = true; + break; + } + } + } + } + + processor->drop_references (); + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + + return 0; +} + +int +Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* err) +{ + ProcessorList deleted; + ProcessorList as_we_were; + + if (!_session.engine().connected()) { + return 1; + } + + processor_max_streams.reset(); + + { + Glib::RWLock::WriterLock lm (_processor_lock); + ProcessorList::iterator i; + boost::shared_ptr processor; + + as_we_were = _processors; + + for (i = _processors.begin(); i != _processors.end(); ) { + + processor = *i; + + /* these can never be removed */ + + if (processor == _amp || processor == _meter || processor == _main_outs) { + ++i; + continue; + } + + /* see if its in the list of processors to delete */ + + if (find (to_be_deleted.begin(), to_be_deleted.end(), processor) == to_be_deleted.end()) { + ++i; + continue; + } + + /* stop IOProcessors that send to JACK ports + from causing noise as a result of no longer being + run. + */ + + boost::shared_ptr iop; + + if ((iop = boost::dynamic_pointer_cast (processor)) != 0) { + iop->disconnect (); + } + + deleted.push_back (processor); + i = _processors.erase (i); + } + + if (deleted.empty()) { + /* none of those in the requested list were found */ + return 0; + } + + _output->set_user_latency (0); + + if (configure_processors_unlocked (err)) { + /* get back to where we where */ + _processors = as_we_were; + /* we know this will work, because it worked before :) */ + configure_processors_unlocked (0); + return -1; + } + + _have_internal_generator = false; + + for (i = _processors.begin(); i != _processors.end(); ++i) { + boost::shared_ptr pi; + if ((pi = boost::dynamic_pointer_cast(*i)) != 0) { if (pi->is_generator()) { _have_internal_generator = true; @@ -1240,18 +1444,18 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream } } - if (old_pms != processor_max_streams) { - reset_panner (); + /* now try to do what we need to so that those that were removed will be deleted */ + + for (ProcessorList::iterator i = deleted.begin(); i != deleted.end(); ++i) { + (*i)->drop_references (); } - processor->drop_references (); - - dump_processors (_name + " removed one", _processors); - processors_changed (); /* EMIT SIGNAL */ + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ return 0; } + int Route::configure_processors (ProcessorStreams* err) { @@ -1275,18 +1479,27 @@ 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 = n_inputs(); + ChanCount in = _input->n_ports (); ChanCount out; list< pair > configuration; uint32_t index = 0; + + DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configure processors\n", _name)); +#ifndef NDEBUG + DEBUG_TRACE (DEBUG::Processors, "{\n"); + for (list >::const_iterator p = _processors.begin(); p != _processors.end(); ++p) { + DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID = %2\n", (*p)->name(), (*p)->id())); + } + DEBUG_TRACE (DEBUG::Processors, "}\n"); +#endif + for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) { - cerr << "Can " << (*p)->name() << " support in= " << in << " out= " << out; + if ((*p)->can_support_io_configuration(in, out)) { - cerr << " yes\n"; + DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1in = %2 out = %3\n",(*p)->name(), in, out)); configuration.push_back(make_pair(in, out)); in = out; } else { - cerr << " no\n"; if (err) { err->index = index; err->count = in; @@ -1295,22 +1508,24 @@ Route::configure_processors_unlocked (ProcessorStreams* err) return -1; } } - + // We can, so configure everything list< pair >::iterator c = configuration.begin(); for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) { (*p)->configure_io(c->first, c->second); - (*p)->activate(); processor_max_streams = ChanCount::max(processor_max_streams, c->first); processor_max_streams = ChanCount::max(processor_max_streams, c->second); out = c->second; } - // Ensure route outputs match last processor's outputs - if (out != n_outputs()) { - ensure_io (n_inputs(), out, false, this); + 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 ()); + _in_configure_processors = false; return 0; } @@ -1325,7 +1540,7 @@ Route::all_processors_flip () } bool first_is_on = _processors.front()->active(); - + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (first_is_on) { (*i)->deactivate (); @@ -1333,7 +1548,7 @@ Route::all_processors_flip () (*i)->activate (); } } - + _session.set_dirty (); } @@ -1366,13 +1581,44 @@ Route::all_processors_active (Placement p, bool state) } } } - + _session.set_dirty (); } +bool +Route::processor_is_prefader (boost::shared_ptr p) +{ + bool pre_fader = true; + Glib::RWLock::ReaderLock lm (_processor_lock); + + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + + /* semantic note: if p == amp, we want to return true, so test + for equality before checking if this is the amp + */ + + if ((*i) == p) { + break; + } + + if ((*i) == _amp) { + pre_fader = false; + break; + } + } + + return pre_fader; +} + int -Route::reorder_processors (const ProcessorList& new_order, Placement placement, ProcessorStreams* err) +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 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. + */ + { Glib::RWLock::WriterLock lm (_processor_lock); ChanCount old_pms = processor_max_streams; @@ -1380,34 +1626,34 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement, ProcessorList::const_iterator niter; ProcessorList as_it_was_before = _processors; ProcessorList as_it_will_be; - ProcessorList::iterator start, end; - dump_processors (_name + " PreReorder", _processors); - placement_range (placement, start, end); + oiter = _processors.begin(); + niter = new_order.begin(); - oiter = start; - niter = new_order.begin(); + while (niter != new_order.end()) { - while (oiter != end && niter != new_order.end()) { - /* if the next processor in the old list is invisible (i.e. should not be in the new order) - then append it to the temp list. + then append it to the temp list. Otherwise, see if the next processor in the old list is in the new list. if not, its been deleted. If its there, append it to the temp list. */ - - if (oiter == end) { - /* no more elements in the old list, so just stick the rest of + if (oiter == _processors.end()) { + + /* no more elements in the old list, so just stick the rest of the new order onto the temp list. */ as_it_will_be.insert (as_it_will_be.end(), niter, new_order.end()); + while (niter != new_order.end()) { + ++niter; + } + break; } else { - - if (!(*oiter)->visible()) { + + if (!(*oiter)->display_to_user()) { as_it_will_be.push_back (*oiter); @@ -1416,34 +1662,30 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement, /* visible processor: check that its in the new order */ if (find (new_order.begin(), new_order.end(), (*oiter)) == new_order.end()) { - /* deleted: do nothing */ + /* deleted: do nothing, shared_ptr<> will clean up */ } else { /* ignore this one, and add the next item from the new order instead */ as_it_will_be.push_back (*niter); ++niter; } } - + /* now remove from old order - its taken care of no matter what */ oiter = _processors.erase (oiter); } - } - _processors.insert (end, as_it_will_be.begin(), as_it_will_be.end()); + } - dump_processors (_name + " PostReorder", _processors); + _processors.insert (oiter, as_it_will_be.begin(), as_it_will_be.end()); if (configure_processors_unlocked (err)) { _processors = as_it_was_before; processor_max_streams = old_pms; return -1; - } - } + } + } - dump_processors (_name + " sorted", _processors); - /* do we really need to do this every time? */ - reset_panner (); - processors_changed (); /* EMIT SIGNAL */ + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ return 0; } @@ -1467,39 +1709,33 @@ Route::state(bool full_state) ProcessorList::iterator i; char buf[32]; + id().print (buf, sizeof (buf)); + node->add_property("id", buf); + node->add_property ("name", _name); + node->add_property("default-type", _default_type.to_string()); + if (_flags) { 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"); - node->add_property("mute-affects-main-outs", _mute_affects_main_outs?"yes":"no"); node->add_property("meter-point", enum_2_string (_meter_point)); - if (_edit_group) { - node->add_property("edit-group", _edit_group->name()); - } - if (_mix_group) { - node->add_property("mix-group", _mix_group->name()); + if (_route_group) { + node->add_property("route-group", _route_group->name()); } string order_string; - OrderKeys::iterator x = order_keys.begin(); + OrderKeys::iterator x = order_keys.begin(); while (x != order_keys.end()) { order_string += string ((*x).first); order_string += '='; snprintf (buf, sizeof(buf), "%ld", (*x).second); order_string += buf; - + ++x; if (x == order_keys.end()) { @@ -1509,10 +1745,14 @@ 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); + node->add_property ("soloed-by-others", buf); - node->add_child_nocopy (IO::state (full_state)); + node->add_child_nocopy (_input->state (full_state)); + node->add_child_nocopy (_output->state (full_state)); node->add_child_nocopy (_solo_control->get_state ()); - node->add_child_nocopy (_mute_control->get_state ()); + node->add_child_nocopy (_mute_master->get_state ()); XMLNode* remote_control_node = new XMLNode (X_("RemoteControl")); snprintf (buf, sizeof (buf), "%d", _remote_control_id); @@ -1531,104 +1771,179 @@ Route::state(bool full_state) if (_extra_xml){ node->add_child_copy (*_extra_xml); } - + return *node; } -XMLNode& -Route::get_processor_state () +int +Route::set_state (const XMLNode& node, int version) { - XMLNode* root = new XMLNode (X_("redirects")); - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - root->add_child_nocopy ((*i)->state (true)); - } - - return *root; + return _set_state (node, version, true); } int -Route::set_processor_state (const XMLNode& root) +Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) { - if (root.name() != X_("redirects")) { - return -1; + if (version < 3000) { + return _set_state_2X (node, version); } XMLNodeList nlist; - XMLNodeList nnlist; - XMLNodeConstIterator iter; XMLNodeConstIterator niter; - Glib::RWLock::ReaderLock lm (_processor_lock); + XMLNode *child; + XMLPropertyList plist; + const XMLProperty *prop; - nlist = root.children(); - - for (iter = nlist.begin(); iter != nlist.end(); ++iter){ + if (node.name() != "Route"){ + error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg; + return -1; + } - /* iter now points to a IOProcessor state node */ - - nnlist = (*iter)->children (); + if ((prop = node.property (X_("name"))) != 0) { + Route::set_name (prop->value()); + } + + if ((prop = node.property ("id")) != 0) { + _id = prop->value (); + } - for (niter = nnlist.begin(); niter != nnlist.end(); ++niter) { + if ((prop = node.property (X_("flags"))) != 0) { + _flags = Flag (string_2_enum (prop->value(), _flags)); + } else { + _flags = Flag (0); + } - /* find the IO child node, since it contains the ID we need */ + /* add all processors (except amp, which is always present) */ - /* XXX OOP encapsulation violation, ugh */ + nlist = node.children(); + XMLNode processor_state (X_("processor_state")); - if ((*niter)->name() == IO::state_node_name) { + for (niter = nlist.begin(); niter != nlist.end(); ++niter){ - XMLProperty* prop = (*niter)->property (X_("id")); - - if (!prop) { - warning << _("IOProcessor node has no ID, ignored") << endmsg; - break; - } + child = *niter; - ID id = prop->value (); + if (child->name() == IO::state_node_name) { + if ((prop = child->property (X_("direction"))) == 0) { + continue; + } - /* now look for a processor with that ID */ - - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if ((*i)->id() == id) { - (*i)->set_state (**iter); - break; - } - } - - break; - + if (prop->value() == "Input") { + _input->set_state (*child, version); + } else if (prop->value() == "Output") { + _output->set_state (*child, version); } } + if (child->name() == X_("Processor")) { + processor_state.add_child_copy (*child); + } } - return 0; -} + set_processor_state (processor_state); -void -Route::set_deferred_state () -{ - XMLNodeList nlist; - XMLNodeConstIterator niter; + if ((prop = node.property ("self-solo")) != 0) { + set_self_solo (string_is_affirmative (prop->value())); + } - if (!deferred_state) { - return; + if ((prop = node.property ("soloed-by-others")) != 0) { + _soloed_by_others = 0; // needed for mod_solo_by_others () to work + mod_solo_by_others (atoi (prop->value())); } - nlist = deferred_state->children(); + if ((prop = node.property ("solo-isolated")) != 0) { + set_solo_isolated (string_is_affirmative (prop->value()), this); + } - _set_processor_states (nlist); + if ((prop = node.property (X_("phase-invert"))) != 0) { + set_phase_invert (string_is_affirmative (prop->value())); + } - delete deferred_state; - deferred_state = 0; -} + if ((prop = node.property (X_("denormal-protection"))) != 0) { + set_denormal_protection (string_is_affirmative (prop->value())); + } -int -Route::set_state (const XMLNode& node) -{ - return _set_state (node, true); + if ((prop = node.property (X_("active"))) != 0) { + bool yn = string_is_affirmative (prop->value()); + _active = !yn; // force switch + set_active (yn); + } + + if ((prop = node.property (X_("meter-point"))) != 0) { + _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point)); + if (_meter) { + _meter->set_display_to_user (_meter_point == MeterCustom); + } + } + + if ((prop = node.property (X_("order-keys"))) != 0) { + + long n; + + string::size_type colon, equal; + string remaining = prop->value(); + + while (remaining.length()) { + + if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) { + 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) { + error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining) + << endmsg; + } else { + set_order_key (remaining.substr (0, equal), n); + } + } + + colon = remaining.find_first_of (':'); + + if (colon != string::npos) { + remaining = remaining.substr (colon+1); + } else { + break; + } + } + } + + for (niter = nlist.begin(); niter != nlist.end(); ++niter){ + child = *niter; + + 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() == 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, version); + _session.add_controllable (_solo_control); + } + + } else if (child->name() == X_("RemoteControl")) { + if ((prop = child->property (X_("id"))) != 0) { + int32_t x; + sscanf (prop->value().c_str(), "%d", &x); + set_remote_control_id (x); + } + + } else if (child->name() == X_("MuteMaster")) { + _mute_master->set_state (*child, version); + } + } + + return 0; } int -Route::_set_state (const XMLNode& node, bool call_base) +Route::_set_state_2X (const XMLNode& node, int version) { XMLNodeList nlist; XMLNodeConstIterator niter; @@ -1636,7 +1951,18 @@ Route::_set_state (const XMLNode& node, bool call_base) XMLPropertyList plist; const XMLProperty *prop; - if (node.name() != "Route"){ + /* 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 + */ + + if (node.name() != "Route") { error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg; return -1; } @@ -1646,73 +1972,78 @@ Route::_set_state (const XMLNode& node, bool call_base) } else { _flags = Flag (0); } - - if ((prop = node.property (X_("default-type"))) != 0) { - _default_type = DataType(prop->value()); - assert(_default_type != DataType::NIL); - } - if ((prop = node.property (X_("phase-invert"))) != 0) { - set_phase_invert (prop->value()=="yes"?true:false, this); - } + /* add standard processors */ - if ((prop = node.property (X_("denormal-protection"))) != 0) { - set_denormal_protection (prop->value()=="yes"?true:false, this); - } - - _active = true; - if ((prop = node.property (X_("active"))) != 0) { - set_active (prop->value() == "yes"); + _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); } - if ((prop = node.property (X_("muted"))) != 0) { - bool yn = prop->value()=="yes"?true:false; + _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main)); + add_processor (_main_outs, PostFader); - /* force reset of mute status */ + /* IOs */ - _muted = !yn; - set_mute(yn, this); - mute_gain = desired_mute_gain; - } + nlist = node.children (); + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((prop = node.property (X_("soloed"))) != 0) { - bool yn = prop->value()=="yes"?true:false; + child = *niter; - /* force reset of solo status */ + if (child->name() == IO::state_node_name) { - _soloed = !yn; - set_solo (yn, this); - solo_gain = desired_solo_gain; - } + /* 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 = node.property (X_("mute-affects-pre-fader"))) != 0) { - _mute_affects_pre_fader = (prop->value()=="yes")?true:false; + 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_("mute-affects-post-fader"))) != 0) { - _mute_affects_post_fader = (prop->value()=="yes")?true:false; + if ((prop = node.property (X_("phase-invert"))) != 0) { + set_phase_invert (string_is_affirmative (prop->value())); } - if ((prop = node.property (X_("mute-affects-control-outs"))) != 0) { - _mute_affects_control_outs = (prop->value()=="yes")?true:false; + if ((prop = node.property (X_("denormal-protection"))) != 0) { + set_denormal_protection (string_is_affirmative (prop->value())); } - if ((prop = node.property (X_("mute-affects-main-outs"))) != 0) { - _mute_affects_main_outs = (prop->value()=="yes")?true:false; + 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_("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; - } else { - set_edit_group(edit_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) { @@ -1725,13 +2056,13 @@ Route::_set_state (const XMLNode& node, bool call_base) if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) { error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining) - << endmsg; + << endmsg; } else { if (sscanf (remaining.substr (equal+1).c_str(), "%ld", &n) != 1) { error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining) - << endmsg; + << endmsg; } else { - set_order_key (remaining.substr (0, equal).c_str(), n); + set_order_key (remaining.substr (0, equal), n); } } @@ -1745,52 +2076,24 @@ Route::_set_state (const XMLNode& node, bool call_base) } } - nlist = node.children(); - - delete deferred_state; - deferred_state = new XMLNode(X_("deferred state")); - - /* set parent class properties before anything else */ + XMLNodeList redirect_nodes; for (niter = nlist.begin(); niter != nlist.end(); ++niter){ child = *niter; - if (child->name() == IO::state_node_name && call_base) { - IO::set_state (*child); - break; + if (child->name() == X_("Send") || child->name() == X_("Insert")) { + redirect_nodes.push_back(child); } - } - for (niter = nlist.begin(); niter != nlist.end(); ++niter){ - - child = *niter; - - if (child->name() == X_("Send") || child->name() == X_("Processor")) { - deferred_state->add_child_copy (*child); - } } - if (ports_legal) { - _set_processor_states (deferred_state->children()); - delete deferred_state; - deferred_state = 0; - } + set_processor_state_2X (redirect_nodes, version); for (niter = nlist.begin(); niter != nlist.end(); ++niter){ child = *niter; - if (child->name() == X_("Automation")) { - - if ((prop = child->property (X_("path"))) != 0) { - load_automation (prop->value()); - } - - } else if (child->name() == X_("ControlOuts")) { - - /* ignore this - deprecated */ - - } else if (child->name() == X_("Comment")) { + if (child->name() == X_("Comment")) { /* XXX this is a terrible API design in libxml++ */ @@ -1802,60 +2105,83 @@ Route::_set_state (const XMLNode& node, bool call_base) _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); + _solo_control->set_state (*child, version); _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_("RemoteControl")) { 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 (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; - } else { - set_mix_group(mix_group, this); - } + } } return 0; } +XMLNode& +Route::get_processor_state () +{ + XMLNode* root = new XMLNode (X_("redirects")); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + root->add_child_nocopy ((*i)->state (true)); + } + + return *root; +} + +void +Route::set_processor_state_2X (XMLNodeList const & nList, int version) +{ + /* We don't bother removing existing processors not in nList, as this + method will only be called when creating a Route from scratch, not + for undo purposes. Just put processors in at the appropriate place + in the list. + */ + + for (XMLNodeConstIterator i = nList.begin(); i != nList.end(); ++i) { + add_processor_from_xml_2X (**i, version, _processors.begin ()); + } +} + void -Route::_set_processor_states(const XMLNodeList &nlist) +Route::set_processor_state (const XMLNode& node) { + const XMLNodeList &nlist = node.children(); XMLNodeConstIterator niter; - bool has_meter_processor = false; // legacy sessions don't ProcessorList::iterator i, o; - cerr << "Setting processor states with in = " << n_inputs() << endl; - // 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; - + 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); } @@ -1865,34 +2191,33 @@ Route::_set_processor_states(const XMLNodeList &nlist) // 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) { - - XMLProperty* prop = (*niter)->property ("type"); - if (prop && prop->value() == "meter") { - has_meter_processor = true; - } + XMLProperty* prop = (*niter)->property ("type"); o = i; - if (prop->value() != "meter" && prop->value() != "amp" && prop->value() != "main-outs") { + // 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 - // Check whether the next processor in the list - + if (prop->value() != "amp") { while (o != _processors.end()) { XMLProperty* id_prop = (*niter)->property(X_("id")); if (id_prop && (*o)->id() == id_prop->value()) { 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)) { @@ -1901,10 +2226,11 @@ Route::_set_processor_states(const XMLNodeList &nlist) cerr << "Error restoring route: unable to restore processor" << endl; } - // Otherwise, the processor already exists; just - // ensure it is at the location provided in the XML state } 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 @@ -1912,7 +2238,9 @@ Route::_set_processor_states(const XMLNodeList &nlist) --i; // move iterator to the correct processor } - (*i)->set_state (**niter); + // and make it (just) so + + (*i)->set_state (**niter, Stateful::current_state_version); } } @@ -1920,14 +2248,7 @@ Route::_set_processor_states(const XMLNodeList &nlist) the XML state represents a working signal route. */ - if (!has_meter_processor) { - set_meter_point (_meter_point, NULL); - } - - processors_changed (); - - - + processors_changed (RouteProcessorChange ()); } void @@ -1942,11 +2263,11 @@ Route::silence (nframes_t nframes) { if (!_silent) { - IO::silence (nframes); - - { + _output->silence (nframes); + + { Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); - + if (lm.locked()) { for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr pi; @@ -1955,7 +2276,7 @@ Route::silence (nframes_t nframes) // skip plugins, they don't need anything when we're not active continue; } - + (*i)->silence (nframes); } @@ -1964,166 +2285,130 @@ Route::silence (nframes_t nframes) } } } - + } -} +} -boost::shared_ptr -Route::add_listener (boost::shared_ptr io, const string& listen_name) +void +Route::add_internal_return () { - string name = _name; - name += '['; - name += listen_name; - name += ']'; - - boost::shared_ptr listener (new Delivery (_session, name, Delivery::Listen)); + if (!_intreturn) { + _intreturn.reset (new InternalReturn (_session)); + add_processor (_intreturn, PreFader); + } +} - /* As an IO, our control outs need as many IO outputs as we have outputs - * (we track the changes in ::output_change_handler()). - * As a processor, the listener is an identity processor - * (i.e. it does not modify its input buffers whatsoever) - */ +BufferSet* +Route::get_return_buffer () const +{ + Glib::RWLock::ReaderLock rm (_processor_lock); + + for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) { + boost::shared_ptr d = boost::dynamic_pointer_cast(*x); - if (listener->io()->ensure_io (ChanCount::ZERO, n_outputs(), true, this)) { - return boost::shared_ptr(); + if (d) { + BufferSet* bs = d->get_buffers (); + return bs; + } } - - add_processor (listener, PostFader); - return listener; + return 0; +} + +void +Route::release_return_buffer () const +{ + Glib::RWLock::ReaderLock rm (_processor_lock); + + for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) { + boost::shared_ptr d = boost::dynamic_pointer_cast(*x); + + if (d) { + return d->release_buffers (); + } + } } int -Route::listen_via (boost::shared_ptr io, const string& listen_name) +Route::listen_via (boost::shared_ptr route, Placement placement, bool /*active*/, bool aux) { vector ports; vector::const_iterator i; { Glib::RWLock::ReaderLock rm (_processor_lock); - - for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) { - boost::shared_ptr d = boost::dynamic_pointer_cast(*x); - if (d && d->io() == io) { - /* already listening via the specified IO: do nothing */ - return 0; - } - } - } + for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) { - uint32_t ni = io->n_inputs().n_total(); + boost::shared_ptr d = boost::dynamic_pointer_cast(*x); - for (uint32_t n = 0; n < ni; ++n) { - ports.push_back (io->input(n)->name()); - } + if (d && d->target_route() == route) { - if (ports.empty()) { - return 0; - } - - boost::shared_ptr listen_point = add_listener (io, listen_name); - - /* XXX hack for now .... until we can generalize listen points */ + /* if the target is the control outs, then make sure + we take note of which i-send is doing that. + */ - _control_outs = listen_point; + if (route == _session.control_out()) { + _control_outs = boost::dynamic_pointer_cast(d); + } - /* now connect to the named ports */ - - ni = listen_point->io()->n_outputs().n_total(); - size_t psize = ports.size(); + /* already listening via the specified IO: do nothing */ - for (size_t n = 0; n < ni; ++n) { - if (listen_point->io()->connect_output (listen_point->io()->output (n), ports[n % psize], this)) { - error << string_compose (_("could not connect %1 to %2"), - listen_point->io()->output(n)->name(), ports[n % psize]) << endmsg; - return -1; + return 0; + } } } - - return 0; -} - -void -Route::drop_listen (boost::shared_ptr io) -{ - ProcessorStreams err; - ProcessorList::iterator tmp; - - Glib::RWLock::ReaderLock rm (_processor_lock); - - for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ) { - - tmp = x; - ++tmp; - - boost::shared_ptr d = boost::dynamic_pointer_cast(*x); - - if (d && d->io() == io) { - /* already listening via the specified IO: do nothing */ - remove_processor (*x, &err); - - } - - x = tmp; - } -} + boost::shared_ptr listener; -void -Route::set_edit_group (RouteGroup *eg, void *src) + try { + listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen))); -{ - if (eg == _edit_group) { - return; + } catch (failed_constructor& err) { + return -1; } - if (_edit_group) { - _edit_group->remove (this); + if (route == _session.control_out()) { + _control_outs = listener; } - if ((_edit_group = eg) != 0) { - _edit_group->add (this); - } + add_processor (listener, placement); - _session.set_dirty (); - edit_group_changed (src); /* EMIT SIGNAL */ + return 0; } void -Route::drop_edit_group (void *src) +Route::drop_listen (boost::shared_ptr route) { - _edit_group = 0; - _session.set_dirty (); - edit_group_changed (src); /* EMIT SIGNAL */ -} + ProcessorStreams err; + ProcessorList::iterator tmp; -void -Route::set_mix_group (RouteGroup *mg, void *src) + Glib::RWLock::ReaderLock rl(_processor_lock); + rl.acquire (); -{ - if (mg == _mix_group) { - return; - } + again: + for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ) { - if (_mix_group) { - _mix_group->remove (this); - } + boost::shared_ptr d = boost::dynamic_pointer_cast(*x); + + if (d && d->target_route() == route) { + rl.release (); + remove_processor (*x, &err); + rl.acquire (); + + /* list could have been demolished while we dropped the lock + so start over. + */ - if ((_mix_group = mg) != 0) { - _mix_group->add (this); + goto again; + } } - _session.set_dirty (); - mix_group_changed (src); /* EMIT SIGNAL */ -} + rl.release (); -void -Route::drop_mix_group (void *src) -{ - _mix_group = 0; - _session.set_dirty (); - mix_group_changed (src); /* EMIT SIGNAL */ + if (route == _session.control_out()) { + _control_outs.reset (); + } } void @@ -2135,97 +2420,46 @@ Route::set_comment (string cmt, void *src) } bool -Route::feeds (boost::shared_ptr other) +Route::feeds (boost::shared_ptr other, bool* only_send) { - uint32_t i, j; - - IO& self = *this; - uint32_t no = self.n_outputs().n_total(); - uint32_t ni = other->n_inputs ().n_total(); + DEBUG_TRACE (DEBUG::Graph, string_compose ("Feeds? %1\n", _name)); - for (i = 0; i < no; ++i) { - for (j = 0; j < ni; ++j) { - if (self.output(i)->connected_to (other->input(j)->name())) { - return true; - } + if (_output->connected_to (other->input())) { + DEBUG_TRACE (DEBUG::Graph, string_compose ("\tdirect FEEDS %2\n", other->name())); + if (only_send) { + *only_send = false; } - } - /* check IOProcessors which may also interconnect Routes */ + return true; + } + for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); r++) { - boost::shared_ptr proc = boost::dynamic_pointer_cast(*r); - - if (!proc) { - continue; - } - - no = proc->io()->n_outputs().n_total(); - - for (i = 0; i < no; ++i) { - for (j = 0; j < ni; ++j) { - if (proc->io()->output(i)->connected_to (other->input (j)->name())) { - return true; + boost::shared_ptr iop; + + if ((iop = boost::dynamic_pointer_cast(*r)) != 0) { + if (iop->feeds (other)) { + 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 { + 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())); } + } - return false; -} - -void -Route::set_mute_config (mute_type t, bool onoff, void *src) -{ - switch (t) { - case PRE_FADER: - _mute_affects_pre_fader = onoff; - pre_fader_changed(src); /* EMIT SIGNAL */ - break; - - case POST_FADER: - _mute_affects_post_fader = onoff; - post_fader_changed(src); /* EMIT SIGNAL */ - break; - - case CONTROL_OUTS: - _mute_affects_control_outs = onoff; - control_outs_changed(src); /* EMIT SIGNAL */ - break; - - case MAIN_OUTS: - _mute_affects_main_outs = onoff; - main_outs_changed(src); /* EMIT SIGNAL */ - break; - } -} - -bool -Route::get_mute_config (mute_type t) -{ - bool onoff = false; - - switch (t){ - case PRE_FADER: - onoff = _mute_affects_pre_fader; - break; - case POST_FADER: - onoff = _mute_affects_post_fader; - break; - case CONTROL_OUTS: - onoff = _mute_affects_control_outs; - break; - case MAIN_OUTS: - onoff = _mute_affects_main_outs; - break; - } - - return onoff; + 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) +Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors) { nframes_t now = _session.transport_frame(); @@ -2237,23 +2471,21 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f } 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)->transport_stopped (now); } } - IO::transport_stopped (now); - _roll_delay = _initial_delay; } void -Route::input_change_handler (IOChange change, void *src) +Route::input_change_handler (IOChange change, void * /*src*/) { if ((change & ConfigurationChanged)) { configure_processors (0); @@ -2261,14 +2493,13 @@ Route::input_change_handler (IOChange change, void *src) } void -Route::output_change_handler (IOChange change, void *src) +Route::output_change_handler (IOChange change, void * /*src*/) { if ((change & ConfigurationChanged)) { - if (_control_outs) { - _control_outs->io()->ensure_io (ChanCount::ZERO, n_outputs(), true, this); - } - - configure_processors (0); + + /* XXX resize all listeners to match _main_outs? */ + + // configure_processors (0); } } @@ -2278,31 +2509,40 @@ Route::pans_required () const if (n_outputs().n_audio() < 2) { return 0; } - + return max (n_inputs ().n_audio(), processor_max_streams.n_audio()); } -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) +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*/) { if (n_outputs().n_total() == 0) { return 0; } - if (session_state_changing || !_active) { + if (!_active || n_inputs() == ChanCount::ZERO) { silence (nframes); return 0; } - - _amp->apply_gain_automation(false); - - if (n_inputs() != ChanCount::ZERO) { - passthru (start_frame, end_frame, nframes, 0); - } else { - silence (nframes); + 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); + return 0; } @@ -2320,11 +2560,11 @@ Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame) nframes -= _roll_delay; silence (_roll_delay); - /* we've written _roll_delay of samples into the + /* we've written _roll_delay of samples into the output ports, so make a note of that for future reference. */ - increment_output_offset (_roll_delay); + _main_outs->increment_output_offset (_roll_delay); transport_frame += _roll_delay; _roll_delay = 0; @@ -2335,22 +2575,27 @@ 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*/) { { + // 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 can also be called from the non-rt context - // and it uses the processor list, so we take the lock out here automation_snapshot (_session.transport_frame(), false); } } - if ((n_outputs().n_total() == 0 && _processors.empty()) || n_inputs().n_total() == 0 || !_active) { + if (n_outputs().n_total() == 0) { + return 0; + } + + if (!_active || n_inputs().n_total() == 0) { silence (nframes); return 0; } - + nframes_t unused = 0; if ((nframes = check_initial_delay (nframes, unused)) == 0) { @@ -2359,29 +2604,14 @@ Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int _silent = false; - _amp->apply_gain_automation(false); - - { - Glib::Mutex::Lock am (data().control_lock(), Glib::TRY_LOCK); - - if (am.locked() && _session.transport_rolling()) { - - if (_gain_control->automation_playback()) { - _amp->apply_gain_automation( - _gain_control->list()->curve().rt_safe_get_vector ( - start_frame, end_frame, _session.gain_automation_buffer(), nframes)); - } - } - } - passthru (start_frame, end_frame, nframes, declick); return 0; } int -Route::silent_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, - bool can_record, bool rec_monitors_input) +Route::silent_roll (nframes_t nframes, sframes_t /*start_frame*/, sframes_t /*end_frame*/, + bool /*can_record*/, bool /*rec_monitors_input*/) { silence (nframes); return 0; @@ -2390,7 +2620,7 @@ Route::silent_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_fram void Route::toggle_monitor_input () { - for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) { + for (PortSet::iterator i = _input->ports().begin(); i != _input->ports().end(); ++i) { i->ensure_monitor_input( ! i->monitoring_input()); } } @@ -2398,21 +2628,21 @@ Route::toggle_monitor_input () bool Route::has_external_redirects () const { - // FIXME: what about sends? + // FIXME: what about sends? - they don't return a signal back to ardour? boost::shared_ptr pi; - + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + if ((pi = boost::dynamic_pointer_cast(*i)) != 0) { - for (PortSet::const_iterator port = pi->io()->outputs().begin(); - port != pi->io()->outputs().end(); ++port) { - + for (PortSet::const_iterator port = pi->output()->ports().begin(); port != pi->output()->ports().end(); ++port) { + string port_name = port->name(); string client_name = port_name.substr (0, port_name.find(':')); /* only say "yes" if the redirect is actually in use */ - + if (client_name != "ardour" && pi->active()) { return true; } @@ -2441,56 +2671,132 @@ Route::flush_processors () void Route::set_meter_point (MeterPoint p, void *src) { - if (_meter_point != p) { - _meter_point = p; + /* CAN BE CALLED FROM PROCESS CONTEXT */ + + if (_meter_point == p) { + return; + } + + 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 (src); /* 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) +{ + if (!_control_outs) { + 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(), _meter); + ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _control_outs); _processors.erase(loc); + switch (p) { - case MeterInput: - loc = _processors.begin(); - break; - case MeterPreFader: + case PreFader: loc = find(_processors.begin(), _processors.end(), _amp); + if (loc != _processors.begin()) { + --loc; + } break; - case MeterPostFader: - loc = _processors.end(); + case PostFader: + loc = find(_processors.begin(), _processors.end(), _amp); + assert (loc != _processors.end()); + loc++; break; } - _processors.insert(loc, _meter); - meter_change (src); /* EMIT SIGNAL */ - processors_changed (); /* EMIT SIGNAL */ - _session.set_dirty (); + _processors.insert(loc, _control_outs); + + if (configure_processors_unlocked (0)) { + _processors = as_it_was; + configure_processors_unlocked (0); // it worked before we tried to add it ... + return; + } } + + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + _session.set_dirty (); } nframes_t Route::update_total_latency () { - nframes_t old = _own_latency; - - if (_user_latency) { - _own_latency = _user_latency; - } else { - _own_latency = 0; + nframes_t old = _output->effective_latency(); + nframes_t own_latency = _output->user_latency(); - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if ((*i)->active ()) { - _own_latency += (*i)->signal_latency (); - } + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + if ((*i)->active ()) { + own_latency += (*i)->signal_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); + + if (_output->user_latency() == 0) { - set_port_latency (_own_latency); - - if (!_user_latency) { /* this (virtual) function is used for pure Routes, not derived classes like AudioTrack. this means that the data processed here comes from an input @@ -2498,26 +2804,23 @@ Route::update_total_latency () have to take into account any input latency. */ - - _own_latency += input_latency (); + own_latency += _input->signal_latency (); } - if (old != _own_latency) { + if (old != own_latency) { + _output->set_latency_delay (own_latency); signal_latency_changed (); /* EMIT SIGNAL */ } - -#ifdef DEBUG_LATENCY - cerr << _name << ": input latency = " << input_latency() << " total = " - << _own_latency << endl; -#endif - return _own_latency; + DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: input latency = %2 total = %3\n", _name, _input->signal_latency(), own_latency)); + + return _output->effective_latency (); } void Route::set_user_latency (nframes_t nframes) { - Latent::set_user_latency (nframes); + _output->set_user_latency (nframes); _session.update_latency_compensation (false, false); } @@ -2526,8 +2829,8 @@ Route::set_latency_delay (nframes_t longest_session_latency) { nframes_t old = _initial_delay; - if (_own_latency < longest_session_latency) { - _initial_delay = longest_session_latency - _own_latency; + if (_output->effective_latency() < longest_session_latency) { + _initial_delay = longest_session_latency - _output->effective_latency(); } else { _initial_delay = 0; } @@ -2544,73 +2847,95 @@ Route::set_latency_delay (nframes_t longest_session_latency) void Route::automation_snapshot (nframes_t now, bool force) { - if (!force && !should_snapshot(now)) { - return; - } - - IO::automation_snapshot (now, force); - + panner()->automation_snapshot (now, force); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->automation_snapshot (now, force); } } -Route::ToggleControllable::ToggleControllable (std::string name, Route& s, ToggleType tp) - : Controllable (name), route (s), type(tp) +Route::SoloControllable::SoloControllable (std::string name, Route& r) + : AutomationControl (r.session(), Evoral::Parameter (SoloAutomation), + boost::shared_ptr(), name) + , route (r) { - + boost::shared_ptr gl(new AutomationList(Evoral::Parameter(SoloAutomation))); + set_list (gl); } void -Route::ToggleControllable::set_value (float val) +Route::SoloControllable::set_value (float val) { bool bval = ((val >= 0.5f) ? true: false); - - switch (type) { - case MuteControl: - route.set_mute (bval, this); - break; - case SoloControl: - route.set_solo (bval, this); - break; - default: - break; +# 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::ToggleControllable::get_value (void) const +Route::SoloControllable::get_value (void) const { - float val = 0.0f; - - switch (type) { - case MuteControl: - val = route.muted() ? 1.0f : 0.0f; - break; - case SoloControl: - val = route.soloed() ? 1.0f : 0.0f; - break; - default: - break; + 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); +} - return val; +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.muted() ? 1.0f : 0.0f; } -void +void Route::set_block_size (nframes_t nframes) { for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->set_block_size (nframes); } - _session.ensure_buffers(processor_max_streams); + + _session.ensure_buffers (n_process_buffers ()); } void Route::protect_automation () { - Automatable::protect_automation(); - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) (*i)->protect_automation(); } @@ -2638,7 +2963,7 @@ Route::set_pending_declick (int declick) */ void -Route::shift (nframes64_t pos, nframes64_t frames) +Route::shift (nframes64_t /*pos*/, nframes64_t /*frames*/) { #ifdef THIS_NEEDS_FIXING_FOR_V3 @@ -2661,10 +2986,10 @@ Route::shift (nframes64_t pos, nframes64_t frames) { Glib::RWLock::ReaderLock lm (redirect_lock); for (RedirectList::iterator i = _redirects.begin (); i != _redirects.end (); ++i) { - + set a; (*i)->what_has_automation (a); - + for (set::const_iterator j = a.begin (); j != a.end (); ++j) { AutomationList & al = (*i)->automation_list (*j); XMLNode &before = al.get_state (); @@ -2684,9 +3009,9 @@ Route::save_as_template (const string& path, const string& name) { XMLNode& node (state (false)); XMLTree tree; - + IO::set_name_in_state (*node.children().front(), name); - + tree.set_root (&node); return tree.write (path.c_str()); } @@ -2697,23 +3022,25 @@ Route::set_name (const string& str) { bool ret; string ioproc_name; + string name; + + name = Route::ensure_track_or_route_name (str, _session); + SessionObject::set_name (name); + + ret = (_input->set_name(name) && _output->set_name(name)); + + if (ret) { - if ((ret = IO::set_name (str)) == true) { Glib::RWLock::ReaderLock lm (_processor_lock); - + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - - /* rename all delivery objects to reflect our new name */ - boost::shared_ptr dp = boost::dynamic_pointer_cast (*i); + /* rename all I/O processors that have inputs or outputs */ + + boost::shared_ptr iop = boost::dynamic_pointer_cast (*i); - if (dp) { - string dp_name = str; - dp_name += '['; - dp_name += "XXX FIX ME XXX"; - dp_name += ']'; - - if (!dp->set_name (dp_name)) { + if (iop && (iop->output() || iop->input())) { + if (!iop->set_name (name)) { ret = false; } } @@ -2723,3 +3050,163 @@ Route::set_name (const string& str) return ret; } + +boost::shared_ptr +Route::internal_send_for (boost::shared_ptr target) const +{ + Glib::RWLock::ReaderLock lm (_processor_lock); + + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + boost::shared_ptr send; + + if ((send = boost::dynamic_pointer_cast(*i)) != 0) { + if (send->target_route() == target) { + return send; + } + } + } + + return boost::shared_ptr(); +} + +void +Route::set_phase_invert (bool yn) +{ + if (_phase_invert != yn) { + _phase_invert = 0xffff; // XXX all channels + phase_invert_changed (); /* EMIT SIGNAL */ + } +} + +bool +Route::phase_invert () const +{ + return _phase_invert != 0; +} + +void +Route::set_denormal_protection (bool yn) +{ + if (_denormal_protection != yn) { + _denormal_protection = yn; + denormal_protection_changed (); /* EMIT SIGNAL */ + } +} + +bool +Route::denormal_protection () const +{ + return _denormal_protection; +} + +void +Route::set_active (bool yn) +{ + if (_active != yn) { + _active = yn; + _input->set_active (yn); + _output->set_active (yn); + active_changed (); // EMIT SIGNAL + } +} + +void +Route::meter () +{ + Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK); + + assert (_meter); + + _meter->meter (); + + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + + boost::shared_ptr s; + boost::shared_ptr r; + + if ((s = boost::dynamic_pointer_cast (*i)) != 0) { + s->meter()->meter(); + } else if ((r = boost::dynamic_pointer_cast (*i)) != 0) { + r->meter()->meter (); + } + } +} + +boost::shared_ptr +Route::panner() const +{ + return _main_outs->panner(); +} + +boost::shared_ptr +Route::gain_control() const +{ + return _amp->gain_control(); +} + +boost::shared_ptr +Route::get_control (const Evoral::Parameter& param) +{ + /* either we own the control or .... */ + + boost::shared_ptr c = boost::dynamic_pointer_cast(control (param)); + + if (!c) { + + /* maybe one of our processors does or ... */ + + 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)->control (param))) != 0) { + break; + } + } + } + + if (!c) { + + /* nobody does so we'll make a new one */ + + c = boost::dynamic_pointer_cast(control_factory(param)); + add_control(c); + } + + 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) { + cerr << "check " << (*i)->name() << endl; + if (boost::dynamic_pointer_cast (*i)) { + if (n-- == 0) { + return *i; + } + } else { + cerr << "\tnot a send\n"; + } + } + + return boost::shared_ptr (); +}