X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=30e80b2627ffcdf150b310af5bba1e4aefae2dbd;hb=dba601eeab9054ca4fac811c6c5e414d59fdc460;hp=37349cc5fb0cc2671f2697d2d94ca6c6c3e95807;hpb=0082e3364f7682ff41df52305cfff2cf7a861ef3;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 37349cc5fb..30e80b2627 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -45,6 +45,8 @@ #include "pbd/file_utils.h" #include "pbd/convert.h" #include "pbd/strsplit.h" +#include "pbd/strsplit.h" +#include "pbd/unwind.h" #include "ardour/amp.h" #include "ardour/analyser.h" @@ -151,7 +153,6 @@ Session::Session (AudioEngine &eng, , _post_transport_work (0) , _send_timecode_update (false) , _all_route_group (new RouteGroup (*this, "all")) - , _process_graph (new Graph (*this)) , routes (new RouteList) , _total_free_4k_blocks (0) , _bundles (new BundleList) @@ -167,6 +168,13 @@ Session::Session (AudioEngine &eng, { _locations = new Locations (*this); + if (how_many_dsp_threads () > 1) { + /* For now, only create the graph if we are using >1 DSP threads, as + it is a bit slower than the old code with 1 thread. + */ + _process_graph.reset (new Graph (*this)); + } + playlists.reset (new SessionPlaylists); _all_route_group->set_active (true, this); @@ -217,6 +225,9 @@ Session::Session (AudioEngine &eng, Session::~Session () { +#ifdef PT_TIMING + ST.dump ("ST.dump"); +#endif destroy (); } @@ -366,19 +377,27 @@ Session::when_engine_running () XMLNode* child = 0; _click_io.reset (new ClickIO (*this, "click")); + _click_gain.reset (new Amp (*this)); + _click_gain->activate (); if (state_tree && (child = find_named_node (*state_tree->root(), "Click")) != 0) { /* existing state for Click */ - int c; + int c = 0; if (Stateful::loading_state_version < 3000) { c = _click_io->set_state_2X (*child->children().front(), Stateful::loading_state_version, false); } else { - c = _click_io->set_state (*child->children().front(), Stateful::loading_state_version); + const XMLNodeList& children (child->children()); + XMLNodeList::const_iterator i = children.begin(); + if ((c = _click_io->set_state (**i, Stateful::loading_state_version)) == 0) { + ++i; + if (i != children.end()) { + c = _click_gain->set_state (**i, Stateful::loading_state_version); + } + } } - - + if (c == 0) { _clicking = Config->get_clicking (); @@ -536,105 +555,207 @@ Session::when_engine_running () hookup_io (); if (_is_new && !no_auto_connect()) { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock()); + auto_connect_master_bus (); + } - /* don't connect the master bus outputs if there is a monitor bus */ + _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty)); - if (_master_out && Config->get_auto_connect_standard_busses() && !_monitor_out) { + /* update latencies */ - /* if requested auto-connect the outputs to the first N physical ports. - */ + initialize_latencies (); - uint32_t limit = _master_out->n_outputs().n_total(); + /* hook us up to the engine */ - for (uint32_t n = 0; n < limit; ++n) { - boost::shared_ptr p = _master_out->output()->nth (n); - string connect_to; - if (outputs[p->type()].size() > n) { - connect_to = outputs[p->type()][n]; - } + BootMessage (_("Connect to engine")); + _engine.set_session (this); +} - if (!connect_to.empty() && p->connected_to (connect_to) == false) { - if (_master_out->output()->connect (p, connect_to, this)) { - error << string_compose (_("cannot connect master output %1 to %2"), n, connect_to) - << endmsg; - break; - } - } +void +Session::auto_connect_master_bus () +{ + if (!_master_out || !Config->get_auto_connect_standard_busses() || _monitor_out) { + return; + } + + /* if requested auto-connect the outputs to the first N physical ports. + */ + + uint32_t limit = _master_out->n_outputs().n_total(); + vector outputs[DataType::num_types]; + + for (uint32_t i = 0; i < DataType::num_types; ++i) { + _engine.get_physical_outputs (DataType (DataType::Symbol (i)), outputs[i]); + } + + for (uint32_t n = 0; n < limit; ++n) { + boost::shared_ptr p = _master_out->output()->nth (n); + string connect_to; + if (outputs[p->type()].size() > n) { + connect_to = outputs[p->type()][n]; + } + + if (!connect_to.empty() && p->connected_to (connect_to) == false) { + if (_master_out->output()->connect (p, connect_to, this)) { + error << string_compose (_("cannot connect master output %1 to %2"), n, connect_to) + << endmsg; + break; } } + } +} - if (_monitor_out) { +void +Session::remove_monitor_section () +{ + if (!_monitor_out) { + return; + } - /* AUDIO ONLY as of june 29th 2009, because listen semantics for anything else - are undefined, at best. - */ + /* force reversion to Solo-In-Pace */ + Config->set_solo_control_is_listen_control (false); - /* control out listens to master bus (but ignores it - under some conditions) - */ + { + /* Hold process lock while doing this so that we don't hear bits and + * pieces of audio as we work on each route. + */ + + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + + /* Connect tracks to monitor section. Note that in an + existing session, the internal sends will already exist, but we want the + routes to notice that they connect to the control out specifically. + */ + + + boost::shared_ptr r = routes.reader (); + PBD::Unwinder uw (ignore_route_processor_changes, true); + + for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { + + if ((*x)->is_monitor()) { + /* relax */ + } else if ((*x)->is_master()) { + /* relax */ + } else { + (*x)->remove_aux_or_listen (_monitor_out); + } + } + } - uint32_t limit = _monitor_out->n_inputs().n_audio(); + remove_route (_monitor_out); + auto_connect_master_bus (); +} - if (_master_out) { - for (uint32_t n = 0; n < limit; ++n) { - boost::shared_ptr p = _monitor_out->input()->ports().nth_audio_port (n); - boost::shared_ptr o = _master_out->output()->ports().nth_audio_port (n); +void +Session::add_monitor_section () +{ + RouteList rl; - if (o) { - string connect_to = o->name(); - if (_monitor_out->input()->connect (p, connect_to, this)) { - error << string_compose (_("cannot connect control input %1 to %2"), n, connect_to) - << endmsg; - break; - } - } - } - } + if (_monitor_out || !_master_out) { + return; + } - /* if control out is not connected, connect control out to physical outs - */ + boost::shared_ptr r (new Route (*this, _("monitor"), Route::MonitorOut, DataType::AUDIO)); - if (!_monitor_out->output()->connected ()) { + if (r->init ()) { + return; + } - if (!Config->get_monitor_bus_preferred_bundle().empty()) { +#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS + // boost_debug_shared_ptr_mark_interesting (r.get(), "Route"); +#endif + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + r->input()->ensure_io (_master_out->output()->n_ports(), false, this); + r->output()->ensure_io (_master_out->output()->n_ports(), false, this); + } - boost::shared_ptr b = bundle_by_name (Config->get_monitor_bus_preferred_bundle()); + rl.push_back (r); + add_routes (rl, false, false); + + assert (_monitor_out); - if (b) { - _monitor_out->output()->connect_ports_to_bundle (b, this); - } else { - warning << string_compose (_("The preferred I/O for the monitor bus (%1) cannot be found"), - Config->get_monitor_bus_preferred_bundle()) - << endmsg; - } + /* AUDIO ONLY as of june 29th 2009, because listen semantics for anything else + are undefined, at best. + */ + + uint32_t limit = _monitor_out->n_inputs().n_audio(); + + if (_master_out) { + + /* connect the inputs to the master bus outputs. this + * represents a separate data feed from the internal sends from + * each route. as of jan 2011, it allows the monitor section to + * conditionally ignore either the internal sends or the normal + * input feed, but we should really find a better way to do + * this, i think. + */ + + _master_out->output()->disconnect (this); + + for (uint32_t n = 0; n < limit; ++n) { + boost::shared_ptr p = _monitor_out->input()->ports().nth_audio_port (n); + boost::shared_ptr o = _master_out->output()->ports().nth_audio_port (n); + + if (o) { + string connect_to = o->name(); + if (_monitor_out->input()->connect (p, connect_to, this)) { + error << string_compose (_("cannot connect control input %1 to %2"), n, connect_to) + << endmsg; + break; + } + } + } + } + + /* if monitor section is not connected, connect it to physical outs + */ + + if (Config->get_auto_connect_standard_busses() && !_monitor_out->output()->connected ()) { + + if (!Config->get_monitor_bus_preferred_bundle().empty()) { + + boost::shared_ptr b = bundle_by_name (Config->get_monitor_bus_preferred_bundle()); + + if (b) { + _monitor_out->output()->connect_ports_to_bundle (b, this); + } else { + warning << string_compose (_("The preferred I/O for the monitor bus (%1) cannot be found"), + Config->get_monitor_bus_preferred_bundle()) + << endmsg; + } + + } else { + + /* Monitor bus is audio only */ - } else { + uint32_t mod = n_physical_outputs.get (DataType::AUDIO); + uint32_t limit = _monitor_out->n_outputs().get (DataType::AUDIO); + vector outputs[DataType::num_types]; - /* Monitor bus is audio only */ - uint32_t mod = n_physical_outputs.get (DataType::AUDIO); - uint32_t limit = _monitor_out->n_outputs().get (DataType::AUDIO); - - if (mod != 0) { - - for (uint32_t n = 0; n < limit; ++n) { - - boost::shared_ptr p = _monitor_out->output()->ports().port(DataType::AUDIO, n); - string connect_to; - if (outputs[DataType::AUDIO].size() > (n % mod)) { - connect_to = outputs[DataType::AUDIO][n % mod]; - } - - if (!connect_to.empty()) { - if (_monitor_out->output()->connect (p, connect_to, this)) { - error << string_compose ( - _("cannot connect control output %1 to %2"), - n, connect_to) - << endmsg; - break; - } - } + for (uint32_t i = 0; i < DataType::num_types; ++i) { + _engine.get_physical_outputs (DataType (DataType::Symbol (i)), outputs[i]); + } + + + if (mod != 0) { + + for (uint32_t n = 0; n < limit; ++n) { + + boost::shared_ptr p = _monitor_out->output()->ports().port(DataType::AUDIO, n); + string connect_to; + if (outputs[DataType::AUDIO].size() > (n % mod)) { + connect_to = outputs[DataType::AUDIO][n % mod]; + } + + if (!connect_to.empty()) { + if (_monitor_out->output()->connect (p, connect_to, this)) { + error << string_compose ( + _("cannot connect control output %1 to %2"), + n, connect_to) + << endmsg; + break; } } } @@ -642,16 +763,32 @@ Session::when_engine_running () } } - _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty)); + /* Hold process lock while doing this so that we don't hear bits and + * pieces of audio as we work on each route. + */ + + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - /* update latencies */ + /* Connect tracks to monitor section. Note that in an + existing session, the internal sends will already exist, but we want the + routes to notice that they connect to the control out specifically. + */ - initialize_latencies (); - /* hook us up to the engine */ + boost::shared_ptr rls = routes.reader (); - BootMessage (_("Connect to engine")); - _engine.set_session (this); + PBD::Unwinder uw (ignore_route_processor_changes, true); + + for (RouteList::iterator x = rls->begin(); x != rls->end(); ++x) { + + if ((*x)->is_monitor()) { + /* relax */ + } else if ((*x)->is_master()) { + /* relax */ + } else { + (*x)->enable_monitor_send (); + } + } } void @@ -698,30 +835,6 @@ Session::hookup_io () Delivery::reset_panners (); - /* Connect tracks to monitor/listen bus if there is one. Note that in an - existing session, the internal sends will already exist, but we want the - routes to notice that they connect to the control out specifically. - */ - - if (_monitor_out) { - boost::shared_ptr r = routes.reader (); - for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { - - if ((*x)->is_monitor()) { - - /* relax */ - - } else if ((*x)->is_master()) { - - /* relax */ - - } else { - - (*x)->listen_via_monitor (); - } - } - } - /* Anyone who cares about input state, wake up and do something */ IOConnectionsComplete (); /* EMIT SIGNAL */ @@ -1285,8 +1398,6 @@ Session::resort_routes () /* writer goes out of scope and forces update */ } - //_process_graph->dump(1); - #ifndef NDEBUG boost::shared_ptr rl = routes.reader (); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { @@ -1361,7 +1472,10 @@ Session::resort_routes_using (boost::shared_ptr r) Note: the process graph rechain does not require a topologically-sorted list, but hey ho. */ - _process_graph->rechain (sorted_routes, edges); + if (_process_graph) { + _process_graph->rechain (sorted_routes, edges); + } + _current_route_graph = edges; /* Complete the building of the routes' lists of what directly @@ -1464,7 +1578,7 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m list > ret; uint32_t control_id; - control_id = ntracks() + nbusses(); + control_id = next_control_id (); bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("MIDI"); @@ -1680,9 +1794,8 @@ Session::auto_connect_route (boost::shared_ptr route, ChanCount& existing * @param name_template string to use for the start of the name, or "" to use "Audio". */ list< boost::shared_ptr > -Session::new_audio_track ( - int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template - ) +Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group, + uint32_t how_many, string name_template) { char track_name[32]; uint32_t track_id = 0; @@ -1691,7 +1804,7 @@ Session::new_audio_track ( list > ret; uint32_t control_id; - control_id = ntracks() + nbusses() + 1; + control_id = next_control_id (); bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("Audio"); @@ -1810,7 +1923,7 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r RouteList ret; uint32_t control_id; - control_id = ntracks() + nbusses() + 1; + control_id = next_control_id (); bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("Bus"); @@ -1898,7 +2011,9 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template XMLNode* node = tree.root(); - control_id = ntracks() + nbusses() + 1; + IO::disable_connecting (); + + control_id = next_control_id (); while (how_many) { @@ -1975,6 +2090,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template out: if (!ret.empty()) { add_routes (ret, true, true); + IO::enable_connecting (); } return ret; @@ -2045,13 +2161,17 @@ Session::add_routes (RouteList& new_routes, bool auto_connect, bool save) if (_monitor_out && IO::connecting_legal) { - for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { - if ((*x)->is_monitor()) { - /* relax */ - } else if ((*x)->is_master()) { - /* relax */ - } else { - (*x)->listen_via_monitor (); + { + Glib::Mutex::Lock lm (_engine.process_lock()); + + for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { + if ((*x)->is_monitor()) { + /* relax */ + } else if ((*x)->is_master()) { + /* relax */ + } else { + (*x)->enable_monitor_send (); + } } } @@ -2134,14 +2254,14 @@ Session::add_internal_sends (boost::shared_ptr dest, Placement p, boost:: if (!dest->internal_return()) { dest->add_internal_return(); } - + for (RouteList::iterator i = senders->begin(); i != senders->end(); ++i) { - + if ((*i)->is_monitor() || (*i)->is_master() || (*i) == dest) { continue; } - - (*i)->listen_via (dest, p); + + (*i)->add_aux_send (dest, p); } graph_reordered (); @@ -2150,7 +2270,7 @@ Session::add_internal_sends (boost::shared_ptr dest, Placement p, boost:: void Session::remove_route (boost::shared_ptr route) { - if (((route == _master_out) || (route == _monitor_out)) && !Config->get_allow_special_bus_removal()) { + if (route == _master_out) { return; } @@ -2172,13 +2292,6 @@ Session::remove_route (boost::shared_ptr route) } if (route == _monitor_out) { - - /* cancel control outs for all routes */ - - for (RouteList::iterator r = rs->begin(); r != rs->end(); ++r) { - (*r)->drop_listen (_monitor_out); - } - _monitor_out.reset (); } @@ -2219,7 +2332,9 @@ Session::remove_route (boost::shared_ptr route) */ resort_routes (); - _process_graph->clear_other_chain (); + if (_process_graph) { + _process_graph->clear_other_chain (); + } /* get rid of it from the dead wood collection in the route list manager */ @@ -3530,6 +3645,26 @@ Session::next_send_id () } } +uint32_t +Session::next_aux_send_id () +{ + /* this doesn't really loop forever. just think about it */ + + while (true) { + for (boost::dynamic_bitset::size_type n = 0; n < aux_send_bitset.size(); ++n) { + if (!aux_send_bitset[n]) { + aux_send_bitset[n] = true; + return n; + + } + } + + /* none available, so resize and try again */ + + aux_send_bitset.resize (aux_send_bitset.size() + 16, false); + } +} + uint32_t Session::next_return_id () { @@ -3562,6 +3697,18 @@ Session::mark_send_id (uint32_t id) send_bitset[id] = true; } +void +Session::mark_aux_send_id (uint32_t id) +{ + if (id >= aux_send_bitset.size()) { + aux_send_bitset.resize (id+16, false); + } + if (aux_send_bitset[id]) { + warning << string_compose (_("aux send ID %1 appears to be in use already"), id) << endmsg; + } + aux_send_bitset[id] = true; +} + void Session::mark_return_id (uint32_t id) { @@ -3594,6 +3741,14 @@ Session::unmark_send_id (uint32_t id) } } +void +Session::unmark_aux_send_id (uint32_t id) +{ + if (id < aux_send_bitset.size()) { + aux_send_bitset[id] = false; + } +} + void Session::unmark_return_id (uint32_t id) { @@ -4561,3 +4716,9 @@ Session::session_name_is_legal (const string& path) return 0; } + +uint32_t +Session::next_control_id () const +{ + return ntracks() + nbusses() + 1; +}