X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=7706aeb37af609fd9ed5c08c215567cb5c9c788a;hb=b8f29872a7284ccefde1d14806247e4ee0fe3e20;hp=1c6b08b95d81adb047b58d327a09299e4148614e;hpb=48fdb2815340ae1f35bbe30de00a2b206102166f;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 1c6b08b95d..7706aeb37a 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -46,6 +46,7 @@ #include "pbd/search_path.h" #include "pbd/stacktrace.h" #include "pbd/stl_delete.h" +#include "pbd/replace_all.h" #include "pbd/unwind.h" #include "ardour/amp.h" @@ -66,6 +67,10 @@ #include "ardour/control_protocol_manager.h" #include "ardour/data_type.h" #include "ardour/debug.h" +#include "ardour/directory_names.h" +#ifdef USE_TRACKS_CODE_FEATURES +#include "ardour/engine_state_controller.h" +#endif #include "ardour/filename_extensions.h" #include "ardour/graph.h" #include "ardour/midiport_manager.h" @@ -77,6 +82,7 @@ #include "ardour/plugin.h" #include "ardour/plugin_insert.h" #include "ardour/process_thread.h" +#include "ardour/profile.h" #include "ardour/rc_configuration.h" #include "ardour/recent_sessions.h" #include "ardour/region.h" @@ -91,7 +97,9 @@ #include "ardour/smf_source.h" #include "ardour/source_factory.h" #include "ardour/speakers.h" +#include "ardour/tempo.h" #include "ardour/track.h" +#include "ardour/user_bundle.h" #include "ardour/utils.h" #include "midi++/port.h" @@ -170,6 +178,7 @@ Session::Session (AudioEngine &eng, , _writable (false) , _was_seamless (Config->get_seamless_loop ()) , _under_nsm_control (false) + , _xrun_count (0) , delta_accumulator_cnt (0) , average_slave_delta (1800) // !!! why 1800 ??? , average_dir (0) @@ -209,6 +218,9 @@ Session::Session (AudioEngine &eng, , cumulative_rf_motion (0) , rf_scale (1.0) , _locations (new Locations (*this)) + , _ignore_skips_updates (false) + , _rt_thread_active (false) + , _rt_emit_pending (false) , step_speed (0) , outbound_mtc_timecode_frame (0) , next_quarter_frame_to_send (-1) @@ -236,6 +248,8 @@ Session::Session (AudioEngine &eng, , _all_route_group (new RouteGroup (*this, "all")) , routes (new RouteList) , _adding_routes_in_progress (false) + , _reconnecting_routes_in_progress (false) + , _route_deletion_in_progress (false) , destructive_index (0) , _track_number_decimals(1) , solo_update_disabled (false) @@ -261,10 +275,11 @@ Session::Session (AudioEngine &eng, , first_file_header_format_reset (true) , have_looped (false) , _have_rec_enabled_track (false) + , _have_rec_disabled_track (true) , _step_editors (0) , _suspend_timecode_transmission (0) , _speakers (new Speakers) - , _order_hint (0) + , _order_hint (-1) , ignore_route_processor_changes (false) , _scene_changer (0) , _midi_ports (0) @@ -272,6 +287,9 @@ Session::Session (AudioEngine &eng, { uint32_t sr = 0; + pthread_mutex_init (&_rt_emit_mutex, 0); + pthread_cond_init (&_rt_emit_cond, 0); + pre_engine_init (fullpath); if (_is_new) { @@ -296,8 +314,11 @@ Session::Session (AudioEngine &eng, * of a template. */ - if (!mix_template.empty() && load_state (_current_snapshot_name)) { - throw failed_constructor (); + if (!mix_template.empty()) { + if (load_state (_current_snapshot_name)) { + throw failed_constructor (); + } + store_recent_templates (mix_template); } /* load default session properties - if any */ @@ -350,6 +371,8 @@ Session::Session (AudioEngine &eng, _is_new = false; + emit_thread_start (); + /* hook us up to the engine since we are now completely constructed */ BootMessage (_("Connect to engine")); @@ -357,8 +380,52 @@ Session::Session (AudioEngine &eng, _engine.set_session (this); _engine.reset_timebase (); - BootMessage (_("Session loading complete")); +#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 + 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 (); + } + } + } +#endif + + _is_new = false; + session_loaded (); + BootMessage (_("Session loading complete")); } Session::~Session () @@ -441,6 +508,10 @@ Session::immediately_post_engine () return -1; } + /* TODO, connect in different thread. (PortRegisteredOrUnregistered may be in RT context) + * can we do that? */ + _engine.PortRegisteredOrUnregistered.connect_same_thread (*this, boost::bind (&Session::setup_bundles, this)); + return 0; } @@ -463,6 +534,10 @@ Session::destroy () _engine.remove_session (); +#ifdef USE_TRACKS_CODE_FEATURES + EngineStateController::instance()->remove_session(); +#endif + /* deregister all ports - there will be no process or any other * callbacks from the engine any more. */ @@ -563,6 +638,11 @@ Session::destroy () /* not strictly necessary, but doing it here allows the shared_ptr debugging to work */ playlists.reset (); + emit_thread_terminate (); + + pthread_cond_destroy (&_rt_emit_cond); + pthread_mutex_destroy (&_rt_emit_mutex); + delete _scene_changer; _scene_changer = 0; delete midi_control_ui; midi_control_ui = 0; @@ -570,6 +650,8 @@ Session::destroy () delete _midi_ports; _midi_ports = 0; delete _locations; _locations = 0; + delete _tempo_map; + DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n"); #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS @@ -684,6 +766,19 @@ Session::setup_click_state (const XMLNode* node) void Session::setup_bundles () { + + { + RCUWriter writer (_bundles); + boost::shared_ptr b = writer.get_copy (); + for (BundleList::iterator i = b->begin(); i != b->end();) { + if (boost::dynamic_pointer_cast(*i)) { + ++i; + continue; + } + i = b->erase(i); + } + } + vector inputs[DataType::num_types]; vector outputs[DataType::num_types]; for (uint32_t i = 0; i < DataType::num_types; ++i) { @@ -703,13 +798,18 @@ Session::setup_bundles () for (uint32_t np = 0; np < outputs[DataType::AUDIO].size(); ++np) { char buf[32]; - snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1); + std::string pn = _engine.get_pretty_name_by_name (outputs[DataType::AUDIO][np]); + if (!pn.empty()) { + snprintf (buf, sizeof (buf), _("out %s"), pn.substr(0,12).c_str()); + } else { + snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1); + } boost::shared_ptr c (new Bundle (buf, true)); c->add_channel (_("mono"), DataType::AUDIO); c->set_port (0, outputs[DataType::AUDIO][np]); - add_bundle (c); + add_bundle (c, false); } /* stereo output bundles */ @@ -724,7 +824,7 @@ Session::setup_bundles () c->add_channel (_("R"), DataType::AUDIO); c->set_port (1, outputs[DataType::AUDIO][np + 1]); - add_bundle (c); + add_bundle (c, false); } } @@ -732,13 +832,18 @@ Session::setup_bundles () for (uint32_t np = 0; np < inputs[DataType::AUDIO].size(); ++np) { char buf[32]; - snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1); + std::string pn = _engine.get_pretty_name_by_name (inputs[DataType::AUDIO][np]); + if (!pn.empty()) { + snprintf (buf, sizeof (buf), _("in %s"), pn.substr(0,12).c_str()); + } else { + snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1); + } boost::shared_ptr c (new Bundle (buf, false)); c->add_channel (_("mono"), DataType::AUDIO); c->set_port (0, inputs[DataType::AUDIO][np]); - add_bundle (c); + add_bundle (c, false); } /* stereo input bundles */ @@ -754,7 +859,7 @@ Session::setup_bundles () c->add_channel (_("R"), DataType::AUDIO); c->set_port (1, inputs[DataType::AUDIO][np + 1]); - add_bundle (c); + add_bundle (c, false); } } @@ -762,26 +867,36 @@ Session::setup_bundles () for (uint32_t np = 0; np < inputs[DataType::MIDI].size(); ++np) { string n = inputs[DataType::MIDI][np]; - boost::erase_first (n, X_("alsa_pcm:")); - + std::string pn = _engine.get_pretty_name_by_name (n); + if (!pn.empty()) { + n = pn; + } else { + boost::erase_first (n, X_("alsa_pcm:")); + } boost::shared_ptr c (new Bundle (n, false)); c->add_channel ("", DataType::MIDI); c->set_port (0, inputs[DataType::MIDI][np]); - add_bundle (c); + add_bundle (c, false); } /* MIDI output bundles */ for (uint32_t np = 0; np < outputs[DataType::MIDI].size(); ++np) { string n = outputs[DataType::MIDI][np]; - boost::erase_first (n, X_("alsa_pcm:")); - + std::string pn = _engine.get_pretty_name_by_name (n); + if (!pn.empty()) { + n = pn; + } else { + boost::erase_first (n, X_("alsa_pcm:")); + } boost::shared_ptr c (new Bundle (n, true)); c->add_channel ("", DataType::MIDI); c->set_port (0, outputs[DataType::MIDI][np]); - add_bundle (c); + add_bundle (c, false); } + // we trust the backend to only calls us if there's a change + BundleAddedOrRemoved (); /* EMIT SIGNAL */ } void @@ -790,7 +905,13 @@ 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) ) { + return; + } + /* if requested auto-connect the outputs to the first N physical ports. */ @@ -821,7 +942,7 @@ Session::auto_connect_master_bus () void Session::remove_monitor_section () { - if (!_monitor_out) { + if (!_monitor_out || Profile->get_trx()) { return; } @@ -875,7 +996,7 @@ Session::add_monitor_section () { RouteList rl; - if (_monitor_out || !_master_out) { + if (_monitor_out || !_master_out || Profile->get_trx()) { return; } @@ -1018,6 +1139,121 @@ Session::add_monitor_section () } } +void +Session::reset_monitor_section () +{ + /* Process lock should be held by the caller.*/ + + if (!_monitor_out || Profile->get_trx()) { + return; + } + + uint32_t limit = _master_out->n_outputs().n_audio(); + + /* 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); + _monitor_out->output()->disconnect (this); + + _monitor_out->input()->ensure_io (_master_out->output()->n_ports(), false, this); + _monitor_out->output()->ensure_io (_master_out->output()->n_ports(), false, this); + + for (uint32_t n = 0; n < limit; ++n) { + boost::shared_ptr p = _monitor_out->input()->ports().nth_audio_port (n); + boost::shared_ptr o = _master_out->output()->ports().nth_audio_port (n); + + if (o) { + string connect_to = o->name(); + if (_monitor_out->input()->connect (p, connect_to, this)) { + error << string_compose (_("cannot connect control input %1 to %2"), n, connect_to) + << endmsg; + break; + } + } + } + + /* connect monitor section to physical outs + */ + + if (Config->get_auto_connect_standard_busses()) { + + 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 { + 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 */ + + 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]); + } + + 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 ( + _("cannot connect control output %1 to %2"), + n, connect_to) + << endmsg; + break; + } + } + } + } + } + } + + /* 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 rls = routes.reader (); + + PBD::Unwinder uw (ignore_route_processor_changes, true); + + for (RouteList::iterator x = rls->begin(); x != rls->end(); ++x) { + + if ((*x)->is_monitor()) { + /* relax */ + } else if ((*x)->is_master()) { + /* relax */ + } else { + (*x)->enable_monitor_send (); + } + } +} + void Session::hookup_io () { @@ -1175,10 +1411,10 @@ Session::auto_loop_changed (Location* location) framepos_t dcp; framecnt_t dcl; auto_loop_declick_range (location, dcp, dcl); - replace_event (SessionEvent::AutoLoopDeclick, dcp, dcl); if (transport_rolling() && play_loop) { + replace_event (SessionEvent::AutoLoopDeclick, dcp, dcl); // if (_transport_frame > location->end()) { @@ -1202,6 +1438,9 @@ Session::auto_loop_changed (Location* location) } } + } else { + clear_events (SessionEvent::AutoLoopDeclick); + clear_events (SessionEvent::AutoLoop); } last_loopend = location->end(); @@ -1298,9 +1537,21 @@ Session::set_auto_loop_location (Location* location) location->StartChanged.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, location)); location->EndChanged.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, location)); location->Changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, location)); + location->FlagsChanged.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, location)); location->set_auto_loop (true, this); + if (Config->get_loop_is_mode() && play_loop && Config->get_seamless_loop()) { + // set all tracks to use internal looping + boost::shared_ptr rl = routes.reader (); + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + if (tr && !tr->hidden()) { + tr->set_loop (location); + } + } + } + /* take care of our stuff first */ auto_loop_changed (location); @@ -1325,11 +1576,16 @@ Session::update_marks (Location*) void Session::update_skips (Location* loc, bool consolidate) { + if (_ignore_skips_updates) { + return; + } + Locations::LocationList skips; - if (consolidate) { - consolidate_skips (loc); - } + if (consolidate) { + PBD::Unwinder uw (_ignore_skips_updates, true); + consolidate_skips (loc); + } sync_locations_to_skips (); @@ -1422,6 +1678,15 @@ Session::location_added (Location *location) _session_range_location = location; } + if (location->is_mark()) { + /* listen for per-location signals that require us to do any * global updates for marks */ + + location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location)); + location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location)); + location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location)); + location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location)); + } + if (location->is_skip()) { /* listen for per-location signals that require us to update skip-locate events */ @@ -1432,7 +1697,7 @@ Session::location_added (Location *location) update_skips (location, true); } - + set_dirty (); } @@ -1470,20 +1735,15 @@ 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. - */ + 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); + for (Locations::LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) { + location_added (*i); + } } void @@ -2002,6 +2262,8 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost: new_routes.push_back (track); ret.push_back (track); + + RouteAddedOrRemoved (true); /* EMIT SIGNAL */ } catch (failed_constructor &err) { @@ -2021,7 +2283,11 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost: failed: if (!new_routes.empty()) { StateProtector sp (this); - add_routes (new_routes, true, true, true); + if (Profile->get_trx()) { + add_routes (new_routes, false, false, false); + } else { + add_routes (new_routes, true, true, false); + } if (instrument) { for (RouteList::iterator r = new_routes.begin(); r != new_routes.end(); ++r) { @@ -2151,6 +2417,17 @@ Session::auto_connect_route (boost::shared_ptr route, ChanCount& existing for (uint32_t i = output_start.get(*t); i < route->n_outputs().get(*t); ++i) { string port; + /* Waves Tracks: + * do not create new connections if we reached the limit of physical outputs + * in Multi Out mode + */ + + if (!(Config->get_output_auto_connect() & AutoConnectMaster) && + ARDOUR::Profile->get_trx () && + existing_outputs.get(*t) == nphysical_out ) { + break; + } + if ((*t) == DataType::MIDI && (Config->get_output_auto_connect() & AutoConnectPhysical)) { port = physoutputs[(out_offset.get(*t) + i) % nphysical_out]; } else if ((*t) == DataType::AUDIO && (Config->get_output_auto_connect() & AutoConnectMaster)) { @@ -2176,13 +2453,288 @@ Session::auto_connect_route (boost::shared_ptr route, ChanCount& existing } } +#ifdef USE_TRACKS_CODE_FEATURES + +static bool +compare_routes_by_remote_id (const boost::shared_ptr& route1, const boost::shared_ptr& route2) +{ + return route1->remote_control_id() < route2->remote_control_id(); +} + void Session::reconnect_existing_routes (bool withLock, bool reconnect_master, bool reconnect_inputs, bool reconnect_outputs) { - /* TRX does stuff here, ardour does not (but probably should). This is called after an engine reset (in particular). - */ + // it is not allowed to perform connection + 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(); + const AutoConnectOption current_input_auto_connection (Config->get_input_auto_connect()); + const AutoConnectOption current_output_auto_connection (Config->get_output_auto_connect()); + for (; rIter != existing_routes.end(); ++rIter) { + if (*rIter == _master_out || *rIter == _monitor_out ) { + continue; + } + + if (current_output_auto_connection == AutoConnectPhysical) { + (*rIter)->amp()->deactivate(); + } 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 ); + } else { + error << error << "Master bus is not available" << endmsg; + 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 */ +} + +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); + } + } + } + + } 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); + } + } + } + } +} + +void +Session::reconnect_mtc_ports() +{ +#if 0 + boost::shared_ptr mtc_in_ptr = _midi_ports->mtc_input_port(); + + if (mtc_in_ptr) { + 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 + } + } +#endif +} + +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); + } + } + } } +#endif /** Caller must not hold process lock * @param name_template string to use for the start of the name, or "" to use "Audio". @@ -2197,10 +2749,19 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod RouteList new_routes; list > ret; - bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("Audio"); + string name_pattern; + if (Profile->get_trx() ) { + name_pattern = "Track "; + } else { + name_pattern = "Audio "; + } + + bool const use_number = (how_many != 1) || name_template.empty () || name_template == _(name_pattern.c_str() ); + while (how_many) { - if (!find_route_name (name_template.empty() ? _("Audio") : name_template, ++track_id, track_name, sizeof(track_name), use_number)) { + + if (!find_route_name (name_template.empty() ? _(name_pattern.c_str()) : name_template, ++track_id, track_name, sizeof(track_name), use_number)) { error << "cannot find name for new audio track" << endmsg; goto failed; } @@ -2214,6 +2775,20 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod goto failed; } + if (ARDOUR::Profile->get_trx ()) { + // TRACKS considers it's not a USE CASE, it's + // a piece of behavior of the session model: + // + // Gain for a newly created route depends on + // the current output_auto_connect mode: + // + // 0 for Stereo Out mode + // 0 Multi Out mode + if (Config->get_output_auto_connect() & AutoConnectMaster) { + track->set_gain (dB_to_coefficient (0), 0); + } + } + track->use_new_diskstream(); #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS @@ -2252,6 +2827,8 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod new_routes.push_back (track); ret.push_back (track); + + RouteAddedOrRemoved (true); /* EMIT SIGNAL */ } catch (failed_constructor &err) { @@ -2271,7 +2848,11 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod failed: if (!new_routes.empty()) { StateProtector sp (this); - add_routes (new_routes, true, true, true); + if (Profile->get_trx()) { + add_routes (new_routes, false, false, false); + } else { + add_routes (new_routes, true, true, false); + } } return ret; @@ -2336,6 +2917,8 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r ret.push_back (bus); + RouteAddedOrRemoved (true); /* EMIT SIGNAL */ + ARDOUR::GUIIdle (); } @@ -2357,7 +2940,11 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r failure: if (!ret.empty()) { StateProtector sp (this); - add_routes (ret, false, true, true); // autoconnect outputs only + if (Profile->get_trx()) { + add_routes (ret, false, false, false); + } else { + add_routes (ret, false, true, true); // autoconnect // outputs only + } } return ret; @@ -2456,6 +3043,8 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template ++control_id; ret.push_back (route); + + RouteAddedOrRemoved (true); /* EMIT SIGNAL */ } catch (failed_constructor &err) { @@ -2474,7 +3063,11 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template out: if (!ret.empty()) { StateProtector sp (this); - add_routes (ret, true, true, true); + if (Profile->get_trx()) { + add_routes (ret, false, false, false); + } else { + add_routes (ret, true, true, false); + } IO::enable_connecting (); } @@ -2505,6 +3098,8 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output reassign_track_numbers(); + update_route_record_state (); + RouteAdded (new_routes); /* EMIT SIGNAL */ } @@ -2515,9 +3110,9 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool ChanCount existing_outputs; uint32_t order = next_control_id(); - if (_order_hint != 0) { + if (_order_hint > -1) { order = _order_hint; - _order_hint = 0; + _order_hint = -1; } count_existing_track_channels (existing_inputs, existing_outputs); @@ -2562,7 +3157,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool if (tr) { tr->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::track_playlist_changed, this, boost::weak_ptr (tr))); track_playlist_changed (boost::weak_ptr (tr)); - tr->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_have_rec_enabled_track, this)); + tr->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this)); boost::shared_ptr mt = boost::dynamic_pointer_cast (tr); if (mt) { @@ -2618,7 +3213,7 @@ Session::globally_set_send_gains_to_zero (boost::shared_ptr dest) for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((s = (*i)->internal_send_for (dest)) != 0) { - s->amp()->gain_control()->set_value (0.0); + s->amp()->gain_control()->set_value (GAIN_COEFF_ZERO); } } } @@ -2631,7 +3226,7 @@ Session::globally_set_send_gains_to_unity (boost::shared_ptr dest) for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((s = (*i)->internal_send_for (dest)) != 0) { - s->amp()->gain_control()->set_value (1.0); + s->amp()->gain_control()->set_value (GAIN_COEFF_UNITY); } } } @@ -2696,100 +3291,128 @@ Session::add_internal_send (boost::shared_ptr dest, boost::shared_ptr route) +Session::remove_routes (boost::shared_ptr routes_to_remove) { - if (route == _master_out) { - return; - } - - route->set_solo (false, this); - - { + { // RCU Writer scope RCUWriter writer (routes); boost::shared_ptr rs = writer.get_copy (); - - rs->remove (route); - - /* deleting the master out seems like a dumb - idea, but its more of a UI policy issue - than our concern. - */ - - if (route == _master_out) { - _master_out = boost::shared_ptr (); - } - - if (route == _monitor_out) { - _monitor_out.reset (); - } - - /* writer goes out of scope, forces route list update */ - } - - update_route_solo_state (); - - // We need to disconnect the route's inputs and outputs - - route->input()->disconnect (0); - route->output()->disconnect (0); - - /* if the route had internal sends sending to it, remove them */ - if (route->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 (route); - if (s) { - (*i)->remove_processor (s); + + + 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 (); } - } - } - /* if the monitoring section had a pointer to this route, remove it */ - if (_monitor_out && !route->is_master() && !route->is_monitor()) { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - PBD::Unwinder uw (ignore_route_processor_changes, true); - route->remove_aux_or_listen (_monitor_out); - } + 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); + if (s) { + (*i)->remove_processor (s); + } + } + } + + /* 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--; + } + } - boost::shared_ptr mt = boost::dynamic_pointer_cast (route); - 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_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 */ - resort_routes (); +#ifdef USE_TRACKS_CODE_FEATURES + reconnect_existing_routes(true, false); +#else + 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); - /* try to cause everyone to drop their references */ - - route->drop_references (); - + 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); } + reassign_track_numbers(); + update_route_record_state (); +} + +void +Session::remove_route (boost::shared_ptr route) +{ + boost::shared_ptr rl (new RouteList); + rl->push_back (route); + remove_routes (rl); } void @@ -2983,6 +3606,7 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) { DEBUG_TRACE (DEBUG::Solo, string_compose ("mute change for %1, which neither feeds or is fed by %2\n", (*i)->name(), route->name())); + (*i)->act_on_mute (); (*i)->mute_changed (this); } @@ -3574,6 +4198,41 @@ Session::count_sources_by_origin (const string& path) string Session::peak_path (string base) const { + if (Glib::path_is_absolute (base)) { + + /* 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) { + + session_path = Glib::path_get_dirname (base); /* 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 */ + + /* see if it is within our session */ + + for (vector::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) { + if (i->path == session_path) { + in_another_session = false; + break; + } + } + } else { + in_another_session = false; + } + + + if (in_another_session) { + SessionDirectory sd (session_path); + return Glib::build_filename (sd.peak_path(), Glib::path_get_basename (base) + peakfile_suffix); + } + } + + base = Glib::path_get_basename (base); return Glib::build_filename (_session_dir->peak_path(), base + peakfile_suffix); } @@ -3637,8 +4296,19 @@ 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 false otherwise. + * + * 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. + */ bool -Session::audio_source_name_is_unique (const string& name, uint32_t chan) +Session::audio_source_name_is_unique (const string& name) { std::vector sdirs = source_search_path (DataType::AUDIO); vector::iterator i; @@ -3671,7 +4341,7 @@ Session::audio_source_name_is_unique (const string& name, uint32_t chan) string possible_path = Glib::build_filename (spath, name); - if (audio_source_by_path_and_channel (possible_path, chan)) { + if (audio_source_by_path_and_channel (possible_path, 0)) { existing++; break; } @@ -3739,7 +4409,7 @@ Session::new_audio_source_path (const string& base, uint32_t nchan, uint32_t cha 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, chan)) { + if (audio_source_name_is_unique (possible_name)) { break; } @@ -4016,7 +4686,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) { + if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _reconnecting_routes_in_progress) { return; } @@ -4086,7 +4756,7 @@ Session::available_capture_duration () } void -Session::add_bundle (boost::shared_ptr bundle) +Session::add_bundle (boost::shared_ptr bundle, bool emit_signal) { { RCUWriter writer (_bundles); @@ -4094,7 +4764,9 @@ Session::add_bundle (boost::shared_ptr bundle) b->push_back (bundle); } - BundleAdded (bundle); /* EMIT SIGNAL */ + if (emit_signal) { + BundleAddedOrRemoved (); /* EMIT SIGNAL */ + } set_dirty(); } @@ -4116,7 +4788,7 @@ Session::remove_bundle (boost::shared_ptr bundle) } if (removed) { - BundleRemoved (bundle); /* EMIT SIGNAL */ + BundleAddedOrRemoved (); /* EMIT SIGNAL */ } set_dirty(); @@ -4639,6 +5311,12 @@ Session::gain_automation_buffer() const return ProcessThread::gain_automation_buffer (); } +gain_t* +Session::trim_automation_buffer() const +{ + return ProcessThread::trim_automation_buffer (); +} + gain_t* Session::send_gain_automation_buffer() const { @@ -4719,9 +5397,15 @@ Session::have_rec_enabled_track () const return g_atomic_int_get (const_cast(&_have_rec_enabled_track)) == 1; } +bool +Session::have_rec_disabled_track () const +{ + return g_atomic_int_get (const_cast(&_have_rec_disabled_track)) == 1; +} + /** Update the state of our rec-enabled tracks flag */ void -Session::update_have_rec_enabled_track () +Session::update_route_record_state () { boost::shared_ptr rl = routes.reader (); RouteList::iterator i = rl->begin(); @@ -4742,6 +5426,22 @@ Session::update_have_rec_enabled_track () if (g_atomic_int_get (&_have_rec_enabled_track) != old) { RecordStateChanged (); /* EMIT SIGNAL */ } + + for (i = rl->begin(); i != rl->end (); ++i) { + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + if (tr && !tr->record_enabled ()) { + 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 @@ -4784,7 +5484,8 @@ Session::route_added_to_route_group (RouteGroup* rg, boost::weak_ptr r) void Session::route_removed_from_route_group (RouteGroup* rg, boost::weak_ptr r) { - RouteRemovedFromRouteGroup (rg, r); + update_route_record_state (); + RouteRemovedFromRouteGroup (rg, r); /* EMIT SIGNAL */ } boost::shared_ptr @@ -5314,6 +6015,14 @@ Session::notify_remote_id_change () default: break; } + +#ifdef USE_TRACKS_CODE_FEATURES + /* Waves Tracks: for Waves Tracks session it's required to reconnect their IOs + * if track order has been changed by user + */ + reconnect_existing_routes(true, true); +#endif + } void