X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=4f0f5dbc1ce9ee3bec0b28797a6758ec7a8c2195;hb=d6b36a13a127d9b152cfe12d596ea862773f9923;hp=e393a96648994dc759cd22210b0adcdef00edbd7;hpb=6ef7139fb33edac3820a0cabf77428ea28618380;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index e393a96648..4f0f5dbc1c 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -46,6 +46,7 @@ #include "ardour/capturing_processor.h" #include "ardour/debug.h" #include "ardour/delivery.h" +#include "ardour/event_type_map.h" #include "ardour/gain_control.h" #include "ardour/internal_return.h" #include "ardour/internal_send.h" @@ -74,7 +75,7 @@ #include "ardour/utils.h" #include "ardour/vca.h" -#include "i18n.h" +#include "pbd/i18n.h" using namespace std; using namespace ARDOUR; @@ -84,8 +85,8 @@ PBD::Signal3, boost::shared_ptr, Rout /** Base class for all routable/mixable objects (tracks and busses) */ Route::Route (Session& sess, string name, PresentationInfo::Flag flag, DataType default_type) - : GraphNode (sess._process_graph) - , Stripable (sess, name, PresentationInfo (flag)) + : Stripable (sess, name, PresentationInfo (flag)) + , GraphNode (sess._process_graph) , Muteable (sess, name) , Automatable (sess) , _active (true) @@ -117,6 +118,11 @@ Route::Route (Session& sess, string name, PresentationInfo::Flag flag, DataType processor_max_streams.reset(); } +boost::weak_ptr +Route::weakroute () { + return boost::weak_ptr (shared_from_this ()); +} + int Route::init () { @@ -229,10 +235,6 @@ Route::init () _monitor_control->activate (); } - if (is_master() || is_monitor() || is_auditioner()) { - _mute_master->set_solo_ignore (true); - } - /* now that we have _meter, its safe to connect to this */ { @@ -317,6 +319,8 @@ Route::process_output_buffers (BufferSet& bufs, return; } + _mute_control->automation_run (start_frame, nframes); + /* figure out if we're going to use gain automation */ if (gain_automation_ok) { _amp->set_gain_automation_buffer (_session.gain_automation_buffer ()); @@ -413,6 +417,7 @@ Route::process_output_buffers (BufferSet& bufs, bool const meter_already_run = metering_state() == MeteringInput; framecnt_t latency = 0; + const double speed = _session.transport_speed (); for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { @@ -442,8 +447,13 @@ Route::process_output_buffers (BufferSet& bufs, if (boost::dynamic_pointer_cast(*i) != 0) { boost::dynamic_pointer_cast(*i)->set_delay_in(_signal_latency - latency); } + if (boost::dynamic_pointer_cast(*i) != 0) { + const framecnt_t longest_session_latency = _initial_delay + _signal_latency; + boost::dynamic_pointer_cast(*i)->set_sidechain_latency ( + _initial_delay + latency, longest_session_latency - latency); + } - (*i)->run (bufs, start_frame - latency, end_frame - latency, nframes, *i != _processors.back()); + (*i)->run (bufs, start_frame - latency, end_frame - latency, speed, nframes, *i != _processors.back()); bufs.set_count ((*i)->output_streams()); if ((*i)->active ()) { @@ -471,6 +481,7 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes, _trim->setup_gain_automation (start, start + nframes, nframes); latency = 0; + const double speed = _session.transport_speed (); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (!include_endpoint && (*i) == endpoint) { @@ -493,7 +504,7 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes, */ if ((*i) == _main_outs) { assert ((*i)->does_routing()); - (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true); + (*i)->run (buffers, start - latency, start - latency + nframes, speed, nframes, true); buffers.set_count ((*i)->output_streams()); } @@ -501,7 +512,7 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes, * Also don't bother with metering. */ if (!(*i)->does_routing() && !boost::dynamic_pointer_cast(*i)) { - (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true); + (*i)->run (buffers, start - latency, start - latency + nframes, 1.0, nframes, true); buffers.set_count ((*i)->output_streams()); latency += (*i)->signal_latency (); } @@ -617,6 +628,9 @@ void Route::set_listen (bool yn) { if (_monitor_send) { + if (_monitor_send->active() == yn) { + return; + } if (yn) { _monitor_send->activate (); } else { @@ -826,6 +840,7 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || prop->value() == "windows-vst" || + prop->value() == "mac-vst" || prop->value() == "lxvst" || prop->value() == "audiounit") { @@ -890,6 +905,13 @@ int Route::add_processors (const ProcessorList& others, boost::shared_ptr before, ProcessorStreams* err) { ProcessorList::iterator loc; + boost::shared_ptr fanout; + + if (g_atomic_int_get (&_pending_process_reorder)) { + /* we need to flush any pending re-order changes */ + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); + apply_processor_changes_rt (); + } if (before) { loc = find(_processors.begin(), _processors.end(), before); @@ -948,7 +970,8 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr if (flags != None) { boost::optional rv = PluginSetup (shared_from_this (), pi, flags); /* EMIT SIGNAL */ - switch (rv.get_value_or (0)) { + int mode = rv.get_value_or (0); + switch (mode & 3) { case 1: to_skip.push_back (*i); // don't add this one; break; @@ -959,6 +982,9 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr default: break; } + if ((mode & 5) == 4) { + fanout = pi; + } } } @@ -1009,7 +1035,12 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr } } + if (pi && pi->has_sidechain ()) { + pi->sidechain_input ()->changed.connect_same_thread (*this, boost::bind (&Route::sidechain_change_handler, this, _1, _2)); + } + if ((*i)->active()) { + // emit ActiveChanged() and latency_changed() if needed (*i)->activate (); } @@ -1040,6 +1071,11 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ set_processor_positions (); + if (fanout && fanout->configured () + && fanout->output_streams().n_audio() > 2 + && boost::dynamic_pointer_cast (the_instrument ()) == fanout) { + fan_out (); /* EMIT SIGNAL */ + } return 0; } @@ -1068,7 +1104,7 @@ Route::disable_processors (Placement p) placement_range(p, start, end); for (ProcessorList::iterator i = start; i != end; ++i) { - (*i)->deactivate (); + (*i)->enable (false); } _session.set_dirty (); @@ -1082,7 +1118,7 @@ Route::disable_processors () Glib::Threads::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - (*i)->deactivate (); + (*i)->enable (false); } _session.set_dirty (); @@ -1101,7 +1137,7 @@ Route::disable_plugins (Placement p) for (ProcessorList::iterator i = start; i != end; ++i) { if (boost::dynamic_pointer_cast (*i)) { - (*i)->deactivate (); + (*i)->enable (false); } } @@ -1117,7 +1153,7 @@ Route::disable_plugins () for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (boost::dynamic_pointer_cast (*i)) { - (*i)->deactivate (); + (*i)->enable (false); } } @@ -1140,9 +1176,17 @@ Route::ab_plugins (bool forward) if (!boost::dynamic_pointer_cast (*i)) { continue; } + if (!(*i)->display_to_user ()) { + continue; + } +#ifdef MIXBUS + if (boost::dynamic_pointer_cast (*i)->is_channelstrip()) { + continue; + } +#endif - if ((*i)->active()) { - (*i)->deactivate (); + if ((*i)->enabled ()) { + (*i)->enable (false); (*i)->set_next_ab_is_active (true); } else { (*i)->set_next_ab_is_active (false); @@ -1154,16 +1198,19 @@ Route::ab_plugins (bool forward) /* backward = if the redirect was marked to go active on the next ab, do so */ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if (!boost::dynamic_pointer_cast (*i)) { continue; } - - if ((*i)->get_next_ab_is_active()) { - (*i)->activate (); - } else { - (*i)->deactivate (); + if (!(*i)->display_to_user ()) { + continue; + } +#ifdef MIXBUS + if (boost::dynamic_pointer_cast (*i)->is_channelstrip()) { + continue; } +#endif + + (*i)->enable ((*i)->get_next_ab_is_active ()); } } @@ -1404,7 +1451,7 @@ Route::replace_processor (boost::shared_ptr old, boost::shared_ptractive (); + bool enable = old->enabled (); for (i = _processors.begin(); i != _processors.end(); ) { if (*i == old) { @@ -1448,7 +1495,7 @@ Route::replace_processor (boost::shared_ptr old, boost::shared_ptractivate (); + sub->enable (true); } sub->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false)); @@ -1617,8 +1664,8 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err) if (boost::dynamic_pointer_cast (*p) && boost::dynamic_pointer_cast (*p)->role() == Delivery::Main - && !(is_monitor() || is_auditioner()) - && ( _strict_io || Profile->get_mixbus ())) { + && !is_auditioner() + && (is_monitor() || _strict_io || Profile->get_mixbus ())) { /* with strict I/O the panner + output are forced to * follow the last processor's output. * @@ -1630,8 +1677,9 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err) * Delivery::configure_io() will do the actual removal * by calling _output->ensure_io() */ - if (!is_master() && _session.master_out ()) { - /* ..but at least as many as there are master-inputs */ + if (!is_master() && _session.master_out () && in.n_audio() > 0) { + /* ..but at least as many as there are master-inputs, if + * the delivery is dealing with audio */ // XXX this may need special-casing for mixbus (master-outputs) // and should maybe be a preference anyway ?! out = ChanCount::max (in, _session.master_out ()->n_inputs ()); @@ -1646,13 +1694,21 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err) if (is_monitor()) { // restriction for Monitor Section Processors if (in.n_audio() != out.n_audio() || out.n_midi() > 0) { - /* do not allow to add/remove channels (for now) - * The Monitor follows the master-bus and has no panner (unpan) - * but do allow processors with midi-in to be added (e.g VSTs with control that - * will remain unconnected) + /* Note: The Monitor follows the master-bus and has no panner. + * + * The general idea is to only allow plugins that retain the channel-count + * and plugins with MIDI in (e.g VSTs with control that will remain unconnected). + * Then again 5.1 in, monitor stereo is a valid use-case. + * + * and worse: we only refuse adding plugins *here*. + * + * 1) stereo-master, stereo-mon, add a stereo-plugin, OK + * 2) change master-bus, add a channel + * 2a) monitor-secion follows + * 3) monitor processors fail to re-reconfigure (stereo plugin) + * 4) re-load session, monitor-processor remains unconfigured, crash. */ - DEBUG_TRACE (DEBUG::Processors, "Monitor: Channel configuration not allowed.\n"); - return list > (); + DEBUG_TRACE (DEBUG::Processors, "Monitor: Channel configuration change.\n"); } if (boost::dynamic_pointer_cast (*p)) { // internal sends make no sense, only feedback @@ -1822,12 +1878,15 @@ Route::all_visible_processors_active (bool state) if (!(*i)->display_to_user() || boost::dynamic_pointer_cast (*i)) { continue; } - - if (state) { - (*i)->activate (); - } else { - (*i)->deactivate (); +#ifdef MIXBUS + boost::shared_ptr pi; + if (0 != (pi = boost::dynamic_pointer_cast(*i))) { + if (pi->is_channelstrip ()) { + continue; + } } +#endif + (*i)->enable (state); } _session.set_dirty (); @@ -1950,6 +2009,35 @@ Route::apply_processor_order (const ProcessorList& new_order) maybe_note_meter_position (); } +void +Route::move_instrument_down (bool postfader) +{ + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); + ProcessorList new_order; + boost::shared_ptr instrument; + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + boost::shared_ptr pi = boost::dynamic_pointer_cast(*i); + if (pi && pi->plugin ()->get_info ()->is_instrument ()) { + instrument = *i; + } else if (instrument && *i == _amp) { + if (postfader) { + new_order.push_back (*i); + new_order.push_back (instrument); + } else { + new_order.push_back (instrument); + new_order.push_back (*i); + } + } else { + new_order.push_back (*i); + } + } + if (!instrument) { + return; + } + lm.release (); + reorder_processors (new_order, 0); +} + int Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err) { @@ -2223,7 +2311,6 @@ Route::state(bool full_state) { LocaleGuard lg; if (!_session._template_state_dir.empty()) { - assert (!full_state); // only for templates foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), _session._template_state_dir)); } @@ -2237,7 +2324,7 @@ Route::state(bool full_state) node->add_property("default-type", _default_type.to_string()); node->add_property ("strict-io", _strict_io); - Stripable::add_state (*node); + node->add_child_nocopy (_presentation_info.get_state()); node->add_property("active", _active?"yes":"no"); string p; @@ -2350,10 +2437,6 @@ Route::set_state (const XMLNode& node, int version) _strict_io = string_is_affirmative (prop->value()); } - if (!can_solo()) { - _mute_master->set_solo_ignore (true); - } - if (is_monitor()) { /* monitor bus does not get a panner, but if (re)created via XML, it will already have one by the time we @@ -2392,22 +2475,6 @@ Route::set_state (const XMLNode& node, int version) } else { warning << string_compose (_("Pannable state found for route (%1) without a panner!"), name()) << endmsg; } - } else if (child->name() == Controllable::xml_node_name) { - if ((prop = child->property (X_("name"))) == 0) { - continue; - } - - if (prop->value() == _gain_control->name()) { - _gain_control->set_state (*child, version); - } else if (prop->value() == _solo_control->name()) { - _solo_control->set_state (*child, version); - } else if (prop->value() == _solo_safe_control->name()) { - _solo_safe_control->set_state (*child, version); - } else if (prop->value() == _solo_isolate_control->name()) { - _solo_isolate_control->set_state (*child, version); - } else if (prop->value() == _solo_control->name()) { - _mute_control->set_state (*child, version); - } } else if (child->name() == Slavable::xml_node_name) { Slavable::set_state (*child, version); } @@ -2436,6 +2503,11 @@ Route::set_state (const XMLNode& node, int version) set_denormal_protection (string_is_affirmative (prop->value())); } + /* convert old 3001 state */ + if ((prop = node.property (X_("phase-invert"))) != 0) { + _phase_control->set_phase_invert (boost::dynamic_bitset<> (prop->value ())); + } + if ((prop = node.property (X_("active"))) != 0) { bool yn = string_is_affirmative (prop->value()); set_active (yn, this); @@ -2465,13 +2537,32 @@ Route::set_state (const XMLNode& node, int version) XMLNode *cmt = *(child->children().begin()); _comment = cmt->content(); - } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) { - if (prop->value() == "solo") { + } else if (child->name() == Controllable::xml_node_name) { + if ((prop = child->property (X_("name"))) == 0) { + continue; + } + + if (prop->value() == _gain_control->name()) { + _gain_control->set_state (*child, version); + } else if (prop->value() == _solo_control->name()) { _solo_control->set_state (*child, version); - } else if (prop->value() == "mute") { + } else if (prop->value() == _solo_safe_control->name()) { + _solo_safe_control->set_state (*child, version); + } else if (prop->value() == _solo_isolate_control->name()) { + _solo_isolate_control->set_state (*child, version); + } else if (prop->value() == _mute_control->name()) { _mute_control->set_state (*child, version); + } else if (prop->value() == _phase_control->name()) { + _phase_control->set_state (*child, version); + } else { + Evoral::Parameter p = EventTypeMap::instance().from_symbol (prop->value()); + if (p.type () >= MidiCCAutomation && p.type () < MidiSystemExclusiveAutomation) { + boost::shared_ptr ac = automation_control (p, true); + if (ac) { + ac->set_state (*child, version); + } + } } - } else if (child->name() == MuteMaster::xml_node_name) { _mute_master->set_state (*child, version); @@ -2505,10 +2596,6 @@ Route::set_state_2X (const XMLNode& node, int version) Stripable::set_state (node, version); - if (is_master() || is_monitor() || is_auditioner()) { - _mute_master->set_solo_ignore (true); - } - if ((prop = node.property (X_("denormal-protection"))) != 0) { set_denormal_protection (string_is_affirmative (prop->value())); } @@ -2765,6 +2852,7 @@ Route::set_processor_state (const XMLNode& node) } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || prop->value() == "windows-vst" || + prop->value() == "mac-vst" || prop->value() == "lxvst" || prop->value() == "luaproc" || prop->value() == "audiounit") { @@ -2828,6 +2916,11 @@ Route::set_processor_state (const XMLNode& node) { Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); + /* re-assign _processors w/o process-lock. + * if there's an IO-processor present in _processors but + * not in new_order, it will be deleted and ~IO takes + * a process lock. + */ _processors = new_order; if (must_configure) { @@ -2878,6 +2971,8 @@ Route::silence_unlocked (framecnt_t nframes) { /* Must be called with the processor lock held */ + const framepos_t now = _session.transport_frame (); + if (!_silent) { _output->silence (nframes); @@ -2890,7 +2985,7 @@ Route::silence_unlocked (framecnt_t nframes) continue; } - (*i)->silence (nframes); + (*i)->silence (nframes, now); } if (nframes == _session.get_block_size()) { @@ -2985,8 +3080,7 @@ Route::add_aux_send (boost::shared_ptr route, boost::shared_ptrprocess_lock ()); - boost::shared_ptr sendpan (new Pannable (_session)); - listener.reset (new InternalSend (_session, sendpan, _mute_master, boost::dynamic_pointer_cast(shared_from_this()), route, Delivery::Aux)); + listener.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast(shared_from_this()), route, Delivery::Aux)); } add_processor (listener, before); @@ -3164,6 +3258,11 @@ Route::direct_feeds_according_to_reality (boost::shared_ptr other, bool* if (iop != 0) { boost::shared_ptr iop_out = iop->output(); + if (other.get() == this && iop_out && iop->input() && iop_out->connected_to (iop->input())) { + // TODO this needs a delaylines in the Insert to align connections (!) + DEBUG_TRACE (DEBUG::Graph, string_compose ("\tIOP %1 does feed its own return (%2)\n", iop->name(), other->name())); + continue; + } if ((iop_out && other->all_inputs().fed_by (iop_out)) || iop->feeds (other)) { DEBUG_TRACE (DEBUG::Graph, string_compose ("\tIOP %1 does feed %2\n", iop->name(), other->name())); if (via_send_only) { @@ -3318,46 +3417,56 @@ Route::output_change_handler (IOChange change, void * /*src*/) io_changed (); /* EMIT SIGNAL */ } - if (_solo_control->soloed_by_others_downstream()) { - int sbod = 0; - /* checking all all downstream routes for - * explicit of implict solo is a rather drastic measure, - * ideally the input_change_handler() of the other route - * would propagate the change to us. + if ((change.type & IOChange::ConnectionsChanged)) { + + /* do this ONLY if connections have changed. Configuration + * changes do not, by themselves alter solo upstream or + * downstream status. */ - boost::shared_ptr routes = _session.get_routes (); - if (_output->connected()) { - for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { - if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { - continue; - } - bool sends_only; - bool does_feed = direct_feeds_according_to_reality (*i, &sends_only); - if (does_feed && !sends_only) { - if ((*i)->soloed()) { - ++sbod; - break; + + if (_solo_control->soloed_by_others_downstream()) { + int sbod = 0; + /* checking all all downstream routes for + * explicit of implict solo is a rather drastic measure, + * ideally the input_change_handler() of the other route + * would propagate the change to us. + */ + boost::shared_ptr routes = _session.get_routes (); + if (_output->connected()) { + for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { + if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + continue; + } + bool sends_only; + bool does_feed = direct_feeds_according_to_reality (*i, &sends_only); + if (does_feed && !sends_only) { + if ((*i)->soloed()) { + ++sbod; + break; + } } } } - } - int delta = sbod - _solo_control->soloed_by_others_downstream(); - if (delta <= 0) { - // do not allow new connections to change implicit solo (no propagation) - _solo_control->mod_solo_by_others_downstream (delta); - // Session::route_solo_changed() does not propagate indirect solo-changes - // propagate upstream to tracks - for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { - if ((*i).get() == this || !can_solo()) { - continue; - } - bool sends_only; - bool does_feed = (*i)->feeds (shared_from_this(), &sends_only); - if (delta != 0 && does_feed && !sends_only) { - (*i)->solo_control()->mod_solo_by_others_downstream (delta); + + int delta = sbod - _solo_control->soloed_by_others_downstream(); + if (delta <= 0) { + // do not allow new connections to change implicit solo (no propagation) + _solo_control->mod_solo_by_others_downstream (delta); + // Session::route_solo_changed() does not propagate indirect solo-changes + // propagate upstream to tracks + boost::shared_ptr shared_this = shared_from_this(); + for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { + if ((*i).get() == this || !can_solo()) { + continue; + } + bool sends_only; + bool does_feed = (*i)->feeds (shared_this, &sends_only); + if (delta != 0 && does_feed && !sends_only) { + (*i)->solo_control()->mod_solo_by_others_downstream (delta); + } } - } + } } } } @@ -3382,6 +3491,22 @@ Route::pans_required () const return max (n_inputs ().n_audio(), processor_max_streams.n_audio()); } +void +Route::flush_processor_buffers_locked (framecnt_t nframes) +{ + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + boost::shared_ptr d = boost::dynamic_pointer_cast (*i); + if (d) { + d->flush_buffers (nframes); + } else { + boost::shared_ptr p = boost::dynamic_pointer_cast (*i); + if (p) { + p->flush_buffers (nframes); + } + } + } +} + int Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool session_state_changing) { @@ -3391,13 +3516,16 @@ Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, return 0; } - if (n_outputs().n_total() == 0) { - return 0; - } + //MB has its own output path, regardless of physical outs + if (!Profile->get_mixbus()) { + if (n_outputs().n_total() == 0) { + return 0; + } - if (!_active || n_inputs() == ChanCount::ZERO) { - silence_unlocked (nframes); - return 0; + if (!_active || n_inputs() == ChanCount::ZERO) { + silence_unlocked (nframes); + return 0; + } } if (session_state_changing) { @@ -3420,13 +3548,15 @@ Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, fill_buffers_with_input (bufs, _input, nframes); if (_meter_point == MeterInput) { - _meter->run (bufs, start_frame, end_frame, nframes, true); + _meter->run (bufs, start_frame, end_frame, 0.0, nframes, true); } _amp->apply_gain_automation (false); _trim->apply_gain_automation (false); passthru (bufs, start_frame, end_frame, nframes, 0); + flush_processor_buffers_locked (nframes); + return 0; } @@ -3438,13 +3568,16 @@ Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, in return 0; } - if (n_outputs().n_total() == 0) { - return 0; - } + //MB has its own signal path, regardless of I/O -- TODO handle !active for tracks & aux-busses) + if (!Profile->get_mixbus()) { + if (n_outputs().n_total() == 0) { + return 0; + } - if (!_active || n_inputs().n_total() == 0) { - silence_unlocked (nframes); - return 0; + if (!_active || n_inputs().n_total() == 0) { + silence_unlocked (nframes); + return 0; + } } framepos_t unused = 0; @@ -3460,11 +3593,13 @@ Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, in fill_buffers_with_input (bufs, _input, nframes); if (_meter_point == MeterInput) { - _meter->run (bufs, start_frame, end_frame, nframes, true); + _meter->run (bufs, start_frame, end_frame, 1.0, nframes, true); } passthru (bufs, start_frame, end_frame, nframes, declick); + flush_processor_buffers_locked (nframes); + return 0; } @@ -3472,16 +3607,13 @@ int Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/, bool& /* need_butler */) { silence (nframes); + flush_processor_buffers_locked (nframes); return 0; } void Route::flush_processors () { - /* XXX shouldn't really try to take this lock, since - this is called from the RT audio thread. - */ - Glib::Threads::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { @@ -3695,7 +3827,9 @@ Route::add_export_point() Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lw (_processor_lock); - _capturing_processor.reset (new CapturingProcessor (_session)); + // this aligns all tracks; but not tracks + busses + assert (_session.worst_track_latency () >= _initial_delay); + _capturing_processor.reset (new CapturingProcessor (_session, _session.worst_track_latency () - _initial_delay)); _capturing_processor->activate (); configure_processors_unlocked (0, &lw); @@ -3913,6 +4047,10 @@ Route::save_as_template (const string& path, const string& name) bool Route::set_name (const string& str) { + if (str.empty ()) { + return false; + } + if (str == name()) { return true; } @@ -4025,6 +4163,7 @@ Route::set_active (bool yn, void* src) _active = yn; _input->set_active (yn); _output->set_active (yn); + flush_processors (); active_changed (); // EMIT SIGNAL _session.set_dirty (); } @@ -4473,10 +4612,17 @@ Route::setup_invisible_processors () new_processors.insert (amp, _monitor_control); } + /* TRIM CONTROL */ + + if (_trim && _trim->active()) { + assert (!_trim->display_to_user ()); + new_processors.push_front (_trim); + } + /* INTERNAL RETURN */ - /* doing this here means that any monitor control will come just after - the return. + /* doing this here means that any monitor control will come after + the return and trim. */ if (_intreturn) { @@ -4484,10 +4630,6 @@ Route::setup_invisible_processors () new_processors.push_front (_intreturn); } - if (_trim && _trim->active()) { - assert (!_trim->display_to_user ()); - new_processors.push_front (_trim); - } /* EXPORT PROCESSOR */ if (_capturing_processor) { @@ -4498,8 +4640,8 @@ Route::setup_invisible_processors () _processors = new_processors; for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if (!(*i)->display_to_user () && !(*i)->active () && (*i) != _monitor_send) { - (*i)->activate (); + if (!(*i)->display_to_user () && !(*i)->enabled () && (*i) != _monitor_send) { + (*i)->enable (true); } } @@ -4753,11 +4895,12 @@ boost::shared_ptr Route::pan_azimuth_control() const { #ifdef MIXBUS +# undef MIXBUS_PORTS_H +# include "../../gtk2_ardour/mixbus_ports.h" boost::shared_ptr plug = ch_post(); if (!plug) { return boost::shared_ptr(); } - const uint32_t port_channel_post_pan = 2; // gtk2_ardour/mixbus_ports.h return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_pan))); #else if (!_pannable || !panner()) { @@ -4785,6 +4928,12 @@ Route::pan_elevation_control() const boost::shared_ptr Route::pan_width_control() const { +#ifdef MIXBUS + if (mixbus() && _ch_pre) { + //mono blend + return boost::dynamic_pointer_cast(_ch_pre->control(Evoral::Parameter(PluginAutomation, 0, 5))); + } +#endif if (Profile->get_mixbus() || !_pannable || !panner()) { return boost::shared_ptr(); } @@ -4832,7 +4981,15 @@ uint32_t Route::eq_band_cnt () const { if (Profile->get_mixbus()) { +#ifdef MIXBUS32C + if (is_master() || mixbus()) { + return 3; + } else { + return 4; + } +#else return 3; +#endif } else { /* Ardour has no well-known EQ object */ return 0; @@ -4850,30 +5007,33 @@ Route::eq_gain_controllable (uint32_t band) const } uint32_t port_number; - switch (band) { - case 0: - if (is_master() || mixbus()) { - port_number = 4; - } else { - port_number = 8; + if (is_master() || mixbus()) { + switch (band) { + case 0: port_number = 4; break; + case 1: port_number = 3; break; + case 2: port_number = 2; break; + default: + return boost::shared_ptr(); } - break; - case 1: - if (is_master() || mixbus()) { - port_number = 3; - } else { - port_number = 6; + } else { +#ifdef MIXBUS32C + switch (band) { + case 0: port_number = 14; break; + case 1: port_number = 12; break; + case 2: port_number = 10; break; + case 3: port_number = 8; break; + default: + return boost::shared_ptr(); } - break; - case 2: - if (is_master() || mixbus()) { - port_number = 2; - } else { - port_number = 4; +#else + switch (band) { + case 0: port_number = 8; break; + case 1: port_number = 6; break; + case 2: port_number = 4; break; + default: + return boost::shared_ptr(); } - break; - default: - return boost::shared_ptr(); +#endif } return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number))); @@ -4885,7 +5045,6 @@ boost::shared_ptr Route::eq_freq_controllable (uint32_t band) const { #ifdef MIXBUS - if (mixbus() || is_master()) { /* no frequency controls for mixbusses or master */ return boost::shared_ptr(); @@ -4898,19 +5057,24 @@ Route::eq_freq_controllable (uint32_t band) const } uint32_t port_number; +#ifdef MIXBUS32C switch (band) { - case 0: - port_number = 7; - break; - case 1: - port_number = 5; - break; - case 2: - port_number = 3; - break; - default: - return boost::shared_ptr(); + case 0: port_number = 13; break; + case 1: port_number = 11; break; + case 2: port_number = 9; break; + case 3: port_number = 7; break; + default: + return boost::shared_ptr(); } +#else + switch (band) { + case 0: port_number = 7; break; + case 1: port_number = 5; break; + case 2: port_number = 3; break; + default: + return boost::shared_ptr(); + } +#endif return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number))); #else @@ -4952,11 +5116,15 @@ Route::eq_hpf_controllable () const #ifdef MIXBUS boost::shared_ptr eq = ch_eq(); - if (!eq) { + if (is_master() || mixbus() || !eq) { return boost::shared_ptr(); } - +#ifdef MIXBUS32C + return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 3))); +#else return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 2))); +#endif + #else return boost::shared_ptr(); #endif @@ -4965,20 +5133,30 @@ Route::eq_hpf_controllable () const string Route::eq_band_name (uint32_t band) const { +#ifdef MIXBUS32C + if (is_master() || mixbus()) { +#endif if (Profile->get_mixbus()) { switch (band) { - case 0: - return _("lo"); - case 1: - return _("mid"); - case 2: - return _("hi"); - default: - return string(); + case 0: return _("lo"); + case 1: return _("mid"); + case 2: return _("hi"); + default: return string(); } } else { return string (); } +#ifdef MIXBUS32C + } else { + switch (band) { + case 0: return _("lo"); + case 1: return _("lo mid"); + case 2: return _("hi mid"); + case 3: return _("hi"); + default: return string(); + } + } +#endif } boost::shared_ptr @@ -5057,19 +5235,23 @@ Route::comp_makeup_controllable () const return boost::shared_ptr(); #endif } -boost::shared_ptr +boost::shared_ptr Route::comp_redux_controllable () const { #ifdef MIXBUS boost::shared_ptr comp = ch_comp(); if (!comp) { - return boost::shared_ptr(); + return boost::shared_ptr(); + } + if (is_master()) { + return comp->control_output (2); + } else { + return comp->control_output (6); } - return boost::dynamic_pointer_cast (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 6))); #else - return boost::shared_ptr(); + return boost::shared_ptr(); #endif } @@ -5117,71 +5299,119 @@ boost::shared_ptr Route::send_level_controllable (uint32_t n) const { #ifdef MIXBUS +# undef MIXBUS_PORTS_H +# include "../../gtk2_ardour/mixbus_ports.h" boost::shared_ptr plug = ch_post(); - if (!plug) { - return boost::shared_ptr(); - } + if (plug) { + uint32_t port_id = 0; + switch (n) { + case 0: port_id = port_channel_post_aux1_level; break; + case 1: port_id = port_channel_post_aux2_level; break; + case 2: port_id = port_channel_post_aux3_level; break; + case 3: port_id = port_channel_post_aux4_level; break; + case 4: port_id = port_channel_post_aux5_level; break; + case 5: port_id = port_channel_post_aux6_level; break; + case 6: port_id = port_channel_post_aux7_level; break; + case 7: port_id = port_channel_post_aux8_level; break; +# ifdef MIXBUS32C + case 8: port_id = port_channel_post_aux9_level; break; + case 9: port_id = port_channel_post_aux10_level; break; + case 10: port_id = port_channel_post_aux11_level; break; + case 11: port_id = port_channel_post_aux12_level; break; +# endif + default: + break; + } - if (n >= 8) { - /* no such bus */ - return boost::shared_ptr(); + if (port_id > 0) { + return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id))); + } +# ifdef MIXBUS32C + assert (n > 11); + n -= 12; +# else + assert (n > 7); + n -= 8; +# endif } - - const uint32_t port_id = port_channel_post_aux1_level + (2*n); // gtk2_ardour/mixbus_ports.h - return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id))); -#else +#endif boost::shared_ptr s = boost::dynamic_pointer_cast(nth_send (n)); if (!s) { return boost::shared_ptr(); } return s->gain_control (); -#endif } boost::shared_ptr Route::send_enable_controllable (uint32_t n) const { #ifdef MIXBUS +# undef MIXBUS_PORTS_H +# include "../../gtk2_ardour/mixbus_ports.h" boost::shared_ptr plug = ch_post(); - if (!plug) { - return boost::shared_ptr(); - } + if (plug) { + uint32_t port_id = 0; + switch (n) { + case 0: port_id = port_channel_post_aux1_asgn; break; + case 1: port_id = port_channel_post_aux2_asgn; break; + case 2: port_id = port_channel_post_aux3_asgn; break; + case 3: port_id = port_channel_post_aux4_asgn; break; + case 4: port_id = port_channel_post_aux5_asgn; break; + case 5: port_id = port_channel_post_aux6_asgn; break; + case 6: port_id = port_channel_post_aux7_asgn; break; + case 7: port_id = port_channel_post_aux8_asgn; break; +# ifdef MIXBUS32C + case 8: port_id = port_channel_post_aux9_asgn; break; + case 9: port_id = port_channel_post_aux10_asgn; break; + case 10: port_id = port_channel_post_aux11_asgn; break; + case 11: port_id = port_channel_post_aux12_asgn; break; +# endif + default: + break; + } - if (n >= 8) { - /* no such bus */ - return boost::shared_ptr(); + if (port_id > 0) { + return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id))); + } +# ifdef MIXBUS32C + assert (n > 11); + n -= 12; +# else + assert (n > 7); + n -= 8; +# endif } - - const uint32_t port_id = port_channel_post_aux1_asgn + (2*n); // gtk2_ardour/mixbus_ports.h - return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id))); -#else +#endif /* although Ardour sends have enable/disable as part of the Processor - API, it is not exposed as a controllable. - - XXX: we should fix this. - */ + * API, it is not exposed as a controllable. + * + * XXX: we should fix this (make it click-free, automatable enable-control) + */ return boost::shared_ptr(); -#endif } string Route::send_name (uint32_t n) const { #ifdef MIXBUS - if (n >= 8) { - return string(); +# ifdef MIXBUS32C + if (n < 12) { + return _session.get_mixbus (n)->name(); } - boost::shared_ptr r = _session.get_mixbus (n); - assert (r); - return r->name(); + n -= 12; #else + if (n < 8) { + return _session.get_mixbus (n)->name(); + } + n -= 8; +# endif +#endif boost::shared_ptr p = nth_send (n); if (p) { return p->name(); } else { return string(); } -#endif } boost::shared_ptr @@ -5192,12 +5422,24 @@ Route::master_send_enable_controllable () const if (!plug) { return boost::shared_ptr(); } +# undef MIXBUS_PORTS_H +# include "../../gtk2_ardour/mixbus_ports.h" return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_mstr_assign))); #else return boost::shared_ptr(); #endif } +bool +Route::slaved () const +{ + if (!_gain_control) { + return false; + } + /* just test one particular control, not all of them */ + return _gain_control->slaved (); +} + bool Route::slaved_to (boost::shared_ptr vca) const { @@ -5217,17 +5459,11 @@ Route::muted_by_others_soloing () const return false; } - return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated(); + return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated(); } void Route::clear_all_solo_state () { - double v = _solo_safe_control->get_value (); - _solo_control->clear_all_solo_state (); - - if (v != 0.0) { - _solo_safe_control->set_value (v, Controllable::NoGroup); - } }