X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=26b3d47b7e10fbffbcd1d600e9ac33b9c9d57afc;hb=b087721d8aaf9470833414d01a72d826162946ab;hp=a42f2d44302a1332daec15d250f50c045c9386bd;hpb=0938a42440cc82ce8d0cb064840c258c863714ab;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index a42f2d4430..26b3d47b7e 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -86,6 +86,7 @@ #include "ardour/recent_sessions.h" #include "ardour/region_factory.h" #include "ardour/return.h" +#include "ardour/route_graph.h" #include "ardour/route_group.h" #include "ardour/send.h" #include "ardour/session.h" @@ -128,6 +129,8 @@ PBD::Signal0 Session::AutoBindingOff; PBD::Signal2 Session::Exported; PBD::Signal1 > Session::AskAboutPlaylistDeletion; PBD::Signal0 Session::Quit; +PBD::Signal0 Session::FeedbackDetected; +PBD::Signal0 Session::SuccessfulGraphSort; static void clean_up_session_event (SessionEvent* ev) { delete ev; } const SessionEvent::RTeventCallback Session::rt_cleanup (clean_up_session_event); @@ -148,7 +151,7 @@ Session::Session (AudioEngine &eng, , _post_transport_work (0) , _send_timecode_update (false) , _all_route_group (new RouteGroup (*this, "all")) - , route_graph (new Graph(*this)) + , _process_graph (new Graph (*this)) , routes (new RouteList) , _total_free_4k_blocks (0) , _bundles (new BundleList) @@ -214,6 +217,9 @@ Session::Session (AudioEngine &eng, Session::~Session () { +#ifdef PT_TIMING + ST.dump ("ST.dump"); +#endif destroy (); } @@ -546,7 +552,7 @@ Session::when_engine_running () uint32_t limit = _master_out->n_outputs().n_total(); for (uint32_t n = 0; n < limit; ++n) { - Port* p = _master_out->output()->nth (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]; @@ -576,8 +582,8 @@ Session::when_engine_running () if (_master_out) { for (uint32_t n = 0; n < limit; ++n) { - AudioPort* p = _monitor_out->input()->ports().nth_audio_port (n); - AudioPort* o = _master_out->output()->ports().nth_audio_port (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(); @@ -617,7 +623,7 @@ Session::when_engine_running () for (uint32_t n = 0; n < limit; ++n) { - Port* p = _monitor_out->output()->ports().port(DataType::AUDIO, 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]; @@ -779,21 +785,11 @@ Session::set_track_monitor_input_status (bool yn) boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); if (tr && tr->record_enabled ()) { //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl; - tr->monitor_input (yn); + tr->request_jack_monitors_input (yn); } } } -void -Session::reset_input_monitor_state () -{ - if (transport_rolling()) { - set_track_monitor_input_status (Config->get_monitoring_model() == HardwareMonitoring && !config.get_auto_input()); - } else { - set_track_monitor_input_status (Config->get_monitoring_model() == HardwareMonitoring); - } -} - void Session::auto_punch_start_changed (Location* location) { @@ -1051,6 +1047,8 @@ Session::step_back_from_record () if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { set_track_monitor_input_status (false); } + + RecordStateChanged (); /* emit signal */ } } @@ -1220,56 +1218,6 @@ Session::set_block_size (pframes_t nframes) } } -struct RouteSorter { - /** @return true to run r1 before r2, otherwise false */ - bool sort_by_rec_enabled (const boost::shared_ptr& r1, const boost::shared_ptr& r2) { - if (r1->record_enabled()) { - if (r2->record_enabled()) { - /* both rec-enabled, just use signal order */ - return r1->order_key(N_("signal")) < r2->order_key(N_("signal")); - } else { - /* r1 rec-enabled, r2 not rec-enabled, run r2 early */ - return false; - } - } else { - if (r2->record_enabled()) { - /* r2 rec-enabled, r1 not rec-enabled, run r1 early */ - return true; - } else { - /* neither rec-enabled, use signal order */ - return r1->order_key(N_("signal")) < r2->order_key(N_("signal")); - } - } - } - - bool operator() (boost::shared_ptr r1, boost::shared_ptr r2) { - if (r2->feeds (r1)) { - /* r1 fed by r2; run r2 early */ - return false; - } else if (r1->feeds (r2)) { - /* r2 fed by r1; run r1 early */ - return true; - } else { - if (r1->not_fed ()) { - if (r2->not_fed ()) { - /* no ardour-based connections inbound to either route. */ - return sort_by_rec_enabled (r1, r2); - } else { - /* r2 has connections, r1 does not; run r1 early */ - return true; - } - } else { - if (r2->not_fed()) { - /* r1 has connections, r2 does not; run r2 early */ - return false; - } else { - /* both r1 and r2 have connections, but not to each other. just use signal order */ - return r1->order_key(N_("signal")) < r2->order_key(N_("signal")); - } - } - } - } -}; static void trace_terminal (boost::shared_ptr r1, boost::shared_ptr rbase) @@ -1325,10 +1273,11 @@ void Session::resort_routes () { /* don't do anything here with signals emitted - by Routes while we are being destroyed. + by Routes during initial setup or while we + are being destroyed. */ - if (_state_of_the_state & Deletion) { + if (_state_of_the_state & (InitialConnecting | Deletion)) { return; } @@ -1339,7 +1288,7 @@ Session::resort_routes () /* writer goes out of scope and forces update */ } - //route_graph->dump(1); + //_process_graph->dump(1); #ifndef NDEBUG boost::shared_ptr rl = routes.reader (); @@ -1358,52 +1307,98 @@ Session::resort_routes () #endif } + +/** This is called whenever we need to rebuild the graph of how we will process + * routes. + * @param r List of routes, in any order. + */ + void Session::resort_routes_using (boost::shared_ptr r) { - RouteList::iterator i, j; - - for (i = r->begin(); i != r->end(); ++i) { + /* We are going to build a directed graph of our routes; + this is where the edges of that graph are put. + */ + + GraphEdges edges; + + /* Go through all routes doing two things: + * + * 1. Collect the edges of the route graph. Each of these edges + * is a pair of routes, one of which directly feeds the other + * either by a JACK connection or by an internal send. + * + * 2. Begin the process of making routes aware of which other + * routes directly or indirectly feed them. This information + * is used by the solo code. + */ + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + /* Clear out the route's list of direct or indirect feeds */ (*i)->clear_fed_by (); - for (j = r->begin(); j != r->end(); ++j) { - - /* although routes can feed themselves, it will - cause an endless recursive descent if we - detect it. so don't bother checking for - self-feeding. - */ - - if (*j == *i) { - continue; - } + for (RouteList::iterator j = r->begin(); j != r->end(); ++j) { bool via_sends_only; - if ((*j)->direct_feeds (*i, &via_sends_only)) { + /* See if this *j feeds *i according to the current state of the JACK + connections and internal sends. + */ + if ((*j)->direct_feeds_according_to_reality (*i, &via_sends_only)) { + /* add the edge to the graph (part #1) */ + edges.add (*j, *i, via_sends_only); + /* tell the route (for part #2) */ (*i)->add_fed_by (*j, via_sends_only); } } } - for (i = r->begin(); i != r->end(); ++i) { - trace_terminal (*i, *i); - } + /* Attempt a topological sort of the route graph */ + boost::shared_ptr sorted_routes = topological_sort (r, edges); + + if (sorted_routes) { + /* We got a satisfactory topological sort, so there is no feedback; + use this new graph. + + Note: the process graph rechain does not require a + topologically-sorted list, but hey ho. + */ + _process_graph->rechain (sorted_routes, edges); + _current_route_graph = edges; - RouteSorter cmp; - r->sort (cmp); + /* Complete the building of the routes' lists of what directly + or indirectly feeds them. + */ + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + trace_terminal (*i, *i); + } - route_graph->rechain (r); + r = sorted_routes; #ifndef NDEBUG - DEBUG_TRACE (DEBUG::Graph, "Routes resorted, order follows:\n"); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 signal order %2\n", - (*i)->name(), (*i)->order_key ("signal"))); - } + DEBUG_TRACE (DEBUG::Graph, "Routes resorted, order follows:\n"); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 signal order %2\n", + (*i)->name(), (*i)->order_key ("signal"))); + } #endif + SuccessfulGraphSort (); /* EMIT SIGNAL */ + + } else { + /* The topological sort failed, so we have a problem. Tell everyone + and stick to the old graph; this will continue to be processed, so + until the feedback is fixed, what is played back will not quite + reflect what is actually connected. Note also that we do not + do trace_terminal here, as it would fail due to an endless recursion, + so the solo code will think that everything is still connected + as it was before. + */ + + FeedbackDetected (); /* EMIT SIGNAL */ + } + } /** Find a route name starting with \a base, maybe followed by the @@ -1460,7 +1455,7 @@ Session::count_existing_track_channels (ChanCount& in, ChanCount& out) } /** Caller must not hold process lock - * @param name_template string to use for the start of the name, or "" to use "Midi". + * @param name_template string to use for the start of the name, or "" to use "MIDI". */ list > Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template) @@ -1474,10 +1469,10 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m control_id = ntracks() + nbusses(); - bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("Midi"); + bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("MIDI"); while (how_many) { - if (!find_route_name (name_template.empty() ? _("Midi") : name_template, ++track_id, track_name, sizeof(track_name), use_number)) { + if (!find_route_name (name_template.empty() ? _("MIDI") : name_template, ++track_id, track_name, sizeof(track_name), use_number)) { error << "cannot find name for new midi track" << endmsg; goto failed; } @@ -1494,7 +1489,7 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m track->use_new_diskstream(); #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - boost_debug_shared_ptr_mark_interesting (track.get(), "Track"); + // boost_debug_shared_ptr_mark_interesting (track.get(), "Track"); #endif { Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); @@ -1721,7 +1716,7 @@ Session::new_audio_track ( track->use_new_diskstream(); #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - boost_debug_shared_ptr_mark_interesting (track.get(), "Track"); + // boost_debug_shared_ptr_mark_interesting (track.get(), "Track"); #endif { Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); @@ -1836,7 +1831,7 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r } #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - boost_debug_shared_ptr_mark_interesting (bus.get(), "Route"); + // boost_debug_shared_ptr_mark_interesting (bus.get(), "Route"); #endif { Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); @@ -1895,7 +1890,6 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r RouteList Session::new_route_from_template (uint32_t how_many, const std::string& template_path) { - char name[32]; RouteList ret; uint32_t control_id; XMLTree tree; @@ -1911,27 +1905,35 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template while (how_many) { - XMLNode node_copy (*node); // make a copy so we can change the name if we need to + XMLNode node_copy (*node); - std::string node_name = IO::name_from_state (*node_copy.children().front()); + /* Remove IDs of everything so that new ones are used */ + node_copy.remove_property_recursively (X_("id")); - /* generate a new name by adding a number to the end of the template name */ - if (!find_route_name (node_name.c_str(), ++number, name, sizeof(name), true)) { - fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; - /*NOTREACHED*/ - } - - /* set IO children to use the new name */ - XMLNodeList const & children = node_copy.children (); - for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { - if ((*i)->name() == IO::state_node_name) { - IO::set_name_in_state (**i, name); + try { + string const route_name = node_copy.property(X_("name"))->value (); + + /* generate a new name by adding a number to the end of the template name */ + char name[32]; + if (!find_route_name (route_name.c_str(), ++number, name, sizeof(name), true)) { + fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; + /*NOTREACHED*/ } - } - Track::zero_diskstream_id_in_xml (node_copy); + /* set this name in the XML description that we are about to use */ + Route::set_name_in_state (node_copy, name); - try { + /* trim bitslots from listen sends so that new ones are used */ + XMLNodeList children = node_copy.children (); + for (XMLNodeList::iterator i = children.begin(); i != children.end(); ++i) { + if ((*i)->name() == X_("Processor")) { + XMLProperty* role = (*i)->property (X_("role")); + if (role && role->value() == X_("Listen")) { + (*i)->remove_property (X_("bitslot")); + } + } + } + boost::shared_ptr route (XMLRouteFactory (node_copy, 3000)); if (route == 0) { @@ -2220,7 +2222,7 @@ Session::remove_route (boost::shared_ptr route) */ resort_routes (); - route_graph->clear_other_chain (); + _process_graph->clear_other_chain (); /* get rid of it from the dead wood collection in the route list manager */ @@ -2594,6 +2596,21 @@ Session::route_by_id (PBD::ID id) return boost::shared_ptr ((Route*) 0); } +boost::shared_ptr +Session::track_by_diskstream_id (PBD::ID id) +{ + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + boost::shared_ptr t = boost::dynamic_pointer_cast (*i); + if (t && t->using_diskstream_id (id)) { + return t; + } + } + + return boost::shared_ptr (); +} + boost::shared_ptr Session::route_by_remote_id (uint32_t id) { @@ -4199,7 +4216,7 @@ Session::end_time_changed (framepos_t old) Location* l = _locations->auto_loop_location (); - if (l->end() == old) { + if (l && l->end() == old) { l->set_end (s->end(), true); } } @@ -4207,35 +4224,32 @@ Session::end_time_changed (framepos_t old) string Session::source_search_path (DataType type) const { - string search_path; + vector s; if (session_dirs.size() == 1) { switch (type) { case DataType::AUDIO: - search_path = _session_dir->sound_path().to_string(); + s.push_back ( _session_dir->sound_path().to_string()); break; case DataType::MIDI: - search_path = _session_dir->midi_path().to_string(); + s.push_back (_session_dir->midi_path().to_string()); break; } } else { for (vector::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) { SessionDirectory sdir (i->path); - if (!search_path.empty()) { - search_path += ':'; - } switch (type) { case DataType::AUDIO: - search_path += sdir.sound_path().to_string(); + s.push_back (sdir.sound_path().to_string()); break; case DataType::MIDI: - search_path += sdir.midi_path().to_string(); + s.push_back (sdir.midi_path().to_string()); break; } } } - /* now add user-specified locations + /* now check the explicit (possibly user-specified) search path */ vector dirs; @@ -4250,9 +4264,27 @@ Session::source_search_path (DataType type) const } for (vector::iterator i = dirs.begin(); i != dirs.end(); ++i) { - search_path += ':'; - search_path += *i; + vector::iterator si; + + for (si = s.begin(); si != s.end(); ++si) { + if ((*si) == *i) { + break; + } + } + + if (si == s.end()) { + s.push_back (*i); + } + } + + string search_path; + + for (vector::iterator si = s.begin(); si != s.end(); ++si) { + if (!search_path.empty()) { + search_path += ':'; + } + search_path += *si; } return search_path; @@ -4276,7 +4308,7 @@ Session::ensure_search_path_includes (const string& path, DataType type) search_path = config.get_midi_search_path (); break; } - + split (search_path, dirs, ':'); for (vector::iterator i = dirs.begin(); i != dirs.end(); ++i) { @@ -4519,3 +4551,16 @@ Session::update_latency_compensation (bool force_whole_graph) } } +char +Session::session_name_is_legal (const string& path) +{ + char illegal_chars[] = { '/', '\\', ':', ';', '\0' }; + + for (int i = 0; illegal_chars[i]; ++i) { + if (path.find (illegal_chars[i]) != string::npos) { + return illegal_chars[i]; + } + } + + return 0; +}