X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=eb333f8e0de33499dd722e29f4f63f0e69c142a0;hb=c4a3291b80ecdeba23df206d454238e7e3814fe6;hp=fae67b164b4689d0656fb65f4d7e20fcdcf5976d;hpb=531e71b4859787805024151dc8659118b0a9e868;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index fae67b164b..eb333f8e0d 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -107,6 +107,8 @@ #include "i18n.h" +#include + namespace ARDOUR { class MidiSource; class Processor; @@ -209,7 +211,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)) @@ -262,7 +264,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) @@ -313,12 +314,12 @@ Session::Session (AudioEngine &eng, #endif if (ensure_engine (sr)) { destroy (); - throw failed_constructor (); + throw SessionException (_("Cannot connect to audio/midi engine")); } if (create (mix_template, bus_profile)) { destroy (); - throw failed_constructor (); + throw SessionException (_("Session initialization failed")); } /* if a mix template was provided, then ::create() will @@ -332,9 +333,9 @@ Session::Session (AudioEngine &eng, * of a template. */ - if (!mix_template.empty()) { + if (!mix_template.empty()) { if (load_state (_current_snapshot_name)) { - throw failed_constructor (); + throw SessionException (_("Failed to load template/snapshot state")); } store_recent_templates (mix_template); } @@ -345,7 +346,7 @@ Session::Session (AudioEngine &eng, } else { if (load_state (_current_snapshot_name)) { - throw failed_constructor (); + throw SessionException (_("Failed to load state")); } /* try to get sample rate from XML state so that we @@ -362,13 +363,13 @@ Session::Session (AudioEngine &eng, if (ensure_engine (sr)) { destroy (); - throw failed_constructor (); + throw SessionException (_("Cannot connect to audio/midi engine")); } } if (post_engine_init ()) { destroy (); - throw failed_constructor (); + throw SessionException (_("Cannot configure audio/midi engine with session parameters")); } store_recent_sessions (_name, _path); @@ -416,7 +417,7 @@ Session::Session (AudioEngine &eng, 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) { @@ -426,7 +427,7 @@ Session::Session (AudioEngine &eng, 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()); } @@ -492,7 +493,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. @@ -549,6 +550,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 @@ -1026,10 +1033,13 @@ Session::add_monitor_section () #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS // boost_debug_shared_ptr_mark_interesting (r.get(), "Route"); #endif - { + try { Glib::Threads::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); + } catch (...) { + error << _("Cannot create monitor section. 'Monitor' Port name is not unique.") << endmsg; + return; } rl.push_back (r); @@ -1127,7 +1137,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 @@ -1581,7 +1591,7 @@ Session::set_auto_loop_location (Location* location) } } } - + /* take care of our stuff first */ auto_loop_changed (location); @@ -1612,7 +1622,7 @@ Session::update_skips (Location* loc, bool consolidate) } sync_locations_to_skips (); - + set_dirty (); } @@ -1634,7 +1644,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: @@ -1694,7 +1704,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. @@ -1721,7 +1731,7 @@ Session::location_added (Location *location) update_skips (location, true); } - + set_dirty (); } @@ -1732,7 +1742,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); } @@ -1743,7 +1753,7 @@ Session::location_removed (Location *location) } if (location->is_skip()) { - + update_skips (location, false); } @@ -1759,7 +1769,7 @@ 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. @@ -1812,7 +1822,7 @@ Session::disable_record (bool rt_context, bool force) if ((rs = (RecordState) g_atomic_int_get (&_record_status)) != Disabled) { - if ((!Config->get_latched_record_enable () && !play_loop) || force) { + if (!Config->get_latched_record_enable () || force) { g_atomic_int_set (&_record_status, Disabled); send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit)); } else { @@ -1863,11 +1873,6 @@ Session::maybe_enable_record () save_state ("", true); - if (Config->get_loop_is_mode()) { - /* makes no sense to use loop play as mode when recording */ - request_play_loop (false); - } - if (_transport_speed) { if (!config.get_punch_in()) { enable_record (); @@ -2109,7 +2114,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 */ @@ -2196,6 +2201,21 @@ Session::resort_routes_using (boost::shared_ptr r) bool Session::find_route_name (string const & base, uint32_t& id, string& name, bool definitely_add_number) { + /* the base may conflict with ports that do not belong to existing + 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; + if (id < 1) { + id = 1; + } + break; + } + } + if (!definitely_add_number && route_by_name (base) == 0) { /* juse use the base */ name = base; @@ -2258,7 +2278,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; @@ -2508,7 +2528,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) @@ -2523,42 +2543,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(); @@ -2574,52 +2594,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 ); @@ -2628,54 +2648,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 */ } @@ -2683,16 +2703,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); @@ -2701,17 +2721,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); @@ -2759,30 +2779,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); } @@ -2796,7 +2816,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; @@ -3148,7 +3168,7 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output reassign_track_numbers(); update_route_record_state (); - + RouteAdded (new_routes); /* EMIT SIGNAL */ } @@ -3344,44 +3364,44 @@ 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 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 (); } - update_route_solo_state (); - // 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); @@ -3390,31 +3410,31 @@ 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) { _step_editors--; } } - - RouteAddedOrRemoved (false); /* EMIT SIGNAL */ } - + /* 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 @@ -3426,29 +3446,28 @@ 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 */ - PBD::Unwinder uw_flag (_route_deletion_in_progress, true); 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); } @@ -3483,10 +3502,12 @@ Session::route_listen_changed (void* /*src*/, boost::weak_ptr wpr) 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()); 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); @@ -3509,7 +3530,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; } @@ -3532,120 +3553,6 @@ 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) { @@ -3656,12 +3563,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); @@ -3692,8 +3593,6 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p 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())); @@ -3759,7 +3658,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); @@ -3844,6 +3742,12 @@ Session::io_name_is_legal (const std::string& name) { boost::shared_ptr r = routes.reader (); + for (vector::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) { + if (name == *reserved) { + return false; + } + } + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->name() == name) { return false; @@ -4361,20 +4265,31 @@ Session::count_sources_by_origin (const string& path) return cnt; } +static string +peak_file_helper (const string& peak_path, const string& file_path, const string& file_base, bool hash) { + if (hash) { + std::string checksum = Glib::Checksum::compute_checksum(Glib::Checksum::CHECKSUM_SHA1, file_path + G_DIR_SEPARATOR + file_base); + return Glib::build_filename (peak_path, checksum + peakfile_suffix); + } else { + return Glib::build_filename (peak_path, file_base + peakfile_suffix); + } +} + string -Session::peak_path (string base) const +Session::construct_peak_filepath (const string& filepath, const bool in_session, const bool old_peak_name) const { - if (Glib::path_is_absolute (base)) { + string interchange_dir_string = string (interchange_dir_name) + G_DIR_SEPARATOR; + + if (Glib::path_is_absolute (filepath)) { /* rip the session dir from the audiofile source */ string session_path; - string interchange_dir_string = string (interchange_dir_name) + G_DIR_SEPARATOR; bool in_another_session = true; - if (base.find (interchange_dir_string) != string::npos) { + if (filepath.find (interchange_dir_string) != string::npos) { - session_path = Glib::path_get_dirname (base); /* now ends in audiofiles */ + 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 */ session_path = Glib::path_get_dirname (session_path); /* now has session path */ @@ -4394,31 +4309,44 @@ Session::peak_path (string base) const if (in_another_session) { SessionDirectory sd (session_path); - return Glib::build_filename (sd.peak_path(), Glib::path_get_basename (base) + peakfile_suffix); + return peak_file_helper (sd.peak_path(), "", Glib::path_get_basename (filepath), !old_peak_name); } } - base = Glib::path_get_basename (base); - return Glib::build_filename (_session_dir->peak_path(), base + peakfile_suffix); + /* 1) if file belongs to this session + * it may be a relative path (interchange/...) + * or just basename (session_state, remove source) + * -> just use the basename + */ + std::string filename = Glib::path_get_basename (filepath); + std::string path; + + /* 2) if the file is outside our session dir: + * (imported but not copied) add the path for check-summming */ + 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 @@ -4462,14 +4390,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. */ @@ -4679,7 +4607,7 @@ Session::create_audio_source_for_session (size_t n_chans, string const & base, u if (!path.empty()) { return boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate())); + SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate(), true, true)); } else { throw failed_constructor (); } @@ -4705,21 +4633,21 @@ 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. */ @@ -4852,7 +4780,7 @@ Session::graph_reordered () from a set_state() call or creating new tracks. Ditto for deletion. */ - if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _reconnecting_routes_in_progress) { + if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _reconnecting_routes_in_progress || _route_deletion_in_progress) { return; } @@ -5239,7 +5167,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) { @@ -5599,11 +5527,11 @@ 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 (); } @@ -6150,7 +6078,7 @@ Session::session_name_is_legal (const string& path) return 0; } -uint32_t +uint32_t Session::next_control_id () const { int subtract = 0;