X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=c6f588a79cfb120a02675896a5739310bb4fa4dd;hb=6542ce76d0740ccd505fd9326a7a4b2d737af663;hp=6ee40103f1e3a920b2fe81a79199b33a928a4664;hpb=e65636a0873dfe78af1625ea1c9d15d6c0e8b881;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 6ee40103f1..c6f588a79c 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -26,6 +26,8 @@ #include #include +#include + #include "pbd/xml++.h" #include "pbd/enumwriter.h" #include "pbd/memento_command.h" @@ -33,41 +35,33 @@ #include "pbd/convert.h" #include "pbd/boost_debug.h" -#include "evoral/Curve.hpp" - #include "ardour/amp.h" +#include "ardour/audio_buffer.h" #include "ardour/audio_port.h" #include "ardour/audioengine.h" #include "ardour/buffer.h" #include "ardour/buffer_set.h" -#include "ardour/configuration.h" -#include "ardour/cycle_timer.h" +#include "ardour/capturing_processor.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/internal_send.h" #include "ardour/meter.h" -#include "ardour/mix.h" +#include "ardour/midi_buffer.h" +#include "ardour/midi_port.h" #include "ardour/monitor_processor.h" #include "ardour/pannable.h" -#include "ardour/panner.h" #include "ardour/panner_shell.h" #include "ardour/plugin_insert.h" #include "ardour/port.h" #include "ardour/port_insert.h" #include "ardour/processor.h" -#include "ardour/profile.h" #include "ardour/route.h" #include "ardour/route_group.h" #include "ardour/send.h" #include "ardour/session.h" -#include "ardour/timestamps.h" -#include "ardour/utils.h" -#include "ardour/graph.h" #include "ardour/unknown_processor.h" -#include "ardour/capturing_processor.h" +#include "ardour/utils.h" #include "i18n.h" @@ -75,14 +69,13 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -uint32_t Route::order_key_cnt = 0; -PBD::Signal1 Route::SyncOrderKeys; +PBD::Signal1 Route::SyncOrderKeys; PBD::Signal0 Route::RemoteControlIDChange; Route::Route (Session& sess, string name, Flag flg, DataType default_type) : SessionObject (sess, name) , Automatable (sess) - , GraphNode( sess.route_graph ) + , GraphNode (sess._process_graph) , _active (true) , _signal_latency (0) , _initial_delay (0) @@ -90,6 +83,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) , _flags (flg) , _pending_declick (true) , _meter_point (MeterPostFader) + , _meter_type (MeterPeak) , _self_solo (false) , _soloed_by_others_upstream (0) , _soloed_by_others_downstream (0) @@ -104,9 +98,10 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) , _default_type (default_type) , _remote_control_id (0) , _in_configure_processors (false) + , _custom_meter_position_noted (false) + , _last_custom_meter_was_at_end (false) { processor_max_streams.reset(); - order_keys[N_("signal")] = order_key_cnt++; } int @@ -126,11 +121,7 @@ Route::init () /* panning */ if (!(_flags & Route::MonitorOut)) { - Pannable* p = new Pannable (_session); -#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - boost_debug_shared_ptr_mark_interesting (p, "Pannable"); -#endif - _pannable.reset (p); + _pannable.reset (new Pannable (_session)); } /* input and output objects */ @@ -141,6 +132,8 @@ Route::init () _input->changed.connect_same_thread (*this, boost::bind (&Route::input_change_handler, this, _1, _2)); _input->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::input_port_count_changing, this, _1)); + _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2)); + /* add amp processor */ _amp.reset (new Amp (_session)); @@ -150,7 +143,7 @@ Route::init () they will be added to _processors by setup_invisible_processors () */ - _meter.reset (new PeakMeter (_session)); + _meter.reset (new PeakMeter (_session, _name)); _meter->set_display_to_user (false); _meter->activate (); @@ -169,7 +162,7 @@ Route::init () _monitor_control->activate (); } - if (is_master() || is_monitor() || is_hidden()) { + if (is_master() || is_monitor() || is_auditioner()) { _mute_master->set_solo_ignore (true); } @@ -179,7 +172,7 @@ Route::init () { /* run a configure so that the invisible processors get set up */ - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); configure_processors (0); } @@ -199,7 +192,7 @@ Route::~Route () be half-destroyed by now */ - Glib::RWLock::WriterLock lm (_processor_lock); + Glib::Threads::RWLock::WriterLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->drop_references (); } @@ -210,9 +203,44 @@ Route::~Route () void Route::set_remote_control_id (uint32_t id, bool notify_class_listeners) { - if (id != _remote_control_id) { + if (Config->get_remote_model() != UserOrdered) { + return; + } + + set_remote_control_id_internal (id, notify_class_listeners); +} + +void +Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners) +{ + /* force IDs for master/monitor busses and prevent + any other route from accidentally getting these IDs + (i.e. legacy sessions) + */ + + if (is_master() && id != MasterBusRemoteControlID) { + id = MasterBusRemoteControlID; + } + + if (is_monitor() && id != MonitorBusRemoteControlID) { + id = MonitorBusRemoteControlID; + } + + if (id < 1) { + return; + } + + /* don't allow it to collide */ + + if (!is_master () && !is_monitor() && + (id == MasterBusRemoteControlID || id == MonitorBusRemoteControlID)) { + id += MonitorBusRemoteControlID; + } + + if (id != remote_control_id()) { _remote_control_id = id; RemoteControlIDChanged (); + if (notify_class_listeners) { RemoteControlIDChange (); } @@ -222,88 +250,101 @@ Route::set_remote_control_id (uint32_t id, bool notify_class_listeners) uint32_t Route::remote_control_id() const { + if (is_master()) { + return MasterBusRemoteControlID; + } + + if (is_monitor()) { + return MonitorBusRemoteControlID; + } + return _remote_control_id; } -int32_t -Route::order_key (std::string const & name) const +bool +Route::has_order_key (RouteSortOrderKey key) const +{ + return (order_keys.find (key) != order_keys.end()); +} + +uint32_t +Route::order_key (RouteSortOrderKey key) const { - OrderKeys::const_iterator i = order_keys.find (name); + OrderKeys::const_iterator i = order_keys.find (key); + if (i == order_keys.end()) { - return -1; + return 0; } return i->second; } void -Route::set_order_key (std::string const & name, int32_t n) +Route::sync_order_keys (RouteSortOrderKey base) { - bool changed = false; + /* this is called after changes to 1 or more route order keys have been + * made, and we want to sync up. + */ - /* This method looks more complicated than it should, but - it's important that we don't emit order_key_changed unless - it actually has, as expensive things happen on receipt of that - signal. - */ + OrderKeys::iterator i = order_keys.find (base); - if (order_keys.find(name) == order_keys.end() || order_keys[name] != n) { - order_keys[name] = n; - changed = true; + if (i == order_keys.end()) { + return; } - if (Config->get_sync_all_route_ordering()) { - for (OrderKeys::iterator x = order_keys.begin(); x != order_keys.end(); ++x) { - if (x->second != n) { - x->second = n; - changed = true; - } - } - } + for (OrderKeys::iterator k = order_keys.begin(); k != order_keys.end(); ++k) { - if (changed) { - order_key_changed (); /* EMIT SIGNAL */ - _session.set_dirty (); + if (k->first != base) { + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1 set key for %2 to %3 from %4\n", + name(), + enum_2_string (k->first), + i->second, + enum_2_string (base))); + + k->second = i->second; + } } } -/** 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 (std::string const & base) +Route::set_remote_control_id_from_order_key (RouteSortOrderKey /*key*/, uint32_t rid) { - if (order_keys.empty()) { + if (is_master() || is_monitor() || is_auditioner()) { + /* hard-coded remote IDs, or no remote ID */ return; } - OrderKeys::iterator i; - int32_t key; - - if ((i = order_keys.find (base)) == order_keys.end()) { - /* key doesn't exist, use the first existing key (during session initialization) */ - i = order_keys.begin(); - key = i->second; - ++i; - } else { - /* key exists - use it and reset all others (actually, itself included) */ - key = i->second; - i = order_keys.begin(); + if (_remote_control_id != rid) { + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1: set edit-based RID to %2\n", name(), rid)); + _remote_control_id = rid; + RemoteControlIDChanged (); /* EMIT SIGNAL (per-route) */ } - bool changed = false; + /* don't emit the class-level RID signal RemoteControlIDChange here, + leave that to the entity that changed the order key, so that we + don't get lots of emissions for no good reasons (e.g. when changing + all route order keys). - for (; i != order_keys.end(); ++i) { - if (i->second != key) { - i->second = key; - changed = true; - } - } + See Session::sync_remote_id_from_order_keys() for the (primary|only) + spot where that is emitted. + */ +} - if (changed) { - order_key_changed (); /* EMIT SIGNAL */ +void +Route::set_order_key (RouteSortOrderKey key, uint32_t n) +{ + OrderKeys::iterator i = order_keys.find (key); + + if (i != order_keys.end() && i->second == n) { + return; } + + order_keys[key] = n; + + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1 order key %2 set to %3\n", + name(), enum_2_string (key), order_key (key))); + + _session.set_dirty (); } string @@ -401,23 +442,24 @@ Route::maybe_declick (BufferSet&, framecnt_t, int) void Route::process_output_buffers (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, - bool /*with_processors*/, int declick, - bool gain_automation_ok) + int declick, bool gain_automation_ok) { - bool monitor = should_monitor (); - - bufs.is_silent (false); + bufs.set_is_silent (false); /* figure out if we're going to use gain automation */ if (gain_automation_ok) { + _amp->set_gain_automation_buffer (_session.gain_automation_buffer ()); _amp->setup_gain_automation (start_frame, end_frame, nframes); } else { _amp->apply_gain_automation (false); } - /* tell main outs what to do about monitoring */ - _main_outs->no_outs_cuz_we_no_monitor (!monitor); + /* Tell main outs what to do about monitoring. We do this so that + on a transition between monitoring states we get a de-clicking gain + change in the _main_outs delivery. + */ + _main_outs->no_outs_cuz_we_no_monitor (monitoring_state () == MonitoringSilence); /* ------------------------------------------------------------------------------------------- GLOBAL DECLICK (for transport changes etc.) @@ -482,23 +524,31 @@ Route::process_output_buffers (BufferSet& bufs, and go .... ----------------------------------------------------------------------------------------- */ + /* set this to be true if the meter will already have been ::run() earlier */ + bool const meter_already_run = metering_state() == MeteringInput; + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if (boost::dynamic_pointer_cast (*i)) { - break; + if (meter_already_run && boost::dynamic_pointer_cast (*i)) { + /* don't ::run() the meter, otherwise it will have its previous peak corrupted */ + continue; } #ifndef NDEBUG /* if it has any inputs, make sure they match */ - if ((*i)->input_streams() != ChanCount::ZERO) { + if (boost::dynamic_pointer_cast (*i) == 0 && (*i)->input_streams() != ChanCount::ZERO) { if (bufs.count() != (*i)->input_streams()) { - cerr << _name << " bufs = " << bufs.count() - << " input for " << (*i)->name() << " = " << (*i)->input_streams() - << endl; - abort (); + DEBUG_TRACE ( + DEBUG::Processors, string_compose ( + "%1 bufs = %2 input for %3 = %4\n", + _name, bufs.count(), (*i)->name(), (*i)->input_streams() + ) + ); + continue; } } #endif + /* should we NOT run plugins here if the route is inactive? do we catch route != active somewhere higher? */ @@ -515,52 +565,40 @@ Route::n_process_buffers () } void -Route::passthru (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick) +Route::monitor_run (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick) { - BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); + assert (is_monitor()); + BufferSet& bufs (_session.get_scratch_buffers (n_process_buffers())); + passthru (bufs, start_frame, end_frame, nframes, declick); +} +void +Route::passthru (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick) +{ _silent = false; - assert (bufs.available() >= input_streams()); - - if (_input->n_ports() == ChanCount::ZERO) { - silence_unlocked (nframes); - } - - bufs.set_count (input_streams()); - if (is_monitor() && _session.listening() && !_session.is_auditioning()) { /* control/monitor bus ignores input ports when something is feeding the listen "stream". data will "arrive" into the route from the intreturn processor element. */ - bufs.silence (nframes, 0); - - } else { - 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); - } - } + bufs.silence (nframes, 0); } write_out_of_band_data (bufs, start_frame, end_frame, nframes); - process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, true); + process_output_buffers (bufs, start_frame, end_frame, nframes, declick, true); } void Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int 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, false); + process_output_buffers (bufs, start_frame, end_frame, nframes, declick, false); } void @@ -619,6 +657,7 @@ void Route::set_solo (bool yn, void *src) { if (_solo_safe) { + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name())); return; } @@ -627,6 +666,9 @@ Route::set_solo (bool yn, void *src) return; } + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, src: %3 grp ? %4 currently self-soloed ? %5\n", + name(), yn, src, (src == _route_group), self_soloed())); + if (self_soloed() != yn) { set_self_solo (yn); set_mute_master_solo (); @@ -638,6 +680,7 @@ Route::set_solo (bool yn, void *src) void Route::set_self_solo (bool yn) { + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn)); _self_solo = yn; } @@ -645,9 +688,13 @@ void Route::mod_solo_by_others_upstream (int32_t delta) { if (_solo_safe) { + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-upstream due to solo-safe\n", name())); return; } + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n", + name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream)); + uint32_t old_sbu = _soloed_by_others_upstream; if (delta < 0) { @@ -701,9 +748,13 @@ void Route::mod_solo_by_others_downstream (int32_t delta) { if (_solo_safe) { + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-downstream due to solo safe\n", name())); return; } + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n", + name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream)); + if (delta < 0) { if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) { _soloed_by_others_downstream += delta; @@ -729,7 +780,7 @@ Route::set_mute_master_solo () void Route::set_solo_isolated (bool yn, void *src) { - if (is_master() || is_monitor() || is_hidden()) { + if (is_master() || is_monitor() || is_auditioner()) { return; } @@ -743,12 +794,12 @@ Route::set_solo_isolated (bool yn, void *src) boost::shared_ptr routes = _session.get_routes (); for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { - if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) { + if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { continue; } bool sends_only; - bool does_feed = direct_feeds (*i, &sends_only); // we will recurse anyway, so don't use ::feeds() + bool does_feed = direct_feeds_according_to_graph (*i, &sends_only); // we will recurse anyway, so don't use ::feeds() if (does_feed && !sends_only) { (*i)->set_solo_isolated (yn, (*i)->route_group()); @@ -808,6 +859,11 @@ Route::set_mute (bool yn, void *src) if (muted() != yn) { _mute_master->set_muted_by_self (yn); + /* allow any derived classes to respond to the mute change + before anybody else knows about it. + */ + act_on_mute (); + /* tell everyone else */ mute_changed (src); /* EMIT SIGNAL */ _mute_control->Changed (); /* EMIT SIGNAL */ } @@ -832,16 +888,17 @@ dump_processors(const string& name, const list >& p } #endif -int -Route::add_processor (boost::shared_ptr processor, Placement placement, ProcessorStreams* err, bool activation_allowed) +/** Supposing that we want to insert a Processor at a given Placement, return + * the processor to add the new one before (or 0 to add at the end). + */ +boost::shared_ptr +Route::before_processor_for_placement (Placement p) { - ProcessorList::iterator loc; - - /* XXX this is not thread safe - we don't hold the lock across determining the iter - to add before and actually doing the insertion. dammit. - */ + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); - if (placement == PreFader) { + ProcessorList::iterator loc; + + if (p == PreFader) { /* generic pre-fader: insert immediately before the amp */ loc = find (_processors.begin(), _processors.end(), _amp); } else { @@ -849,15 +906,60 @@ Route::add_processor (boost::shared_ptr processor, Placement placemen loc = find (_processors.begin(), _processors.end(), _main_outs); } - return add_processor (processor, loc, err, activation_allowed); + return loc != _processors.end() ? *loc : boost::shared_ptr (); +} + +/** Supposing that we want to insert a Processor at a given index, return + * the processor to add the new one before (or 0 to add at the end). + */ +boost::shared_ptr +Route::before_processor_for_index (int index) +{ + if (index == -1) { + return boost::shared_ptr (); + } + + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); + + ProcessorList::iterator i = _processors.begin (); + int j = 0; + while (i != _processors.end() && j < index) { + if ((*i)->display_to_user()) { + ++j; + } + + ++i; + } + + return (i != _processors.end() ? *i : boost::shared_ptr ()); +} + +/** Add a processor either pre- or post-fader + * @return 0 on success, non-0 on failure. + */ +int +Route::add_processor (boost::shared_ptr processor, Placement placement, ProcessorStreams* err, bool activation_allowed) +{ + return add_processor (processor, before_processor_for_placement (placement), err, activation_allowed); } +/** Add a processor to a route such that it ends up with a given index into the visible processors. + * @param index Index to add the processor at, or -1 to add at the end of the list. + * @return 0 on success, non-0 on failure. + */ +int +Route::add_processor_by_index (boost::shared_ptr processor, int index, ProcessorStreams* err, bool activation_allowed) +{ + return add_processor (processor, before_processor_for_index (index), err, activation_allowed); +} + /** Add a processor to the route. - * @param iter an iterator in _processors; the new processor will be inserted immediately before this location. + * @param before An existing processor in the list, or 0; the new processor will be inserted immediately before it (or at the end). + * @return 0 on success, non-0 on failure. */ int -Route::add_processor (boost::shared_ptr processor, ProcessorList::iterator iter, ProcessorStreams* err, bool activation_allowed) +Route::add_processor (boost::shared_ptr processor, boost::shared_ptr before, ProcessorStreams* err, bool activation_allowed) { assert (processor != _meter); assert (processor != _main_outs); @@ -870,31 +972,38 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite } { - Glib::RWLock::WriterLock lm (_processor_lock); + Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); boost::shared_ptr pi; boost::shared_ptr porti; - ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), processor); - if (processor == _amp) { - // Ensure only one amp is in the list at any time - if (loc != _processors.end()) { - if (iter == loc) { // Already in place, do nothing + /* Ensure that only one amp is in the list at any time */ + ProcessorList::iterator check = find (_processors.begin(), _processors.end(), processor); + if (check != _processors.end()) { + if (before == _amp) { + /* Already in position; all is well */ return 0; - } else { // New position given, relocate - _processors.erase (loc); + } else { + _processors.erase (check); } } + } - } else { - if (loc != _processors.end()) { - cerr << "ERROR: Processor added to route twice!" << endl; + assert (find (_processors.begin(), _processors.end(), processor) == _processors.end ()); + + ProcessorList::iterator loc; + if (before) { + /* inserting before a processor; find it */ + loc = find (_processors.begin(), _processors.end(), before); + if (loc == _processors.end ()) { + /* Not found */ return 1; } - - loc = iter; + } else { + /* inserting at end */ + loc = _processors.end (); } _processors.insert (loc, processor); @@ -903,7 +1012,7 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite // configure redirect ports properly, etc. { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); if (configure_processors_unlocked (err)) { pstate.restore (); @@ -921,7 +1030,7 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite } - if (activation_allowed) { + if (activation_allowed && !_session.get_disable_all_loaded_plugins()) { processor->activate (); } @@ -930,6 +1039,7 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite _output->set_user_latency (0); } + reset_instrument_info (); processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ set_processor_positions (); @@ -969,7 +1079,8 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || - prop->value() == "vst" || + prop->value() == "windows-vst" || + prop->value() == "lxvst" || prop->value() == "audiounit") { processor.reset (new PluginInsert (_session)); @@ -1030,7 +1141,7 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr } { - Glib::RWLock::WriterLock lm (_processor_lock); + Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) { @@ -1052,7 +1163,7 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr } { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); if (configure_processors_unlocked (err)) { pstate.restore (); configure_processors_unlocked (0); // it worked before we tried to add it ... @@ -1077,6 +1188,7 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr _output->set_user_latency (0); } + reset_instrument_info (); processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ set_processor_positions (); @@ -1102,7 +1214,7 @@ Route::placement_range(Placement p, ProcessorList::iterator& start, ProcessorLis void Route::disable_processors (Placement p) { - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); ProcessorList::iterator start, end; placement_range(p, start, end); @@ -1119,7 +1231,7 @@ Route::disable_processors (Placement p) void Route::disable_processors () { - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->deactivate (); @@ -1134,7 +1246,7 @@ Route::disable_processors () void Route::disable_plugins (Placement p) { - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); ProcessorList::iterator start, end; placement_range(p, start, end); @@ -1153,7 +1265,7 @@ Route::disable_plugins (Placement p) void Route::disable_plugins () { - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (boost::dynamic_pointer_cast (*i)) { @@ -1168,7 +1280,7 @@ Route::disable_plugins () void Route::ab_plugins (bool forward) { - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); if (forward) { @@ -1227,7 +1339,7 @@ Route::clear_processors (Placement p) } { - Glib::RWLock::WriterLock lm (_processor_lock); + Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorList new_list; ProcessorStreams err; bool seen_amp = false; @@ -1273,7 +1385,7 @@ Route::clear_processors (Placement p) _processors = new_list; { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); configure_processors_unlocked (&err); // this can't fail } } @@ -1283,14 +1395,21 @@ Route::clear_processors (Placement p) processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ set_processor_positions (); + reset_instrument_info (); + if (!already_deleting) { _session.clear_deletion_in_progress(); } } int -Route::remove_processor (boost::shared_ptr processor, ProcessorStreams* err) +Route::remove_processor (boost::shared_ptr processor, ProcessorStreams* err, bool need_process_lock) { + // TODO once the export point can be configured properly, do something smarter here + if (processor == _capturing_processor) { + _capturing_processor.reset(); + } + /* these can never be removed */ if (processor == _amp || processor == _meter || processor == _main_outs) { @@ -1304,7 +1423,7 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream processor_max_streams.reset(); { - Glib::RWLock::WriterLock lm (_processor_lock); + Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); ProcessorList::iterator i; @@ -1325,12 +1444,7 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream 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); - } + iop->disconnect (); } i = _processors.erase (i); @@ -1347,11 +1461,18 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream if (!removed) { /* what? */ return 1; - } + } - { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + if (need_process_lock) { + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + if (configure_processors_unlocked (err)) { + pstate.restore (); + /* we know this will work, because it worked before :) */ + configure_processors_unlocked (0); + return -1; + } + } else { if (configure_processors_unlocked (err)) { pstate.restore (); /* we know this will work, because it worked before :) */ @@ -1374,6 +1495,7 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream } } + reset_instrument_info (); processor->drop_references (); processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ set_processor_positions (); @@ -1393,7 +1515,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* processor_max_streams.reset(); { - Glib::RWLock::WriterLock lm (_processor_lock); + Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); ProcessorList::iterator i; @@ -1440,7 +1562,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* _output->set_user_latency (0); { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); if (configure_processors_unlocked (err)) { pstate.restore (); @@ -1470,20 +1592,32 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* (*i)->drop_references (); } + reset_instrument_info (); processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ set_processor_positions (); return 0; } +void +Route::reset_instrument_info () +{ + boost::shared_ptr instr = the_instrument(); + if (instr) { + _instrument_info.set_internal_instrument (instr); + } +} + /** Caller must hold process lock */ int Route::configure_processors (ProcessorStreams* err) { +#ifndef WIN32 assert (!AudioEngine::instance()->process_lock().trylock()); +#endif if (!_in_configure_processors) { - Glib::RWLock::WriterLock lm (_processor_lock); + Glib::Threads::RWLock::WriterLock lm (_processor_lock); return configure_processors_unlocked (err); } @@ -1499,7 +1633,7 @@ Route::input_streams () const list > Route::try_configure_processors (ChanCount in, ProcessorStreams* err) { - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); return try_configure_processors_unlocked (in, err); } @@ -1550,7 +1684,9 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err) int Route::configure_processors_unlocked (ProcessorStreams* err) { +#ifndef WIN32 assert (!AudioEngine::instance()->process_lock().trylock()); +#endif if (_in_configure_processors) { return 0; @@ -1598,86 +1734,33 @@ Route::configure_processors_unlocked (ProcessorStreams* err) return 0; } -void -Route::all_processors_flip () -{ - Glib::RWLock::ReaderLock lm (_processor_lock); - - if (_processors.empty()) { - return; - } - - bool first_is_on = _processors.front()->active(); - - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if (first_is_on) { - (*i)->deactivate (); - } else { - (*i)->activate (); - } - } - - _session.set_dirty (); -} - -/** Set all processors with a given placement to a given active state. - * @param p Placement of processors to change. - * @param state New active state for those processors. +/** Set all visible processors to a given active state (except Fader, whose state is not changed) + * @param state New active state for those processors. */ void -Route::all_processors_active (Placement p, bool state) +Route::all_visible_processors_active (bool state) { - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); if (_processors.empty()) { return; } - ProcessorList::iterator start, end; - placement_range(p, start, end); - - bool before_amp = true; + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if ((*i) == _amp) { - before_amp = false; + if (!(*i)->display_to_user() || boost::dynamic_pointer_cast (*i)) { continue; } - if (p == PreFader && before_amp) { - if (state) { - (*i)->activate (); - } else { - (*i)->deactivate (); - } + + if (state) { + (*i)->activate (); + } else { + (*i)->deactivate (); } } _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, ProcessorStreams* err) { @@ -1688,7 +1771,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err */ { - Glib::RWLock::WriterLock lm (_processor_lock); + Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); ProcessorList::iterator oiter; @@ -1746,8 +1829,11 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err _processors.insert (oiter, as_it_will_be.begin(), as_it_will_be.end()); + /* If the meter is in a custom position, find it and make a rough note of its position */ + maybe_note_meter_position (); + { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); if (configure_processors_unlocked (err)) { pstate.restore (); @@ -1797,6 +1883,8 @@ Route::state(bool full_state) node->add_property("denormal-protection", _denormal_protection?"yes":"no"); node->add_property("meter-point", enum_2_string (_meter_point)); + node->add_property("meter-type", enum_2_string (_meter_type)); + if (_route_group) { node->add_property("route-group", _route_group->name()); } @@ -1805,9 +1893,9 @@ Route::state(bool full_state) OrderKeys::iterator x = order_keys.begin(); while (x != order_keys.end()) { - order_string += string ((*x).first); + order_string += enum_2_string ((*x).first); order_string += '='; - snprintf (buf, sizeof(buf), "%ld", (*x).second); + snprintf (buf, sizeof(buf), "%" PRId32, (*x).second); order_string += buf; ++x; @@ -1848,27 +1936,49 @@ Route::state(bool full_state) } for (i = _processors.begin(); i != _processors.end(); ++i) { + if (!full_state) { + /* template save: do not include internal sends functioning as + aux sends because the chance of the target ID + in the session where this template is used + is not very likely. + + similarly, do not save listen sends which connect to + the monitor section, because these will always be + added if necessary. + */ + boost::shared_ptr is; + + if ((is = boost::dynamic_pointer_cast (*i)) != 0) { + if (is->role() == Delivery::Listen) { + continue; + } + } + } node->add_child_nocopy((*i)->state (full_state)); } - if (_extra_xml){ + if (_extra_xml) { node->add_child_copy (*_extra_xml); } + if (_custom_meter_position_noted) { + boost::shared_ptr after = _processor_after_last_custom_meter.lock (); + if (after) { + after->id().print (buf, sizeof (buf)); + node->add_property (X_("processor-after-last-custom-meter"), buf); + } + + node->add_property (X_("last-custom-meter-was-at-end"), _last_custom_meter_was_at_end ? "yes" : "no"); + } + return *node; } int Route::set_state (const XMLNode& node, int version) -{ - return _set_state (node, version, true); -} - -int -Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) { if (version < 3000) { - return _set_state_2X (node, version); + return set_state_2X (node, version); } XMLNodeList nlist; @@ -1885,9 +1995,7 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) Route::set_name (prop->value()); } - if ((prop = node.property ("id")) != 0) { - _id = prop->value (); - } + set_id (node); if ((prop = node.property (X_("flags"))) != 0) { _flags = Flag (string_2_enum (prop->value(), _flags)); @@ -1895,7 +2003,7 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) _flags = Flag (0); } - if (is_master() || is_monitor() || is_hidden()) { + if (is_master() || is_monitor() || is_auditioner()) { _mute_master->set_solo_ignore (true); } @@ -1934,7 +2042,6 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) processor_state.add_child_copy (*child); } - if (child->name() == X_("Pannable")) { if (_pannable) { _pannable->set_state (*child, version); @@ -1944,8 +2051,23 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) } } + if ((prop = node.property (X_("meter-point"))) != 0) { + MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point)); + set_meter_point (mp, true); + if (_meter) { + _meter->set_display_to_user (_meter_point == MeterCustom); + } + } + + if ((prop = node.property (X_("meter-type"))) != 0) { + _meter_type = MeterType (string_2_enum (prop->value (), _meter_type)); + } + set_processor_state (processor_state); + // this looks up the internal instrument in processors + reset_instrument_info(); + if ((prop = node.property ("self-solo")) != 0) { set_self_solo (string_is_affirmative (prop->value())); } @@ -1982,14 +2104,6 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) set_active (yn, this); } - if ((prop = node.property (X_("meter-point"))) != 0) { - MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point)); - set_meter_point (mp, true); - if (_meter) { - _meter->set_display_to_user (_meter_point == MeterCustom); - } - } - if ((prop = node.property (X_("order-keys"))) != 0) { int32_t n; @@ -2007,7 +2121,18 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining) << endmsg; } else { - set_order_key (remaining.substr (0, equal), n); + string keyname = remaining.substr (0, equal); + RouteSortOrderKey sk; + + if (keyname == "signal") { + sk = MixerSort; + } else if (keyname == "editor") { + sk = EditorSort; + } else { + sk = (RouteSortOrderKey) string_2_enum (remaining.substr (0, equal), sk); + } + + set_order_key (sk, n); } } @@ -2021,6 +2146,24 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) } } + if ((prop = node.property (X_("processor-after-last-custom-meter"))) != 0) { + PBD::ID id (prop->value ()); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); + ProcessorList::const_iterator i = _processors.begin (); + while (i != _processors.end() && (*i)->id() != id) { + ++i; + } + + if (i != _processors.end ()) { + _processor_after_last_custom_meter = *i; + _custom_meter_position_noted = true; + } + } + + if ((prop = node.property (X_("last-custom-meter-was-at-end"))) != 0) { + _last_custom_meter_was_at_end = string_is_affirmative (prop->value ()); + } + for (niter = nlist.begin(); niter != nlist.end(); ++niter){ child = *niter; @@ -2034,13 +2177,15 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) { if (prop->value() == "solo") { _solo_control->set_state (*child, version); + } else if (prop->value() == "mute") { + _mute_control->set_state (*child, version); } } 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); + set_remote_control_id_internal (x); } } else if (child->name() == X_("MuteMaster")) { @@ -2052,7 +2197,7 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) } int -Route::_set_state_2X (const XMLNode& node, int version) +Route::set_state_2X (const XMLNode& node, int version) { XMLNodeList nlist; XMLNodeConstIterator niter; @@ -2071,11 +2216,17 @@ Route::_set_state_2X (const XMLNode& node, int version) } if ((prop = node.property (X_("flags"))) != 0) { - _flags = Flag (string_2_enum (prop->value(), _flags)); + string f = prop->value (); + boost::replace_all (f, "ControlOut", "MonitorOut"); + _flags = Flag (string_2_enum (f, _flags)); } else { _flags = Flag (0); } + if (is_master() || is_monitor() || is_auditioner()) { + _mute_master->set_solo_ignore (true); + } + if ((prop = node.property (X_("phase-invert"))) != 0) { boost::dynamic_bitset<> p (_input->n_ports().n_audio ()); if (string_is_affirmative (prop->value ())) { @@ -2181,7 +2332,18 @@ Route::_set_state_2X (const XMLNode& node, int version) error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining) << endmsg; } else { - set_order_key (remaining.substr (0, equal), n); + string keyname = remaining.substr (0, equal); + RouteSortOrderKey sk; + + if (keyname == "signal") { + sk = MixerSort; + } else if (keyname == "editor") { + sk = EditorSort; + } else { + sk = (RouteSortOrderKey) string_2_enum (remaining.substr (0, equal), sk); + } + + set_order_key (sk, n); } } @@ -2215,9 +2377,7 @@ Route::_set_state_2X (const XMLNode& node, int version) Route::set_name (prop->value ()); } - if ((prop = child->property (X_("id"))) != 0) { - _id = prop->value (); - } + set_id (*child); if ((prop = child->property (X_("active"))) != 0) { bool yn = string_is_affirmative (prop->value()); @@ -2290,7 +2450,7 @@ Route::_set_state_2X (const XMLNode& node, int version) if ((prop = child->property (X_("id"))) != 0) { int32_t x; sscanf (prop->value().c_str(), "%d", &x); - set_remote_control_id (x); + set_remote_control_id_internal (x); } } @@ -2341,6 +2501,7 @@ Route::set_processor_state (const XMLNode& node) new_order.push_back (_amp); } else if (prop->value() == "meter") { _meter->set_state (**niter, Stateful::current_state_version); + new_order.push_back (_meter); } else if (prop->value() == "main-outs") { _main_outs->set_state (**niter, Stateful::current_state_version); } else if (prop->value() == "intreturn") { @@ -2356,7 +2517,8 @@ Route::set_processor_state (const XMLNode& node) } _monitor_control->set_state (**niter, Stateful::current_state_version); } else if (prop->value() == "capture") { - _capturing_processor.reset (new CapturingProcessor (_session)); + /* CapturingProcessor should never be restored, it's always + added explicitly when needed */ } else { ProcessorList::iterator o; @@ -2381,7 +2543,8 @@ Route::set_processor_state (const XMLNode& node) } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || - prop->value() == "vst" || + prop->value() == "windows-vst" || + prop->value() == "lxvst" || prop->value() == "audiounit") { processor.reset (new PluginInsert(_session)); @@ -2399,10 +2562,21 @@ Route::set_processor_state (const XMLNode& node) continue; } +#ifndef NO_PLUGIN_STATE if (processor->set_state (**niter, Stateful::current_state_version) != 0) { /* This processor could not be configured. Turn it into a UnknownProcessor */ processor.reset (new UnknownProcessor (_session, **niter)); } +#else + if (boost::dynamic_pointer_cast(processor)) { + if (processor->set_state (**niter, Stateful::current_state_version) != 0) { + /* This processor could not be configured. Turn it into a UnknownProcessor */ + processor.reset (new UnknownProcessor (_session, **niter)); + } + } else { + /* plugin, but ::set_state() not * allowed no message here - things will get too verbose */ + } +#endif /* we have to note the monitor send here, otherwise a new one will be created and the state of this one will be lost. @@ -2423,11 +2597,11 @@ Route::set_processor_state (const XMLNode& node) } { - Glib::RWLock::WriterLock lm (_processor_lock); + Glib::Threads::RWLock::WriterLock lm (_processor_lock); _processors = new_order; if (must_configure) { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); configure_processors_unlocked (0); } @@ -2446,6 +2620,7 @@ Route::set_processor_state (const XMLNode& node) } } + reset_instrument_info (); processors_changed (RouteProcessorChange ()); set_processor_positions (); } @@ -2460,7 +2635,7 @@ Route::curve_reallocate () void Route::silence (framecnt_t nframes) { - Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); if (!lm.locked()) { return; } @@ -2506,7 +2681,7 @@ Route::add_internal_return () void Route::add_send_to_internal_return (InternalSend* send) { - Glib::RWLock::ReaderLock rm (_processor_lock); + Glib::Threads::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); @@ -2520,7 +2695,7 @@ Route::add_send_to_internal_return (InternalSend* send) void Route::remove_send_from_internal_return (InternalSend* send) { - Glib::RWLock::ReaderLock rm (_processor_lock); + Glib::Threads::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); @@ -2531,11 +2706,13 @@ Route::remove_send_from_internal_return (InternalSend* send) } } -/** Add a monitor send (if we don't already have one) but don't activate it */ -int -Route::listen_via_monitor () +void +Route::enable_monitor_send () { - /* master never sends to control outs */ + /* Caller must hold process lock */ + assert (!AudioEngine::instance()->process_lock().trylock()); + + /* master never sends to monitor section via the normal mechanism */ assert (!is_master ()); /* make sure we have one */ @@ -2545,23 +2722,20 @@ Route::listen_via_monitor () } /* set it up */ - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); configure_processors (0); - - return 0; } -/** Add an internal send to a route. +/** Add an aux send to a route. * @param route route to send to. - * @param placement placement for the send. + * @param before Processor to insert before, or 0 to insert at the end. */ int -Route::listen_via (boost::shared_ptr route, Placement placement) +Route::add_aux_send (boost::shared_ptr route, boost::shared_ptr before) { assert (route != _session.monitor_out ()); { - Glib::RWLock::ReaderLock rm (_processor_lock); + Glib::Threads::RWLock::ReaderLock rm (_processor_lock); for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) { @@ -2575,8 +2749,15 @@ Route::listen_via (boost::shared_ptr route, Placement placement) } try { - boost::shared_ptr listener (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux)); - add_processor (listener, placement); + + boost::shared_ptr listener; + + { + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + listener.reset (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux)); + } + + add_processor (listener, before); } catch (failed_constructor& err) { return -1; @@ -2586,36 +2767,39 @@ Route::listen_via (boost::shared_ptr route, Placement placement) } void -Route::drop_listen (boost::shared_ptr route) +Route::remove_aux_or_listen (boost::shared_ptr route) { ProcessorStreams err; ProcessorList::iterator tmp; - Glib::RWLock::ReaderLock rl(_processor_lock); - rl.acquire (); - - again: - for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ) { - - boost::shared_ptr d = boost::dynamic_pointer_cast(*x); - - if (d && d->target_route() == route) { - rl.release (); - remove_processor (*x, &err); - rl.acquire (); + { + Glib::Threads::RWLock::ReaderLock rl(_processor_lock); - /* list could have been demolished while we dropped the lock - so start over. - */ + /* have to do this early because otherwise processor reconfig + * will put _monitor_send back in the list + */ - goto again; + if (route == _session.monitor_out()) { + _monitor_send.reset (); } - } - rl.release (); + again: + for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) { + + boost::shared_ptr d = boost::dynamic_pointer_cast(*x); + + if (d && d->target_route() == route) { + rl.release (); + remove_processor (*x, &err, false); + rl.acquire (); - if (route == _session.monitor_out()) { - _monitor_send.reset (); + /* list could have been demolished while we dropped the lock + so start over. + */ + + goto again; + } + } } } @@ -2674,14 +2858,14 @@ Route::feeds (boost::shared_ptr other, bool* via_sends_only) } bool -Route::direct_feeds (boost::shared_ptr other, bool* only_send) +Route::direct_feeds_according_to_reality (boost::shared_ptr other, bool* via_send_only) { DEBUG_TRACE (DEBUG::Graph, string_compose ("Feeds? %1\n", _name)); if (_output->connected_to (other->input())) { DEBUG_TRACE (DEBUG::Graph, string_compose ("\tdirect FEEDS %2\n", other->name())); - if (only_send) { - *only_send = false; + if (via_send_only) { + *via_send_only = false; } return true; @@ -2695,8 +2879,8 @@ Route::direct_feeds (boost::shared_ptr other, bool* only_send) 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; + if (via_send_only) { + *via_send_only = true; } return true; } else { @@ -2712,18 +2896,20 @@ Route::direct_feeds (boost::shared_ptr other, bool* only_send) return false; } +bool +Route::direct_feeds_according_to_graph (boost::shared_ptr other, bool* via_send_only) +{ + return _session._current_route_graph.has (shared_from_this (), other, via_send_only); +} + /** Called from the (non-realtime) butler thread when the transport is stopped */ void -Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors) +Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool /*did_locate*/, bool can_flush_processors) { framepos_t now = _session.transport_frame(); { - Glib::RWLock::ReaderLock lm (_processor_lock); - - if (!did_locate) { - automation_snapshot (now, true); - } + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); Automatable::transport_stopped (now); @@ -2740,15 +2926,63 @@ Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool did_lo _roll_delay = _initial_delay; } -/** Called with the process lock held if change contains ConfigurationChanged */ void Route::input_change_handler (IOChange change, void * /*src*/) { + bool need_to_queue_solo_change = true; + if ((change.type & IOChange::ConfigurationChanged)) { + /* This is called with the process lock held if change + contains ConfigurationChanged + */ + need_to_queue_solo_change = false; configure_processors (0); _phase_invert.resize (_input->n_ports().n_audio ()); io_changed (); /* EMIT SIGNAL */ } + + if (!_input->connected() && _soloed_by_others_upstream) { + if (need_to_queue_solo_change) { + _session.cancel_solo_after_disconnect (shared_from_this(), true); + } else { + cancel_solo_after_disconnect (true); + } + } +} + +void +Route::output_change_handler (IOChange change, void * /*src*/) +{ + bool need_to_queue_solo_change = true; + + if ((change.type & IOChange::ConfigurationChanged)) { + /* This is called with the process lock held if change + contains ConfigurationChanged + */ + need_to_queue_solo_change = false; + configure_processors (0); + io_changed (); /* EMIT SIGNAL */ + } + + if (!_output->connected() && _soloed_by_others_downstream) { + if (need_to_queue_solo_change) { + _session.cancel_solo_after_disconnect (shared_from_this(), false); + } else { + cancel_solo_after_disconnect (false); + } + } +} + +void +Route::cancel_solo_after_disconnect (bool upstream) +{ + if (upstream) { + _soloed_by_others_upstream = 0; + } else { + _soloed_by_others_downstream = 0; + } + set_mute_master_solo (); + solo_changed (false, this); } uint32_t @@ -2762,10 +2996,10 @@ Route::pans_required () const } int -Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, - bool session_state_changing, bool /*can_record*/) +Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool session_state_changing) { - Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); + if (!lm.locked()) { return 0; } @@ -2778,6 +3012,7 @@ Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, silence_unlocked (nframes); return 0; } + if (session_state_changing) { if (_session.transport_speed() != 0.0f) { /* we're rolling but some state is changing (e.g. our diskstream contents) @@ -2793,59 +3028,28 @@ Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, */ } - _amp->apply_gain_automation (false); - passthru (start_frame, end_frame, nframes, 0); - - return 0; -} - -framecnt_t -Route::check_initial_delay (framecnt_t nframes, framecnt_t& transport_frame) -{ - if (_roll_delay > nframes) { - - _roll_delay -= nframes; - silence_unlocked (nframes); - /* transport frame is not legal for caller to use */ - return 0; - - } else if (_roll_delay > 0) { - - nframes -= _roll_delay; - silence_unlocked (_roll_delay); - transport_frame += _roll_delay; - - /* shuffle all the port buffers for things that lead "out" of this Route - to reflect that we just wrote _roll_delay frames of silence. - */ - - Glib::RWLock::ReaderLock lm (_processor_lock); - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - boost::shared_ptr iop = boost::dynamic_pointer_cast (*i); - if (iop) { - iop->increment_port_buffer_offset (_roll_delay); - } - } - _output->increment_port_buffer_offset (_roll_delay); + BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); - _roll_delay = 0; + fill_buffers_with_input (bufs, _input, nframes); + if (_meter_point == MeterInput) { + _meter->run (bufs, start_frame, end_frame, nframes, true); } - return nframes; + _amp->apply_gain_automation (false); + passthru (bufs, start_frame, end_frame, nframes, 0); + + return 0; } int -Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, - bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */) +Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, bool& /* need_butler */) { - Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); if (!lm.locked()) { return 0; } - automation_snapshot (_session.transport_frame(), false); - if (n_outputs().n_total() == 0) { return 0; } @@ -2855,7 +3059,7 @@ Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, in return 0; } - framecnt_t unused = 0; + framepos_t unused = 0; if ((nframes = check_initial_delay (nframes, unused)) == 0) { return 0; @@ -2863,55 +3067,26 @@ Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, in _silent = false; - passthru (start_frame, end_frame, nframes, declick); + BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); + + fill_buffers_with_input (bufs, _input, nframes); + + if (_meter_point == MeterInput) { + _meter->run (bufs, start_frame, end_frame, nframes, true); + } + + passthru (bufs, start_frame, end_frame, nframes, declick); return 0; } int -Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/, - bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */) +Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/, bool& /* need_butler */) { silence (nframes); return 0; } -void -Route::toggle_monitor_input () -{ - for (PortSet::iterator i = _input->ports().begin(); i != _input->ports().end(); ++i) { - i->ensure_monitor_input( ! i->monitoring_input()); - } -} - -bool -Route::has_external_redirects () const -{ - // 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->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; - } - } - } - } - - return false; -} - void Route::flush_processors () { @@ -2919,7 +3094,7 @@ Route::flush_processors () this is called from the RT audio thread. */ - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->flush (); @@ -2929,18 +3104,18 @@ Route::flush_processors () void Route::set_meter_point (MeterPoint p, bool force) { - /* CAN BE CALLED FROM PROCESS CONTEXT */ - if (_meter_point == p && !force) { return; } - _meter_point = p; - bool meter_was_visible_to_user = _meter->display_to_user (); { - Glib::RWLock::WriterLock lm (_processor_lock); + Glib::Threads::RWLock::WriterLock lm (_processor_lock); + + maybe_note_meter_position (); + + _meter_point = p; if (_meter_point != MeterCustom) { @@ -2948,32 +3123,48 @@ Route::set_meter_point (MeterPoint p, bool force) setup_invisible_processors (); - ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter); + } else { - ChanCount m_in; + _meter->set_display_to_user (true); - if (loc == _processors.begin()) { - m_in = _input->n_ports(); - } else { - ProcessorList::iterator before = loc; - --before; - m_in = (*before)->output_streams (); + /* If we have a previous position for the custom meter, try to put it there */ + if (_custom_meter_position_noted) { + boost::shared_ptr after = _processor_after_last_custom_meter.lock (); + + if (after) { + ProcessorList::iterator i = find (_processors.begin(), _processors.end(), after); + if (i != _processors.end ()) { + _processors.remove (_meter); + _processors.insert (i, _meter); + } + } else if (_last_custom_meter_was_at_end) { + _processors.remove (_meter); + _processors.push_back (_meter); + } } + } - _meter->reflect_inputs (m_in); - - /* 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. - */ + /* Set up the meter for its new position */ + ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter); + + ChanCount m_in; + + if (loc == _processors.begin()) { + m_in = _input->n_ports(); } else { - - // just make it visible and let the user move it - - _meter->set_display_to_user (true); + ProcessorList::iterator before = loc; + --before; + m_in = (*before)->output_streams (); } + + _meter->reflect_inputs (m_in); + + /* 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_change (); /* EMIT SIGNAL */ @@ -2987,11 +3178,11 @@ void Route::listen_position_changed () { { - Glib::RWLock::WriterLock lm (_processor_lock); + Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); if (configure_processors_unlocked (0)) { pstate.restore (); @@ -3014,7 +3205,7 @@ Route::add_export_point() _capturing_processor->activate (); { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); configure_processors (0); } @@ -3076,18 +3267,6 @@ Route::set_latency_compensation (framecnt_t longest_session_latency) } } -void -Route::automation_snapshot (framepos_t now, bool force) -{ - if (_pannable) { - _pannable->automation_snapshot (now, force); - } - - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - (*i)->automation_snapshot (now, force); - } -} - Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr r) : AutomationControl (r->session(), Evoral::Parameter (SoloAutomation), boost::shared_ptr(), name) @@ -3186,19 +3365,20 @@ Route::protect_automation () (*i)->protect_automation(); } +/** @param declick 1 to set a pending declick fade-in, + * -1 to set a pending declick fade-out + */ void Route::set_pending_declick (int declick) { if (_declickable) { - /* this call is not allowed to turn off a pending declick unless "force" is true */ + /* this call is not allowed to turn off a pending declick */ if (declick) { _pending_declick = declick; } - // cerr << _name << ": after setting to " << declick << " pending declick = " << _pending_declick << endl; } else { _pending_declick = 0; } - } /** Shift automation forwards from a particular place, thereby inserting time. @@ -3239,7 +3419,7 @@ Route::shift (framepos_t pos, framecnt_t frames) /* redirect automation */ { - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin (); i != _processors.end (); ++i) { set parameters = (*i)->what_can_be_automated(); @@ -3304,10 +3484,42 @@ Route::set_name (const string& str) return ret; } +/** Set the name of a route in an XML description. + * @param node XML node to set the name in. + * @param name New name. + */ +void +Route::set_name_in_state (XMLNode& node, string const & name) +{ + node.add_property (X_("name"), name); + + XMLNodeList children = node.children(); + for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) { + + if ((*i)->name() == X_("IO")) { + + IO::set_name_in_state (**i, name); + + } else if ((*i)->name() == X_("Processor")) { + + XMLProperty* role = (*i)->property (X_("role")); + if (role && role->value() == X_("Main")) { + (*i)->add_property (X_("name"), name); + } + + } else if ((*i)->name() == X_("Diskstream")) { + + (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str()); + (*i)->add_property (X_("name"), name); + + } + } +} + boost::shared_ptr Route::internal_send_for (boost::shared_ptr target) const { - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr send; @@ -3385,13 +3597,14 @@ Route::set_active (bool yn, void* src) _input->set_active (yn); _output->set_active (yn); active_changed (); // EMIT SIGNAL + _session.set_dirty (); } } void Route::meter () { - Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK); + Glib::Threads::RWLock::ReaderLock rm (_processor_lock, Glib::Threads::TRY_LOCK); assert (_meter); @@ -3446,7 +3659,7 @@ Route::get_control (const Evoral::Parameter& param) /* maybe one of our processors does or ... */ - Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK); + Glib::Threads::RWLock::ReaderLock rm (_processor_lock, Glib::Threads::TRY_LOCK); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if ((c = boost::dynamic_pointer_cast((*i)->control (param))) != 0) { break; @@ -3468,7 +3681,7 @@ Route::get_control (const Evoral::Parameter& param) boost::shared_ptr Route::nth_plugin (uint32_t n) { - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); ProcessorList::iterator i; for (i = _processors.begin(); i != _processors.end(); ++i) { @@ -3485,7 +3698,7 @@ Route::nth_plugin (uint32_t n) boost::shared_ptr Route::nth_send (uint32_t n) { - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); ProcessorList::iterator i; for (i = _processors.begin(); i != _processors.end(); ++i) { @@ -3502,7 +3715,7 @@ Route::nth_send (uint32_t n) bool Route::has_io_processor_named (const string& name) { - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); ProcessorList::iterator i; for (i = _processors.begin(); i != _processors.end(); ++i) { @@ -3526,7 +3739,7 @@ Route::mute_points () const void Route::set_processor_positions () { - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); bool had_amp = false; for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { @@ -3558,7 +3771,7 @@ Route::unknown_processors () const { list p; - Glib::RWLock::ReaderLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { if (boost::dynamic_pointer_cast (*i)) { p.push_back ((*i)->name ()); @@ -3578,21 +3791,26 @@ Route::update_port_latencies (PortSet& from, PortSet& to, bool playback, framecn jack_latency_range_t all_connections; - all_connections.min = ~((jack_nframes_t) 0); - all_connections.max = 0; - - /* iterate over all "from" ports and determine the latency range for all of their - connections to the "outside" (outside of this Route). - */ - - for (PortSet::iterator p = from.begin(); p != from.end(); ++p) { - - jack_latency_range_t range; - - p->get_connected_latency_range (range, playback); - - all_connections.min = min (all_connections.min, range.min); - all_connections.max = max (all_connections.max, range.max); + if (from.empty()) { + all_connections.min = 0; + all_connections.max = 0; + } else { + all_connections.min = ~((jack_nframes_t) 0); + all_connections.max = 0; + + /* iterate over all "from" ports and determine the latency range for all of their + connections to the "outside" (outside of this Route). + */ + + for (PortSet::iterator p = from.begin(); p != from.end(); ++p) { + + jack_latency_range_t range; + + p->get_connected_latency_range (range, playback); + + all_connections.min = min (all_connections.min, range.min); + all_connections.max = max (all_connections.max, range.max); + } } /* set the "from" port latencies to the max/min range of all their connections */ @@ -3678,7 +3896,7 @@ void Route::setup_invisible_processors () { #ifndef NDEBUG - Glib::RWLock::WriterLock lm (_processor_lock, Glib::TRY_LOCK); + Glib::Threads::RWLock::WriterLock lm (_processor_lock, Glib::Threads::TRY_LOCK); assert (!lm.locked ()); #endif @@ -3831,26 +4049,11 @@ Route::setup_invisible_processors () } } -bool -Route::should_monitor () const -{ - switch (Config->get_monitoring_model()) { - case HardwareMonitoring: - case ExternalMonitoring: - return !record_enabled() || (_session.config.get_auto_input() && !_session.actively_recording()); - break; - default: - break; - } - - return true; -} - void Route::unpan () { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - Glib::RWLock::ReaderLock lp (_processor_lock); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::RWLock::ReaderLock lp (_processor_lock); _pannable.reset (); @@ -3861,3 +4064,209 @@ Route::unpan () } } } + +/** If the meter point is `Custom', make a note of where the meter is. + * This is so that if the meter point is subsequently set to something else, + * and then back to custom, we can put the meter back where it was last time + * custom was enabled. + * + * Must be called with the _processor_lock held. + */ +void +Route::maybe_note_meter_position () +{ + if (_meter_point != MeterCustom) { + return; + } + + _custom_meter_position_noted = true; + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + if (boost::dynamic_pointer_cast (*i)) { + ProcessorList::iterator j = i; + ++j; + if (j != _processors.end ()) { + _processor_after_last_custom_meter = *j; + _last_custom_meter_was_at_end = false; + } else { + _last_custom_meter_was_at_end = true; + } + } + } +} + +boost::shared_ptr +Route::processor_by_id (PBD::ID id) const +{ + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + if ((*i)->id() == id) { + return *i; + } + } + + return boost::shared_ptr (); +} + +/** @return the monitoring state, or in other words what data we are pushing + * into the route (data from the inputs, data from disk or silence) + */ +MonitorState +Route::monitoring_state () const +{ + return MonitoringInput; +} + +/** @return what we should be metering; either the data coming from the input + * IO or the data that is flowing through the route. + */ +MeterState +Route::metering_state () const +{ + return MeteringRoute; +} + +bool +Route::has_external_redirects () const +{ + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + + /* ignore inactive processors and obviously ignore the main + * outs since everything has them and we don't care. + */ + + if ((*i)->active() && (*i) != _main_outs && (*i)->does_routing()) { + return true;; + } + } + + return false; +} + +boost::shared_ptr +Route::the_instrument () const +{ + Glib::Threads::RWLock::WriterLock lm (_processor_lock); + return the_instrument_unlocked (); +} + +boost::shared_ptr +Route::the_instrument_unlocked () const +{ + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + if (boost::dynamic_pointer_cast(*i)) { + if ((*i)->input_streams().n_midi() > 0 && + (*i)->output_streams().n_audio() > 0) { + return (*i); + } + } + } + return boost::shared_ptr(); +} + + + +void +Route::non_realtime_locate (framepos_t pos) +{ + if (_pannable) { + _pannable->transport_located (pos); + } + + { + Glib::Threads::RWLock::WriterLock lm (_processor_lock); + + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + (*i)->transport_located (pos); + } + } +} + +void +Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr io, pframes_t nframes) +{ + size_t n_buffers; + size_t i; + + /* MIDI + * + * We don't currently mix MIDI input together, so we don't need the + * complex logic of the audio case. + */ + + n_buffers = bufs.count().n_midi (); + + for (i = 0; i < n_buffers; ++i) { + + boost::shared_ptr source_port = io->midi (i); + MidiBuffer& buf (bufs.get_midi (i)); + + if (source_port) { + buf.copy (source_port->get_midi_buffer(nframes)); + } else { + buf.silence (nframes); + } + } + + /* AUDIO */ + + n_buffers = bufs.count().n_audio(); + + size_t n_ports = io->n_ports().n_audio(); + float scaling = 1.0f; + + if (n_ports > n_buffers) { + scaling = ((float) n_buffers) / n_ports; + } + + for (i = 0; i < n_ports; ++i) { + + /* if there are more ports than buffers, map them onto buffers + * in a round-robin fashion + */ + + boost::shared_ptr source_port = io->audio (i); + AudioBuffer& buf (bufs.get_audio (i%n_buffers)); + + + if (i < n_buffers) { + + /* first time through just copy a channel into + the output buffer. + */ + + buf.read_from (source_port->get_audio_buffer (nframes), nframes); + + if (scaling != 1.0f) { + buf.apply_gain (scaling, nframes); + } + + } else { + + /* on subsequent times around, merge data from + * the port with what is already there + */ + + if (scaling != 1.0f) { + buf.accumulate_with_gain_from (source_port->get_audio_buffer (nframes), nframes, 0, scaling); + } else { + buf.accumulate_from (source_port->get_audio_buffer (nframes), nframes); + } + } + } + + /* silence any remaining buffers */ + + for (; i < n_buffers; ++i) { + AudioBuffer& buf (bufs.get_audio (i)); + buf.silence (nframes); + } + + /* establish the initial setup of the buffer set, reflecting what was + copied into it. unless, of course, we are the auditioner, in which + case nothing was fed into it from the inputs at all. + */ + + if (!is_auditioner()) { + bufs.set_count (io->n_ports()); + } +}