X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=1231212d2ebdd7ea2e1e0a21a07dedabfa56b20d;hb=159d23be9f1aa9e389b2bca8aec1bb673e2e2549;hp=496afad83b3457470d121c809dcb24f7878b1901;hpb=0613ddd1f9b3ccd7de086738aab874c4153a20cc;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 496afad83b..1231212d2e 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -23,7 +23,6 @@ #include #include #include -#include #include /* sprintf(3) ... grrr */ #include #include @@ -211,7 +210,7 @@ Session::Session (AudioEngine &eng, , loop_changing (false) , last_loopend (0) , _session_dir (new SessionDirectory (fullpath)) - , _current_snapshot_name (snapshot_name) + , _current_snapshot_name (snapshot_name) , state_tree (0) , state_was_pending (false) , _state_of_the_state (StateOfTheState(CannotSave|InitialConnecting|Loading)) @@ -264,7 +263,6 @@ Session::Session (AudioEngine &eng, , _route_deletion_in_progress (false) , destructive_index (0) , _track_number_decimals(1) - , solo_update_disabled (false) , default_fade_steepness (0) , default_fade_msecs (0) , _total_free_4k_blocks (0) @@ -305,12 +303,12 @@ Session::Session (AudioEngine &eng, pthread_cond_init (&_rt_emit_cond, 0); pre_engine_init (fullpath); - + if (_is_new) { Stateful::loading_state_version = CURRENT_SESSION_FILE_VERSION; -#ifdef USE_TRACKS_CODE_FEATURES +#ifdef USE_TRACKS_CODE_FEATURES sr = EngineStateController::instance()->get_current_sample_rate(); #endif if (ensure_engine (sr)) { @@ -334,7 +332,7 @@ Session::Session (AudioEngine &eng, * of a template. */ - if (!mix_template.empty()) { + if (!mix_template.empty()) { if (load_state (_current_snapshot_name)) { throw SessionException (_("Failed to load template/snapshot state")); } @@ -349,7 +347,7 @@ Session::Session (AudioEngine &eng, if (load_state (_current_snapshot_name)) { throw SessionException (_("Failed to load state")); } - + /* try to get sample rate from XML state so that we * can influence the SR if we set up the audio * engine. @@ -357,7 +355,7 @@ Session::Session (AudioEngine &eng, if (state_tree) { const XMLProperty* prop; - if ((prop = state_tree->root()->property (X_("sample-rate"))) != 0) { + if ((prop = state_tree->root()->property (X_("sample-rate"))) != 0) { sr = atoi (prop->value()); } } @@ -399,40 +397,40 @@ Session::Session (AudioEngine &eng, _engine.reset_timebase (); #ifdef USE_TRACKS_CODE_FEATURES - + EngineStateController::instance()->set_session(this); - + if (_is_new ) { if ( ARDOUR::Profile->get_trx () ) { /* Waves Tracks: fill session with tracks basing on the amount of inputs. * each available input must have corresponding track when session starts. */ - + uint32_t how_many (0); - + std::vector inputs; EngineStateController::instance()->get_physical_audio_inputs(inputs); - + how_many = inputs.size(); - + list > tracks; - - // Track names after driver + + // Track names after driver if (Config->get_tracks_auto_naming() == NameAfterDriver) { string track_name = ""; for (std::vector::size_type i = 0; i < inputs.size(); ++i) { string track_name; track_name = inputs[i]; replace_all (track_name, "system:capture", ""); - + list > single_track = new_audio_track (1, 1, Normal, 0, 1, track_name); tracks.insert(tracks.begin(), single_track.front()); - } + } } else { // Default track names tracks = new_audio_track (1, 1, Normal, 0, how_many, string()); } - + if (tracks.size() != how_many) { destroy (); throw failed_constructor (); @@ -440,7 +438,7 @@ Session::Session (AudioEngine &eng, } } #endif - + _is_new = false; session_loaded (); @@ -449,9 +447,9 @@ Session::Session (AudioEngine &eng, Session::~Session () { -#ifdef PT_TIMING +#ifdef PT_TIMING ST.dump ("ST.dump"); -#endif +#endif destroy (); } @@ -494,7 +492,7 @@ Session::immediately_post_engine () * know that the engine is running, but before we either create a * session or set state for an existing one. */ - + 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. @@ -551,6 +549,12 @@ Session::destroy () drop_connections (); + /* shutdown control surface protocols while we still have ports + and the engine to move data to any devices. + */ + + ControlProtocolManager::instance().drop_protocols (); + _engine.remove_session (); #ifdef USE_TRACKS_CODE_FEATURES @@ -581,7 +585,7 @@ Session::destroy () _butler->drop_references (); delete _butler; _butler = 0; - + delete _all_route_group; DEBUG_TRACE (DEBUG::Destruction, "delete route groups\n"); @@ -670,7 +674,7 @@ Session::destroy () delete _locations; _locations = 0; delete _tempo_map; - + DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n"); #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS @@ -682,10 +686,10 @@ void Session::setup_ltc () { XMLNode* child = 0; - + _ltc_input.reset (new IO (*this, X_("LTC In"), IO::Input)); _ltc_output.reset (new IO (*this, X_("LTC Out"), IO::Output)); - + if (state_tree && (child = find_named_node (*state_tree->root(), X_("LTC In"))) != 0) { _ltc_input->set_state (*(child->children().front()), Stateful::loading_state_version); } else { @@ -695,7 +699,7 @@ Session::setup_ltc () } reconnect_ltc_input (); } - + if (state_tree && (child = find_named_node (*state_tree->root(), X_("LTC Out"))) != 0) { _ltc_output->set_state (*(child->children().front()), Stateful::loading_state_version); } else { @@ -705,11 +709,11 @@ Session::setup_ltc () } reconnect_ltc_output (); } - + /* fix up names of LTC ports because we don't want the normal * IO style of NAME/TYPE-{in,out}N */ - + _ltc_input->nth (0)->set_name (X_("LTC-in")); _ltc_output->nth (0)->set_name (X_("LTC-out")); } @@ -730,11 +734,11 @@ Session::setup_click () void Session::setup_click_state (const XMLNode* node) -{ +{ const XMLNode* child = 0; - + if (node && (child = find_named_node (*node, "Click")) != 0) { - + /* existing state for Click */ int c = 0; @@ -750,7 +754,7 @@ Session::setup_click_state (const XMLNode* node) } } } - + if (c == 0) { _clicking = Config->get_clicking (); @@ -924,7 +928,7 @@ Session::auto_connect_master_bus () if (!_master_out || !Config->get_auto_connect_standard_busses() || _monitor_out) { return; } - + // Waves Tracks: Do not connect master bas for Tracks if AutoConnectMaster option is not set // In this case it means "Multi Out" output mode if (ARDOUR::Profile->get_trx() && !(Config->get_output_auto_connect() & AutoConnectMaster) ) { @@ -933,21 +937,21 @@ Session::auto_connect_master_bus () /* 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) @@ -978,20 +982,20 @@ Session::remove_monitor_section () /* Hold process lock while doing this so that we don't hear bits and * pieces of audio as we work on each route. */ - + Glib::Threads::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()) { @@ -1039,17 +1043,17 @@ Session::add_monitor_section () rl.push_back (r); add_routes (rl, false, false, false); - + assert (_monitor_out); /* 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 @@ -1063,7 +1067,7 @@ Session::add_monitor_section () 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)) { @@ -1074,16 +1078,16 @@ Session::add_monitor_section () } } } - + /* 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, true, this); } else { @@ -1091,9 +1095,9 @@ Session::add_monitor_section () Config->get_monitor_bus_preferred_bundle()) << endmsg; } - + } else { - + /* Monitor bus is audio only */ vector outputs[DataType::num_types]; @@ -1104,17 +1108,17 @@ Session::add_monitor_section () uint32_t mod = outputs[DataType::AUDIO].size(); 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 ( @@ -1132,7 +1136,7 @@ Session::add_monitor_section () /* Hold process lock while doing this so that we don't hear bits and * pieces of audio as we work on each route. */ - + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); /* Connect tracks to monitor section. Note that in an @@ -1146,7 +1150,7 @@ Session::add_monitor_section () 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()) { @@ -1477,7 +1481,7 @@ Session::auto_loop_changed (Location* location) } } - + last_loopend = location->end(); set_dirty (); } @@ -1527,14 +1531,14 @@ Session::set_session_extents (framepos_t start, framepos_t end) //if there is no existing session, we need to make a new session location (should never happen) existing = new Location (*this, 0, 0, _("session"), Location::IsSessionRange); } - + if (end <= start) { error << _("Session: you can't use that location for session start/end)") << endmsg; return; } existing->set( start, end ); - + set_dirty(); } @@ -1586,7 +1590,7 @@ Session::set_auto_loop_location (Location* location) } } } - + /* take care of our stuff first */ auto_loop_changed (location); @@ -1608,7 +1612,7 @@ Session::update_skips (Location* loc, bool consolidate) if (_ignore_skips_updates) { return; } - + Locations::LocationList skips; if (consolidate) { @@ -1617,7 +1621,7 @@ Session::update_skips (Location* loc, bool consolidate) } sync_locations_to_skips (); - + set_dirty (); } @@ -1639,7 +1643,7 @@ Session::consolidate_skips (Location* loc) ++l; continue; } - + switch (Evoral::coverage ((*l)->start(), (*l)->end(), loc->start(), loc->end())) { case Evoral::OverlapInternal: case Evoral::OverlapExternal: @@ -1678,9 +1682,9 @@ Session::_sync_locations_to_skips () Locations::LocationList const & locs (_locations->list()); for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) { - + Location* location = *i; - + if (location->is_skip() && location->is_skipping()) { SessionEvent* ev = new SessionEvent (SessionEvent::Skip, SessionEvent::Add, location->start(), location->end(), 1.0); queue_event (ev); @@ -1699,7 +1703,7 @@ Session::location_added (Location *location) if (location->is_auto_loop()) { set_auto_loop_location (location); } - + if (location->is_session_range()) { /* no need for any signal handling or event setting with the session range, because we keep a direct reference to it and use its start/end directly. @@ -1726,7 +1730,7 @@ Session::location_added (Location *location) update_skips (location, true); } - + set_dirty (); } @@ -1737,7 +1741,7 @@ Session::location_removed (Location *location) set_auto_loop_location (0); set_track_loop (false); } - + if (location->is_auto_punch()) { set_auto_punch_location (0); } @@ -1748,7 +1752,7 @@ Session::location_removed (Location *location) } if (location->is_skip()) { - + update_skips (location, false); } @@ -1764,19 +1768,19 @@ Session::locations_changed () void Session::_locations_changed (const Locations::LocationList& locations) { - /* There was some mass-change in the Locations object. + /* There was some mass-change in the Locations object. We might be re-adding a location here but it doesn't actually matter for all the locations that the Session takes an interest in. */ - + { PBD::Unwinder protect_ignore_skip_updates (_ignore_skips_updates, true); for (Locations::LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) { location_added (*i); } } - + update_skips (NULL, false); } @@ -1794,7 +1798,7 @@ Session::enable_record () if (rs == Recording) { break; } - + if (g_atomic_int_compare_and_exchange (&_record_status, rs, Recording)) { _last_record_location = _transport_frame; @@ -1956,7 +1960,7 @@ Session::set_frame_rate (framecnt_t frames_per_second) clear_clicks (); reset_write_sources (false); - + // XXX we need some equivalent to this, somehow // SndFileSource::setup_standard_crossfades (frames_per_second); @@ -1973,7 +1977,7 @@ Session::set_block_size (pframes_t nframes) ::process(). It is therefore fine to do things that block here. */ - + { current_block_size = nframes; @@ -2060,6 +2064,10 @@ Session::resort_routes () return; } + if (_route_deletion_in_progress) { + return; + } + { RCUWriter writer (routes); boost::shared_ptr r = writer.get_copy (); @@ -2096,7 +2104,7 @@ Session::resort_routes_using (boost::shared_ptr r) /* 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: @@ -2109,7 +2117,7 @@ Session::resort_routes_using (boost::shared_ptr r) * 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 */ @@ -2133,7 +2141,7 @@ Session::resort_routes_using (boost::shared_ptr r) /* 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. @@ -2144,7 +2152,7 @@ Session::resort_routes_using (boost::shared_ptr r) if (_process_graph) { _process_graph->rechain (sorted_routes, edges); } - + _current_route_graph = edges; /* Complete the building of the routes' lists of what directly @@ -2175,7 +2183,7 @@ Session::resort_routes_using (boost::shared_ptr r) so the solo code will think that everything is still connected as it was before. */ - + FeedbackDetected (); /* EMIT SIGNAL */ } @@ -2200,7 +2208,7 @@ Session::find_route_name (string const & base, uint32_t& id, string& name, bool routes, but hidden objects like the click track. So check port names before anything else. */ - + for (vector::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) { if (base == *reserved) { definitely_add_number = true; @@ -2210,7 +2218,7 @@ Session::find_route_name (string const & base, uint32_t& id, string& name, bool break; } } - + if (!definitely_add_number && route_by_name (base) == 0) { /* juse use the base */ name = base; @@ -2225,7 +2233,7 @@ Session::find_route_name (string const & base, uint32_t& id, string& name, bool } ++id; - + } while (id < (UINT_MAX-1)); return false; @@ -2273,7 +2281,7 @@ Session::default_track_name_pattern (DataType t) * @param instrument plugin info for the instrument to insert pre-fader, if any */ list > -Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost::shared_ptr instrument, +Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost::shared_ptr instrument, TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template) { string track_name; @@ -2308,7 +2316,7 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost: { Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); if (track->input()->ensure_io (input, false, this)) { - error << "cannot configure " << input << " out configuration for new midi track" << endmsg; + error << "cannot configure " << input << " out configuration for new midi track" << endmsg; goto failed; } @@ -2364,7 +2372,7 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost: PluginPtr plugin = instrument->load (*this); boost::shared_ptr p (new PluginInsert (*this, plugin)); (*r)->add_processor (p, PreFader); - + } } } @@ -2523,7 +2531,7 @@ Session::auto_connect_route (boost::shared_ptr route, ChanCount& existing } } -#ifdef USE_TRACKS_CODE_FEATURES +#ifdef USE_TRACKS_CODE_FEATURES static bool compare_routes_by_remote_id (const boost::shared_ptr& route1, const boost::shared_ptr& route2) @@ -2538,42 +2546,42 @@ Session::reconnect_existing_routes (bool withLock, bool reconnect_master, bool r if (!IO::connecting_legal) { return; } - + // if we are deleting routes we will call this once at the end if (_route_deletion_in_progress) { return; } - + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock (), Glib::Threads::NOT_LOCK); - + if (withLock) { lm.acquire (); } - + // We need to disconnect the route's inputs and outputs first // basing on autoconnect configuration bool reconnectIputs = !(Config->get_input_auto_connect() & ManualConnect) && reconnect_inputs; bool reconnectOutputs = !(Config->get_output_auto_connect() & ManualConnect) && reconnect_outputs; - + ChanCount existing_inputs; ChanCount existing_outputs; count_existing_track_channels (existing_inputs, existing_outputs); - + //ChanCount inputs = ChanCount::ZERO; //ChanCount outputs = ChanCount::ZERO; - + RouteList existing_routes = *routes.reader (); existing_routes.sort (compare_routes_by_remote_id); - + { PBD::Unwinder protect_ignore_changes (_reconnecting_routes_in_progress, true); vector physinputs; vector physoutputs; - + EngineStateController::instance()->get_physical_audio_outputs(physoutputs); EngineStateController::instance()->get_physical_audio_inputs(physinputs); - + uint32_t input_n = 0; uint32_t output_n = 0; RouteList::iterator rIter = existing_routes.begin(); @@ -2589,52 +2597,52 @@ Session::reconnect_existing_routes (bool withLock, bool reconnect_master, bool r } else if (current_output_auto_connection == AutoConnectMaster) { (*rIter)->amp()->activate(); } - + if (reconnectIputs) { (*rIter)->input()->disconnect (this); //GZ: check this; could be heavy - + for (uint32_t route_input_n = 0; route_input_n < (*rIter)->n_inputs().get(DataType::AUDIO); ++route_input_n) { - + if (current_input_auto_connection & AutoConnectPhysical) { - + if ( input_n == physinputs.size() ) { break; } - + string port = physinputs[input_n]; - + if (port.empty() ) { error << "Physical Input number "<< input_n << " is unavailable and cannot be connected" << endmsg; } - + //GZ: check this; could be heavy (*rIter)->input()->connect ((*rIter)->input()->ports().port(DataType::AUDIO, route_input_n), port, this); ++input_n; } } } - + if (reconnectOutputs) { - + //normalize route ouptuts: reduce the amount outputs to be equal to the amount of inputs if (current_output_auto_connection & AutoConnectPhysical) { - + //GZ: check this; could be heavy (*rIter)->output()->disconnect (this); size_t route_inputs_count = (*rIter)->n_inputs().get(DataType::AUDIO); - + //GZ: check this; could be heavy (*rIter)->output()->ensure_io(ChanCount(DataType::AUDIO, route_inputs_count), false, this ); - + } else if (current_output_auto_connection & AutoConnectMaster){ - + if (!reconnect_master) { continue; } - + //GZ: check this; could be heavy (*rIter)->output()->disconnect (this); - + if (_master_out) { uint32_t master_inputs_count = _master_out->n_inputs().get(DataType::AUDIO); (*rIter)->output()->ensure_io(ChanCount(DataType::AUDIO, master_inputs_count), false, this ); @@ -2643,54 +2651,54 @@ Session::reconnect_existing_routes (bool withLock, bool reconnect_master, bool r break; } } - + for (uint32_t route_output_n = 0; route_output_n < (*rIter)->n_outputs().get(DataType::AUDIO); ++route_output_n) { if (current_output_auto_connection & AutoConnectPhysical) { - + if ( output_n == physoutputs.size() ) { break; } - + string port = physoutputs[output_n]; - + if (port.empty() ) { error << "Physical Output number "<< output_n << " is unavailable and cannot be connected" << endmsg; } - + //GZ: check this; could be heavy (*rIter)->output()->connect ((*rIter)->output()->ports().port(DataType::AUDIO, route_output_n), port, this); ++output_n; - + } else if (current_output_auto_connection & AutoConnectMaster) { - + if ( route_output_n == _master_out->n_inputs().get(DataType::AUDIO) ) { break; } - + // connect to master bus string port = _master_out->input()->ports().port(DataType::AUDIO, route_output_n)->name(); - + if (port.empty() ) { error << "MasterBus Input number "<< route_output_n << " is unavailable and cannot be connected" << endmsg; } - - + + //GZ: check this; could be heavy (*rIter)->output()->connect ((*rIter)->output()->ports().port(DataType::AUDIO, route_output_n), port, this); - + } } } - + //auto_connect_route (*rIter, inputs, outputs, false, reconnectIputs); } - + _master_out->output()->disconnect (this); auto_connect_master_bus (); } - + graph_reordered (); - + session_routes_reconnected (); /* EMIT SIGNAL */ } @@ -2698,16 +2706,16 @@ void Session::reconnect_midi_scene_ports(bool inputs) { if (inputs ) { - + boost::shared_ptr scene_in_ptr = scene_in(); if (scene_in_ptr) { scene_in_ptr->disconnect_all (); - + std::vector midi_port_states; EngineStateController::instance()->get_physical_midi_input_states (midi_port_states); - + std::vector::iterator state_iter = midi_port_states.begin(); - + for (; state_iter != midi_port_states.end(); ++state_iter) { if (state_iter->active && state_iter->available && state_iter->scene_connected) { scene_in_ptr->connect (state_iter->name); @@ -2716,17 +2724,17 @@ Session::reconnect_midi_scene_ports(bool inputs) } } else { - + boost::shared_ptr scene_out_ptr = scene_out(); - + if (scene_out_ptr ) { scene_out_ptr->disconnect_all (); std::vector midi_port_states; EngineStateController::instance()->get_physical_midi_output_states (midi_port_states); - + std::vector::iterator state_iter = midi_port_states.begin(); - + for (; state_iter != midi_port_states.end(); ++state_iter) { if (state_iter->active && state_iter->available && state_iter->scene_connected) { scene_out_ptr->connect (state_iter->name); @@ -2746,24 +2754,24 @@ Session::reconnect_mtc_ports () } mtc_in_ptr->disconnect_all (); - + std::vector midi_port_states; EngineStateController::instance()->get_physical_midi_input_states (midi_port_states); - + std::vector::iterator state_iter = midi_port_states.begin(); - + for (; state_iter != midi_port_states.end(); ++state_iter) { if (state_iter->available && state_iter->mtc_in) { mtc_in_ptr->connect (state_iter->name); } } - + if (!_midi_ports->mtc_input_port ()->connected () && config.get_external_sync () && (Config->get_sync_source () == MTC) ) { config.set_external_sync (false); } - + if ( ARDOUR::Profile->get_trx () ) { // Tracks need this signal to update timecode_source_dropdown MtcOrLtcInputPortChanged (); //emit signal @@ -2774,30 +2782,30 @@ void Session::reconnect_mmc_ports(bool inputs) { if (inputs ) { // get all enabled midi input ports - + boost::shared_ptr mmc_in_ptr = _midi_ports->mmc_in(); if (mmc_in_ptr) { mmc_in_ptr->disconnect_all (); std::vector enabled_midi_inputs; EngineStateController::instance()->get_physical_midi_inputs (enabled_midi_inputs); - + std::vector::iterator port_iter = enabled_midi_inputs.begin(); - + for (; port_iter != enabled_midi_inputs.end(); ++port_iter) { mmc_in_ptr->connect (*port_iter); } } } else { // get all enabled midi output ports - + boost::shared_ptr mmc_out_ptr = _midi_ports->mmc_out(); if (mmc_out_ptr ) { mmc_out_ptr->disconnect_all (); std::vector enabled_midi_outputs; EngineStateController::instance()->get_physical_midi_outputs (enabled_midi_outputs); - + std::vector::iterator port_iter = enabled_midi_outputs.begin(); - + for (; port_iter != enabled_midi_outputs.end(); ++port_iter) { mmc_out_ptr->connect (*port_iter); } @@ -2811,7 +2819,7 @@ Session::reconnect_mmc_ports(bool inputs) * @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, +Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template) { string track_name; @@ -2822,7 +2830,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod const string name_pattern = default_track_name_pattern (DataType::AUDIO); bool const use_number = (how_many != 1) || name_template.empty () || (name_template == name_pattern); - + while (how_many) { if (!find_route_name (name_template.empty() ? _(name_pattern.c_str()) : name_template, ++track_id, track_name, use_number)) { @@ -2934,7 +2942,7 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r RouteList ret; bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("Bus"); - + while (how_many) { if (!find_route_name (name_template.empty () ? _("Bus") : name_template, ++bus_id, bus_name, use_number)) { error << "cannot find name for new audio bus" << endmsg; @@ -2980,7 +2988,7 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r bus->add_internal_return (); ret.push_back (bus); - + RouteAddedOrRemoved (true); /* EMIT SIGNAL */ ARDOUR::GUIIdle (); @@ -3059,7 +3067,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template } else { 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 */ if (!find_route_name (route_name.c_str(), ++number, name, true)) { fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; @@ -3080,7 +3088,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template } } } - + boost::shared_ptr route (XMLRouteFactory (node_copy, 3000)); if (route == 0) { @@ -3153,17 +3161,17 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output update_latency (true); update_latency (false); - + set_dirty(); - + if (save) { save_state (_current_snapshot_name); } - + reassign_track_numbers(); update_route_record_state (); - + RouteAdded (new_routes); /* EMIT SIGNAL */ } @@ -3202,8 +3210,8 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool boost::weak_ptr wpr (*x); boost::shared_ptr r (*x); - r->listen_changed.connect_same_thread (*this, boost::bind (&Session::route_listen_changed, this, _1, wpr)); - r->solo_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2, wpr)); + r->listen_changed.connect_same_thread (*this, boost::bind (&Session::route_listen_changed, this, _2, wpr)); + r->solo_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _3, wpr)); r->solo_isolated_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, _1, wpr)); r->mute_changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this, _1)); r->output()->changed.connect_same_thread (*this, boost::bind (&Session::set_worst_io_latencies_x, this, _1, _2)); @@ -3255,8 +3263,8 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool } if (_monitor_out && IO::connecting_legal) { - Glib::Threads::Mutex::Lock lm (_engine.process_lock()); - + Glib::Threads::Mutex::Lock lm (_engine.process_lock()); + for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { if ((*x)->is_monitor()) { /* relax */ @@ -3359,44 +3367,43 @@ Session::add_internal_send (boost::shared_ptr dest, boost::shared_ptr routes_to_remove) { - PBD::Unwinder uw_flag (_route_deletion_in_progress, true); - { // RCU Writer scope + PBD::Unwinder uw_flag (_route_deletion_in_progress, true); RCUWriter writer (routes); boost::shared_ptr rs = writer.get_copy (); - - + + for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) { - + if (*iter == _master_out) { continue; } - + (*iter)->set_solo (false, this); - + rs->remove (*iter); - + /* deleting the master out seems like a dumb idea, but its more of a UI policy issue than our concern. */ - + if (*iter == _master_out) { _master_out = boost::shared_ptr (); } - + if (*iter == _monitor_out) { _monitor_out.reset (); } // We need to disconnect the route's inputs and outputs - + (*iter)->input()->disconnect (0); (*iter)->output()->disconnect (0); - + /* if the route had internal sends sending to it, remove them */ if ((*iter)->internal_return()) { - + boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { boost::shared_ptr s = (*i)->internal_send_for (*iter); @@ -3405,14 +3412,14 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) } } } - + /* if the monitoring section had a pointer to this route, remove it */ if (_monitor_out && !(*iter)->is_master() && !(*iter)->is_monitor()) { Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); PBD::Unwinder uw (ignore_route_processor_changes, true); (*iter)->remove_aux_or_listen (_monitor_out); } - + boost::shared_ptr mt = boost::dynamic_pointer_cast (*iter); if (mt && mt->step_editing()) { if (_step_editors > 0) { @@ -3420,16 +3427,16 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) } } } - + /* writer goes out of scope, forces route list update */ } // end of RCU Writer scope - + update_route_solo_state (); RouteAddedOrRemoved (false); /* EMIT SIGNAL */ update_latency_compensation (); set_dirty(); - + /* Re-sort routes to remove the graph's current references to the one that is * going away, then flush old references out of the graph. * Wave Tracks: reconnect routes @@ -3441,16 +3448,16 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) routes.flush (); // maybe unsafe, see below. resort_routes (); #endif - + if (_process_graph) { _process_graph->clear_other_chain (); } - + /* get rid of it from the dead wood collection in the route list manager */ /* XXX i think this is unsafe as it currently stands, but i am not sure. (pd, october 2nd, 2006) */ - + routes.flush (); - + /* try to cause everyone to drop their references * and unregister ports from the backend */ @@ -3458,11 +3465,11 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) { (*iter)->drop_references (); } - + Route::RemoteControlIDChange(); /* EMIT SIGNAL */ - + /* save the new state of the world */ - + if (save_state (_current_snapshot_name)) { save_history (_current_snapshot_name); } @@ -3486,24 +3493,29 @@ Session::route_mute_changed (void* /*src*/) } void -Session::route_listen_changed (void* /*src*/, boost::weak_ptr wpr) +Session::route_listen_changed (bool group_override, boost::weak_ptr wpr) { boost::shared_ptr route = wpr.lock(); if (!route) { - error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_changed")) << endmsg; + error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_listen_changed")) << endmsg; return; } if (route->listening_via_monitor ()) { if (Config->get_exclusive_solo()) { - /* new listen: disable all other listen */ + /* new listen: disable all other listen, except solo-grouped channels */ + RouteGroup* rg = route->route_group (); + bool leave_group_alone = (rg && rg->is_active() && rg->is_solo()); + if (group_override && rg) { + leave_group_alone = !leave_group_alone; + } boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() || (leave_group_alone && ((*i)->route_group() == rg))) { continue; } - (*i)->set_listen (false, this); + (*i)->set_listen (false, this, group_override); } } @@ -3523,7 +3535,7 @@ Session::route_solo_isolated_changed (void* /*src*/, boost::weak_ptr wpr) if (!route) { /* should not happen */ - error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_changed")) << endmsg; + error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_isolated_changed")) << endmsg; return; } @@ -3547,121 +3559,7 @@ Session::route_solo_isolated_changed (void* /*src*/, boost::weak_ptr wpr) } void -Session::routes_solo_changed (boost::shared_ptr solo_change_routes) -{ - if (solo_update_disabled) { - // We know already - DEBUG_TRACE (DEBUG::Solo, "solo update disabled - changed ignored\n"); - return; - } - - if (solo_change_routes->empty() ) { - return; - } - - boost::shared_ptr non_solo_change_routes (new RouteList); - boost::shared_ptr r = routes.reader (); - int32_t delta; - - std::set_difference (r->begin(), r->end(), - solo_change_routes->begin(), solo_change_routes->end(), - std::back_inserter(*non_solo_change_routes) ); - - DEBUG_TRACE (DEBUG::Solo, string_compose ("propagate solo change, delta = %1\n", delta)); - - solo_update_disabled = true; - RouteList uninvolved; - - for (RouteList::iterator route = solo_change_routes->begin(); route != solo_change_routes->end(); ++route) { - - if ((*route)->self_soloed() ) { - delta = 1; - } else { - delta = -1; - } - - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1\n", (*route)->name())); - - for (RouteList::iterator i = non_solo_change_routes->begin(); i != non_solo_change_routes->end(); ++i) { - bool via_sends_only; - bool in_signal_flow; - - if ((*i) == *route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() ) { - continue; - } - - in_signal_flow = false; - - DEBUG_TRACE (DEBUG::Solo, string_compose ("check feed from %1\n", (*i)->name())); - - if ((*i)->feeds (*route, &via_sends_only)) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a feed from %1\n", (*i)->name())); - if (!via_sends_only) { - if (!(*route)->soloed_by_others_upstream()) { - (*i)->mod_solo_by_others_downstream (delta); - } - } else { - DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a send-only feed from %1\n", (*i)->name())); - } - in_signal_flow = true; - } else { - DEBUG_TRACE (DEBUG::Solo, string_compose ("\tno feed from %1\n", (*i)->name())); - } - - DEBUG_TRACE (DEBUG::Solo, string_compose ("check feed to %1\n", (*i)->name())); - - if ((*route)->feeds (*i, &via_sends_only)) { - /* propagate solo upstream only if routing other than - sends is involved, but do consider the other route - (*i) to be part of the signal flow even if only - sends are involved. - */ - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 feeds %2 via sends only %3 sboD %4 sboU %5\n", - (*route)->name(), - (*i)->name(), - via_sends_only, - (*route)->soloed_by_others_downstream(), - (*route)->soloed_by_others_upstream())); - if (!via_sends_only) { - if (!(*route)->soloed_by_others_downstream()) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("\tmod %1 by %2\n", (*i)->name(), delta)); - (*i)->mod_solo_by_others_upstream (delta); - } else { - DEBUG_TRACE (DEBUG::Solo, "\talready soloed by others downstream\n"); - } - } else { - DEBUG_TRACE (DEBUG::Solo, string_compose ("\tfeed to %1 ignored, sends-only\n", (*i)->name())); - } - in_signal_flow = true; - } else { - DEBUG_TRACE (DEBUG::Solo, "\tno feed to\n"); - } - - if (!in_signal_flow) { - uninvolved.push_back (*i); - } - } - } - solo_update_disabled = false; - DEBUG_TRACE (DEBUG::Solo, "propagation complete\n"); - - update_route_solo_state (); - - /* now notify that the mute state of the routes not involved in the signal - pathway of the just-solo-changed route may have altered. - */ - - for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("mute change for %1\n", (*i)->name() )); - (*i)->mute_changed (this); - } - - SoloChanged (); /* EMIT SIGNAL */ - set_dirty(); -} - -void -Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_ptr wpr) +Session::route_solo_changed (bool self_solo_change, bool group_override, boost::weak_ptr wpr) { DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1\n", self_solo_change)); @@ -3670,12 +3568,6 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p return; } - if (solo_update_disabled) { - // We know already - DEBUG_TRACE (DEBUG::Solo, "solo update disabled - changed ignored\n"); - return; - } - boost::shared_ptr route = wpr.lock (); assert (route); @@ -3690,24 +3582,24 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p RouteGroup* rg = route->route_group (); bool leave_group_alone = (rg && rg->is_active() && rg->is_solo()); - + if (group_override && rg) { + leave_group_alone = !leave_group_alone; + } if (delta == 1 && Config->get_exclusive_solo()) { - + /* new solo: disable all other solos, but not the group if its solo-enabled */ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() || + if ((*i) == route || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() || (leave_group_alone && ((*i)->route_group() == rg))) { continue; } - (*i)->set_solo (false, this); + (*i)->set_solo (false, this, group_override); } } DEBUG_TRACE (DEBUG::Solo, string_compose ("propagate solo change, delta = %1\n", delta)); - solo_update_disabled = true; - RouteList uninvolved; DEBUG_TRACE (DEBUG::Solo, string_compose ("%1\n", route->name())); @@ -3716,7 +3608,7 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p bool via_sends_only; bool in_signal_flow; - if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() || + if ((*i) == route || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() || (leave_group_alone && ((*i)->route_group() == rg))) { continue; } @@ -3724,12 +3616,14 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p in_signal_flow = false; DEBUG_TRACE (DEBUG::Solo, string_compose ("check feed from %1\n", (*i)->name())); - + if ((*i)->feeds (route, &via_sends_only)) { DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a feed from %1\n", (*i)->name())); if (!via_sends_only) { if (!route->soloed_by_others_upstream()) { (*i)->mod_solo_by_others_downstream (delta); + } else { + DEBUG_TRACE (DEBUG::Solo, "\talready soloed by others upstream\n"); } } else { DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a send-only feed from %1\n", (*i)->name())); @@ -3738,7 +3632,7 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p } else { DEBUG_TRACE (DEBUG::Solo, string_compose ("\tno feed from %1\n", (*i)->name())); } - + DEBUG_TRACE (DEBUG::Solo, string_compose ("check feed to %1\n", (*i)->name())); if (route->feeds (*i, &via_sends_only)) { @@ -3754,12 +3648,9 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p route->soloed_by_others_downstream(), route->soloed_by_others_upstream())); if (!via_sends_only) { - if (!route->soloed_by_others_downstream()) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("\tmod %1 by %2\n", (*i)->name(), delta)); - (*i)->mod_solo_by_others_upstream (delta); - } else { - DEBUG_TRACE (DEBUG::Solo, "\talready soloed by others downstream\n"); - } + //NB. Triggers Invert Push, which handles soloed by downstream + DEBUG_TRACE (DEBUG::Solo, string_compose ("\tmod %1 by %2\n", (*i)->name(), delta)); + (*i)->mod_solo_by_others_upstream (delta); } else { DEBUG_TRACE (DEBUG::Solo, string_compose ("\tfeed to %1 ignored, sends-only\n", (*i)->name())); } @@ -3773,7 +3664,6 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p } } - solo_update_disabled = false; DEBUG_TRACE (DEBUG::Solo, "propagation complete\n"); update_route_solo_state (r); @@ -3863,7 +3753,7 @@ Session::io_name_is_legal (const std::string& name) return false; } } - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->name() == name) { return false; @@ -3898,19 +3788,19 @@ Session::set_exclusive_input_active (boost::shared_ptr rl, bool onoff for (RouteList::iterator rt = rl->begin(); rt != rl->end(); ++rt) { PortSet& ps ((*rt)->input()->ports()); - + for (PortSet::iterator p = ps.begin(); p != ps.end(); ++p) { p->get_connections (connections); } - + for (vector::iterator s = connections.begin(); s != connections.end(); ++s) { routes_using_input_from (*s, rl2); } - + /* scan all relevant routes to see if others are on or off */ - + bool others_are_already_on = false; - + for (RouteList::iterator r = rl2.begin(); r != rl2.end(); ++r) { boost::shared_ptr mt = boost::dynamic_pointer_cast (*r); @@ -3928,11 +3818,11 @@ Session::set_exclusive_input_active (boost::shared_ptr rl, bool onoff mt->set_input_active (onoff); } } - + if (flip_others) { /* globally reverse other routes */ - + for (RouteList::iterator r = rl2.begin(); r != rl2.end(); ++r) { if ((*r) != (*rt)) { boost::shared_ptr mt = boost::dynamic_pointer_cast (*r); @@ -4252,13 +4142,13 @@ Session::add_source (boost::shared_ptr source) /* yay, new source */ boost::shared_ptr fs = boost::dynamic_pointer_cast (source); - + if (fs) { if (!fs->within_session()) { ensure_search_path_includes (Glib::path_get_dirname (fs->path()), fs->type()); } } - + set_dirty(); boost::shared_ptr afs; @@ -4354,7 +4244,7 @@ Session::midi_source_by_path (const std::string& path) const = boost::dynamic_pointer_cast(s->second); boost::shared_ptr fs = boost::dynamic_pointer_cast(s->second); - + if (ms && fs && fs->path() == path) { return ms; } @@ -4402,9 +4292,9 @@ Session::construct_peak_filepath (const string& filepath, const bool in_session, string session_path; bool in_another_session = true; - + if (filepath.find (interchange_dir_string) != string::npos) { - + session_path = Glib::path_get_dirname (filepath); /* now ends in audiofiles */ session_path = Glib::path_get_dirname (session_path); /* now ends in session name */ session_path = Glib::path_get_dirname (session_path); /* now ends in interchange */ @@ -4421,7 +4311,7 @@ Session::construct_peak_filepath (const string& filepath, const bool in_session, } else { in_another_session = false; } - + if (in_another_session) { SessionDirectory sd (session_path); @@ -4442,27 +4332,27 @@ Session::construct_peak_filepath (const string& filepath, const bool in_session, if (!in_session) { path = Glib::path_get_dirname (filepath); } - + return peak_file_helper (_session_dir->peak_path(), path, Glib::path_get_basename (filepath), !old_peak_name); } string Session::new_audio_source_path_for_embedded (const std::string& path) { - /* embedded source: + /* embedded source: * * we know that the filename is already unique because it exists - * out in the filesystem. + * out in the filesystem. * * However, when we bring it into the session, we could get a * collision. * * Eg. two embedded files: - * + * * /foo/bar/baz.wav * /frob/nic/baz.wav * - * When merged into session, these collide. + * When merged into session, these collide. * * There will not be a conflict with in-memory sources * because when the source was created we already picked @@ -4476,7 +4366,7 @@ Session::new_audio_source_path_for_embedded (const std::string& path) SessionDirectory sdir (get_best_session_directory_for_new_audio()); string base = Glib::path_get_basename (path); string newpath = Glib::build_filename (sdir.sound_path(), base); - + if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) { MD5 md5; @@ -4484,14 +4374,14 @@ Session::new_audio_source_path_for_embedded (const std::string& path) md5.digestString (path.c_str()); md5.writeToString (); base = md5.digestChars; - + string ext = get_suffix (path); if (!ext.empty()) { base += '.'; base += ext; } - + newpath = Glib::build_filename (sdir.sound_path(), base); /* if this collides, we're screwed */ @@ -4506,14 +4396,14 @@ Session::new_audio_source_path_for_embedded (const std::string& path) return newpath; } -/** Return true if there are no audio file sources that use @param name as - * the filename component of their path. +/** Return true if there are no audio file sources that use @param name as + * the filename component of their path. * * Return false otherwise. * - * This method MUST ONLY be used to check in-session, mono files since it + * This method MUST ONLY be used to check in-session, mono files since it * hard-codes the channel of the audio file source we are looking for as zero. - * + * * If/when Ardour supports native files in non-mono formats, the logic here * will need to be revisited. */ @@ -4525,7 +4415,7 @@ Session::audio_source_name_is_unique (const string& name) uint32_t existing = 0; for (vector::const_iterator i = sdirs.begin(); i != sdirs.end(); ++i) { - + /* note that we search *without* the extension so that we don't end up both "Audio 1-1.wav" and "Audio 1-1.caf" in the event that this new name is required for @@ -4533,12 +4423,12 @@ Session::audio_source_name_is_unique (const string& name) */ const string spath = *i; - + if (matching_unsuffixed_filename_exists_in (spath, name)) { existing++; break; } - + /* it is possible that we have the path already * assigned to a source that has not yet been written * (ie. the write source for a diskstream). we have to @@ -4547,8 +4437,8 @@ Session::audio_source_name_is_unique (const string& name) * two Sources point to the same file with different * notions of their removability. */ - - + + string possible_path = Glib::build_filename (spath, name); if (audio_source_by_path_and_channel (possible_path, 0)) { @@ -4565,20 +4455,20 @@ Session::format_audio_source_name (const string& legalized_base, uint32_t nchan, { ostringstream sstr; const string ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO); - + if (Profile->get_trx() && destructive) { sstr << 'T'; sstr << setfill ('0') << setw (4) << cnt; sstr << legalized_base; } else { sstr << legalized_base; - + if (take_required || related_exists) { sstr << '-'; sstr << cnt; } } - + if (nchan == 2) { if (chan == 0) { sstr << "%L"; @@ -4595,7 +4485,7 @@ Session::format_audio_source_name (const string& legalized_base, uint32_t nchan, sstr << chan+1; } } - + sstr << ext; return sstr.str(); @@ -4618,11 +4508,11 @@ Session::new_audio_source_path (const string& base, uint32_t nchan, uint32_t cha for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) { possible_name = format_audio_source_name (legalized, nchan, chan, destructive, take_required, cnt, some_related_source_name_exists); - + if (audio_source_name_is_unique (possible_name)) { break; } - + some_related_source_name_exists = true; if (cnt > limit) { @@ -4677,14 +4567,14 @@ Session::new_midi_source_path (const string& base) vector::iterator i; uint32_t existing = 0; - + for (vector::const_iterator i = sdirs.begin(); i != sdirs.end(); ++i) { snprintf (buf, sizeof(buf), "%s-%u.mid", legalized.c_str(), cnt); possible_name = buf; possible_path = Glib::build_filename (*i, possible_name); - + if (Glib::file_test (possible_path, Glib::FILE_TEST_EXISTS)) { existing++; } @@ -4734,7 +4624,7 @@ boost::shared_ptr Session::create_midi_source_for_session (string const & basic_name) { const string path = new_midi_source_path (basic_name); - + if (!path.empty()) { return boost::dynamic_pointer_cast ( SourceFactory::createWritable ( @@ -4749,24 +4639,24 @@ boost::shared_ptr Session::create_midi_source_by_stealing_name (boost::shared_ptr track) { /* the caller passes in the track the source will be used in, - so that we can keep the numbering sane. - + so that we can keep the numbering sane. + Rationale: a track with the name "Foo" that has had N captures carried out so far will ALREADY have a write source named "Foo-N+1.mid" waiting to be used for the next capture. - + If we call new_midi_source_name() we will get "Foo-N+2". But there is no region corresponding to "Foo-N+1", so when "Foo-N+2" appears in the track, the gap presents the user with odd behaviour - why did it skip past Foo-N+1? - + We could explain this to the user in some odd way, but instead we rename "Foo-N+1.mid" as "Foo-N+2.mid", and then use "Foo-N+1" here. - + If that attempted rename fails, we get "Foo-N+2.mid" anyway. */ - + boost::shared_ptr mt = boost::dynamic_pointer_cast (track); assert (mt); std::string name = track->steal_write_source_name (); @@ -4932,7 +4822,7 @@ Session::available_capture_duration () if (_total_free_4k_blocks_uncertain) { return boost::optional (); } - + float sample_bytes_on_disk = 4.0; // keep gcc happy switch (config.get_native_file_data_format()) { @@ -5283,7 +5173,7 @@ Session::freeze_all (InterThreadInfo& itt) boost::shared_ptr Session::write_one_track (Track& track, framepos_t start, framepos_t end, bool /*overwrite*/, vector >& srcs, - InterThreadInfo& itt, + InterThreadInfo& itt, boost::shared_ptr endpoint, bool include_endpoint, bool for_export, bool for_freeze) { @@ -5344,7 +5234,7 @@ Session::write_one_track (Track& track, framepos_t start, framepos_t end, string path = ((track.data_type() == DataType::AUDIO) ? new_audio_source_path (legal_playlist_name, diskstream_channels.n_audio(), chan_n, false, true) : new_midi_source_path (legal_playlist_name)); - + if (path.empty()) { goto out; } @@ -5643,15 +5533,15 @@ Session::update_route_record_state () break; } } - + g_atomic_int_set (&_have_rec_disabled_track, i != rl->end () ? 1 : 0); bool record_arm_state_changed = (old != g_atomic_int_get (&_have_rec_enabled_track) ); - + if (record_status() == Recording && record_arm_state_changed ) { RecordArmStateChanged (); } - + } void @@ -6165,7 +6055,7 @@ Session::update_latency_compensation (bool force_whole_graph) (some_track_latency_changed ? "yes" : "no"))); DEBUG_TRACE(DEBUG::Latency, "---------------------------- DONE update latency compensation\n\n"); - + if (some_track_latency_changed || force_whole_graph) { _engine.update_latencies (); } @@ -6194,7 +6084,7 @@ Session::session_name_is_legal (const string& path) return 0; } -uint32_t +uint32_t Session::next_control_id () const { int subtract = 0; @@ -6238,7 +6128,7 @@ Session::notify_remote_id_change () */ reconnect_existing_routes(true, true); #endif - + } void @@ -6349,5 +6239,5 @@ Session::clear_object_selection () _object_selection = Evoral::Range (-1,-1); #ifdef USE_TRACKS_CODE_FEATURES follow_playhead_priority (); -#endif +#endif }