#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"
#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"
PBD::Signal2<void,std::string, std::string> Session::Exported;
PBD::Signal1<int,boost::shared_ptr<Playlist> > Session::AskAboutPlaylistDeletion;
PBD::Signal0<void> Session::Quit;
+PBD::Signal0<void> Session::FeedbackDetected;
+PBD::Signal0<void> Session::SuccessfulGraphSort;
static void clean_up_session_event (SessionEvent* ev) { delete ev; }
const SessionEvent::RTeventCallback Session::rt_cleanup (clean_up_session_event);
, _post_transport_work (0)
, _send_timecode_update (false)
, _all_route_group (new RouteGroup (*this, "all"))
- , route_graph (new Graph(*this))
, routes (new RouteList)
, _total_free_4k_blocks (0)
, _bundles (new BundleList)
{
_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);
Session::~Session ()
{
+#ifdef PT_TIMING
+ ST.dump ("ST.dump");
+#endif
destroy ();
}
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<Port> 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<string> 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<Port> 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<RouteList> r = routes.reader ();
+ PBD::Unwinder<bool> 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<AudioPort> p = _monitor_out->input()->ports().nth_audio_port (n);
- boost::shared_ptr<AudioPort> 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<Route> 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<Bundle> 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<AudioPort> p = _monitor_out->input()->ports().nth_audio_port (n);
+ boost::shared_ptr<AudioPort> 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<Bundle> 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<string> 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<Port> 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<Port> 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;
}
}
}
}
}
- _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<RouteList> rls = routes.reader ();
- BootMessage (_("Connect to engine"));
- _engine.set_session (this);
+ PBD::Unwinder<bool> 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
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<RouteList> 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 */
boost::shared_ptr<AudioTrack> tr = boost::dynamic_pointer_cast<AudioTrack> (*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)
{
}
}
-struct RouteSorter {
- /** @return true to run r1 before r2, otherwise false */
- bool sort_by_rec_enabled (const boost::shared_ptr<Route>& r1, const boost::shared_ptr<Route>& 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<Route> r1, boost::shared_ptr<Route> 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<Route> r1, boost::shared_ptr<Route> rbase)
/* writer goes out of scope and forces update */
}
- //route_graph->dump(1);
-
#ifndef NDEBUG
boost::shared_ptr<RouteList> rl = routes.reader ();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
#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<RouteList> 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<RouteList> 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.
+ */
+ if (_process_graph) {
+ _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
}
/** 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<boost::shared_ptr<MidiTrack> >
Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template)
list<boost::shared_ptr<MidiTrack> > 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");
+ 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;
}
* @param name_template string to use for the start of the name, or "" to use "Audio".
*/
list< boost::shared_ptr<AudioTrack> >
-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;
list<boost::shared_ptr<AudioTrack> > 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");
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");
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;
XMLNode* node = tree.root();
- control_id = ntracks() + nbusses() + 1;
+ control_id = next_control_id ();
while (how_many) {
- XMLNode node_copy (*node); // make a copy so we can change the name if we need to
-
- std::string node_name = IO::name_from_state (*node_copy.children().front());
+ XMLNode node_copy (*node);
- /* 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*/
- }
+ /* Remove IDs of everything so that new ones are used */
+ node_copy.remove_property_recursively (X_("id"));
- /* 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> route (XMLRouteFactory (node_copy, 3000));
if (route == 0) {
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 ();
+ }
}
}
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 ();
void
Session::remove_route (boost::shared_ptr<Route> route)
{
- if (((route == _master_out) || (route == _monitor_out)) && !Config->get_allow_special_bus_removal()) {
+ if (route == _master_out) {
return;
}
}
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 ();
}
*/
resort_routes ();
- route_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 */
return boost::shared_ptr<Route> ((Route*) 0);
}
+boost::shared_ptr<Track>
+Session::track_by_diskstream_id (PBD::ID id)
+{
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (*i);
+ if (t && t->using_diskstream_id (id)) {
+ return t;
+ }
+ }
+
+ return boost::shared_ptr<Track> ();
+}
+
boost::shared_ptr<Route>
Session::route_by_remote_id (uint32_t id)
{
return;
}
- cerr << "Session begins graph reorder\n";
-
/* every track/bus asked for this to be handled but it was deferred because
we were connecting. do it now.
*/
request_input_change_handling ();
- cerr << "Session graph reorder 2\n";
resort_routes ();
- cerr << "Session graph reorder 3\n";
-
/* force all diskstreams to update their capture offset values to
reflect any changes in latencies within the graph.
*/
tr->set_capture_offset ();
}
}
-
- cerr << "Session graph reorder 4\n";
}
framecnt_t
}
}
+uint32_t
+Session::next_aux_send_id ()
+{
+ /* this doesn't really loop forever. just think about it */
+
+ while (true) {
+ for (boost::dynamic_bitset<uint32_t>::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 ()
{
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)
{
}
}
+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)
{
string
Session::source_search_path (DataType type) const
{
- string search_path;
+ vector<string> 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<space_and_path>::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<string> dirs;
}
for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
- search_path += ':';
- search_path += *i;
+ vector<string>::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<string>::iterator si = s.begin(); si != s.end(); ++si) {
+ if (!search_path.empty()) {
+ search_path += ':';
+ }
+ search_path += *si;
}
return search_path;
search_path = config.get_midi_search_path ();
break;
}
-
+
split (search_path, dirs, ':');
for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
}
}
+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;
+}
+
+uint32_t
+Session::next_control_id () const
+{
+ return ntracks() + nbusses() + 1;
+}