diff options
Diffstat (limited to 'libs/ardour/session.cc')
-rw-r--r-- | libs/ardour/session.cc | 811 |
1 files changed, 688 insertions, 123 deletions
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 71ad0bd705..9ef3633819 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" @@ -67,6 +68,9 @@ #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" @@ -78,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" @@ -134,6 +139,13 @@ const framecnt_t Session::bounce_chunk_size = 65536; static void clean_up_session_event (SessionEvent* ev) { delete ev; } const SessionEvent::RTeventCallback Session::rt_cleanup (clean_up_session_event); +// seconds should be added after the region exceeds end marker +#ifdef USE_TRACKS_CODE_FEATURES +const uint32_t Session::session_end_shift = 5; +#else +const uint32_t Session::session_end_shift = 0; +#endif + /** @param snapshot_name Snapshot name, without .ardour suffix */ Session::Session (AudioEngine &eng, const string& fullpath, @@ -179,6 +191,8 @@ Session::Session (AudioEngine &eng, , average_dir (0) , have_first_delta_accumulator (false) , _slave_state (Stopped) + , _mtc_active (false) + , _ltc_active (false) , post_export_sync (false) , post_export_position (0) , _exporting (false) @@ -243,6 +257,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) @@ -263,11 +279,14 @@ Session::Session (AudioEngine &eng, , click_emphasis_length (0) , _clicks_cleared (0) , _play_range (false) + , _range_selection (-1,-1) + , _object_selection (-1,-1) , main_outs (0) , first_file_data_format_reset (true) , 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) @@ -285,6 +304,10 @@ Session::Session (AudioEngine &eng, pre_engine_init (fullpath); if (_is_new) { + +#ifdef USE_TRACKS_CODE_FEATURES + sr = EngineStateController::instance()->get_current_sample_rate(); +#endif if (ensure_engine (sr)) { destroy (); throw failed_constructor (); @@ -306,8 +329,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 */ @@ -358,8 +384,6 @@ Session::Session (AudioEngine &eng, StartTimeChanged.connect_same_thread (*this, boost::bind (&Session::start_time_changed, this, _1)); EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1)); - _is_new = false; - emit_thread_start (); /* hook us up to the engine since we are now completely constructed */ @@ -369,8 +393,53 @@ 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<std::string> inputs; + EngineStateController::instance()->get_physical_audio_inputs(inputs); + + how_many = inputs.size(); + + list<boost::shared_ptr<AudioTrack> > tracks; + + // Track names after driver + if (Config->get_tracks_auto_naming() == NameAfterDriver) { + string track_name = ""; + for (std::vector<string>::size_type i = 0; i < inputs.size(); ++i) { + string track_name; + track_name = inputs[i]; + replace_all (track_name, "system:capture", ""); + + list<boost::shared_ptr<AudioTrack> > 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 () @@ -479,6 +548,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. */ @@ -846,7 +919,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. */ @@ -877,7 +956,7 @@ Session::auto_connect_master_bus () void Session::remove_monitor_section () { - if (!_monitor_out) { + if (!_monitor_out || Profile->get_trx()) { return; } @@ -931,7 +1010,7 @@ Session::add_monitor_section () { RouteList rl; - if (_monitor_out || !_master_out) { + if (_monitor_out || !_master_out || Profile->get_trx()) { return; } @@ -1079,7 +1158,7 @@ Session::reset_monitor_section () { /* Process lock should be held by the caller.*/ - if (!_monitor_out) { + if (!_monitor_out || Profile->get_trx()) { return; } @@ -1378,6 +1457,19 @@ Session::auto_loop_changed (Location* location) clear_events (SessionEvent::AutoLoop); } + /* possibly move playhead if not rolling; if we are rolling we'll move + to the loop start on stop if that is appropriate. + */ + + framepos_t pos; + + if (!transport_rolling() && select_playhead_priority_target (pos)) { + if (pos == location->start()) { + request_locate (pos); + } + } + + last_loopend = location->end(); set_dirty (); } @@ -1472,9 +1564,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<RouteList> rl = routes.reader (); + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i); + if (tr && !tr->hidden()) { + tr->set_loop (location); + } + } + } + /* take care of our stuff first */ auto_loop_changed (location); @@ -1485,12 +1589,6 @@ Session::set_auto_loop_location (Location* location) } void -Session::update_loop (Location*) -{ - set_dirty (); -} - -void Session::update_marks (Location*) { set_dirty (); @@ -1499,10 +1597,10 @@ Session::update_marks (Location*) void Session::update_skips (Location* loc, bool consolidate) { - if (_ignore_skips_updates) { - return; - } - + if (_ignore_skips_updates) { + return; + } + Locations::LocationList skips; if (consolidate) { @@ -1601,6 +1699,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 */ @@ -1611,7 +1718,7 @@ Session::location_added (Location *location) update_skips (location, true); } - + set_dirty (); } @@ -1654,10 +1761,15 @@ Session::_locations_changed (const Locations::LocationList& locations) 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. */ - - for (Locations::LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) { - location_added (*i); - } + + { + PBD::Unwinder<bool> 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); } void @@ -1748,6 +1860,11 @@ 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 (); @@ -1835,7 +1952,8 @@ Session::set_frame_rate (framecnt_t frames_per_second) sync_time_vars(); clear_clicks (); - + reset_write_sources (false); + // XXX we need some equivalent to this, somehow // SndFileSource::setup_standard_crossfades (frames_per_second); @@ -2073,16 +2191,16 @@ Session::resort_routes_using (boost::shared_ptr<RouteList> r) * and \a id do not reflect a free route name. */ bool -Session::find_route_name (string const & base, uint32_t& id, char* name, size_t name_len, bool definitely_add_number) +Session::find_route_name (string const & base, uint32_t& id, string& name, bool definitely_add_number) { if (!definitely_add_number && route_by_name (base) == 0) { /* juse use the base */ - snprintf (name, name_len, "%s", base.c_str()); + name = base; return true; } do { - snprintf (name, name_len, "%s %" PRIu32, base.c_str(), id); + name = string_compose ("%1 %2", base, id); if (route_by_name (name) == 0) { return true; @@ -2113,6 +2231,25 @@ Session::count_existing_track_channels (ChanCount& in, ChanCount& out) } } +string +Session::default_track_name_pattern (DataType t) +{ + switch (t) { + case DataType::AUDIO: + if (Profile->get_trx()) { + return _("Track "); + } else { + return _("Audio "); + } + break; + + case DataType::MIDI: + return _("MIDI "); + } + + return ""; +} + /** Caller must not hold process lock * @param name_template string to use for the start of the name, or "" to use "MIDI". * @param instrument plugin info for the instrument to insert pre-fader, if any @@ -2121,16 +2258,17 @@ list<boost::shared_ptr<MidiTrack> > Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost::shared_ptr<PluginInfo> instrument, TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template) { - char track_name[32]; + string track_name; uint32_t track_id = 0; string port; RouteList new_routes; list<boost::shared_ptr<MidiTrack> > ret; - bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("MIDI"); + const string name_pattern = default_track_name_pattern (DataType::MIDI); + bool const use_number = (how_many != 1) || name_template.empty () || (name_template == name_pattern); while (how_many) { - if (!find_route_name (name_template.empty() ? _("MIDI") : name_template, ++track_id, track_name, sizeof(track_name), use_number)) { + if (!find_route_name (name_template.empty() ? _("MIDI") : name_template, ++track_id, track_name, use_number)) { error << "cannot find name for new midi track" << endmsg; goto failed; } @@ -2176,6 +2314,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) { @@ -2195,7 +2335,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) { @@ -2325,6 +2469,17 @@ Session::auto_connect_route (boost::shared_ptr<Route> 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)) { @@ -2350,13 +2505,289 @@ Session::auto_connect_route (boost::shared_ptr<Route> route, ChanCount& existing } } +#ifdef USE_TRACKS_CODE_FEATURES + +static bool +compare_routes_by_remote_id (const boost::shared_ptr<Route>& route1, const boost::shared_ptr<Route>& 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<bool> protect_ignore_changes (_reconnecting_routes_in_progress, true); + + vector<string> physinputs; + vector<string> 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<MidiPort> scene_in_ptr = scene_in(); + if (scene_in_ptr) { + scene_in_ptr->disconnect_all (); + + std::vector<EngineStateController::MidiPortState> midi_port_states; + EngineStateController::instance()->get_physical_midi_input_states (midi_port_states); + + std::vector<EngineStateController::MidiPortState>::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<MidiPort> scene_out_ptr = scene_out(); + + if (scene_out_ptr ) { + scene_out_ptr->disconnect_all (); + + std::vector<EngineStateController::MidiPortState> midi_port_states; + EngineStateController::instance()->get_physical_midi_output_states (midi_port_states); + + std::vector<EngineStateController::MidiPortState>::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 () +{ + boost::shared_ptr<MidiPort> mtc_in_ptr = _midi_ports->mtc_input_port(); + + if (!mtc_in_ptr) { + return; + } + + mtc_in_ptr->disconnect_all (); + + std::vector<EngineStateController::MidiPortState> midi_port_states; + EngineStateController::instance()->get_physical_midi_input_states (midi_port_states); + + std::vector<EngineStateController::MidiPortState>::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 + } +} + +void +Session::reconnect_mmc_ports(bool inputs) +{ + if (inputs ) { // get all enabled midi input ports + + boost::shared_ptr<MidiPort> mmc_in_ptr = _midi_ports->mmc_in(); + if (mmc_in_ptr) { + mmc_in_ptr->disconnect_all (); + std::vector<std::string> enabled_midi_inputs; + EngineStateController::instance()->get_physical_midi_inputs (enabled_midi_inputs); + + std::vector<std::string>::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<MidiPort> mmc_out_ptr = _midi_ports->mmc_out(); + if (mmc_out_ptr ) { + mmc_out_ptr->disconnect_all (); + std::vector<std::string> enabled_midi_outputs; + EngineStateController::instance()->get_physical_midi_outputs (enabled_midi_outputs); + + std::vector<std::string>::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". @@ -2365,16 +2796,18 @@ list< boost::shared_ptr<AudioTrack> > Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template) { - char track_name[32]; + string track_name; uint32_t track_id = 0; string port; RouteList new_routes; list<boost::shared_ptr<AudioTrack> > ret; - bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("Audio"); - + 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() ? _("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, use_number)) { error << "cannot find name for new audio track" << endmsg; goto failed; } @@ -2388,6 +2821,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 @@ -2426,6 +2873,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) { @@ -2445,7 +2894,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; @@ -2457,7 +2910,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod RouteList Session::new_audio_route (int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many, string name_template) { - char bus_name[32]; + string bus_name; uint32_t bus_id = 0; string port; RouteList ret; @@ -2465,7 +2918,7 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r 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, sizeof(bus_name), use_number)) { + 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; goto failure; } @@ -2510,6 +2963,8 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r ret.push_back (bus); + RouteAddedOrRemoved (true); /* EMIT SIGNAL */ + ARDOUR::GUIIdle (); } @@ -2531,7 +2986,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; @@ -2565,7 +3024,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template node_copy.remove_property_recursively (X_("id")); try { - char name[32]; + string name; if (!name_base.empty()) { @@ -2574,7 +3033,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template * numbered, via the final parameter. */ - if (!find_route_name (name_base.c_str(), ++number, name, sizeof(name), (being_added > 1))) { + if (!find_route_name (name_base.c_str(), ++number, name, (being_added > 1))) { fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; /*NOTREACHDE*/ } @@ -2584,7 +3043,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template 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, sizeof(name), true)) { + if (!find_route_name (route_name.c_str(), ++number, name, true)) { fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; abort(); /*NOTREACHED*/ } @@ -2630,6 +3089,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) { @@ -2648,7 +3109,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 (); } @@ -2679,6 +3144,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 */ } @@ -2736,7 +3203,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<Track> (tr))); track_playlist_changed (boost::weak_ptr<Track> (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<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (tr); if (mt) { @@ -2870,100 +3337,128 @@ Session::add_internal_send (boost::shared_ptr<Route> dest, boost::shared_ptr<Pro graph_reordered (); } + void -Session::remove_route (boost::shared_ptr<Route> route) +Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove) { - if (route == _master_out) { - return; - } - - route->set_solo (false, this); - - { + { // RCU Writer scope RCUWriter<RouteList> writer (routes); boost::shared_ptr<RouteList> 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<Route> (); - } - - 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<RouteList> r = routes.reader (); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - boost::shared_ptr<Send> 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<Route> (); + } + + 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<bool> 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<RouteList> r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + boost::shared_ptr<Send> 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<bool> uw (ignore_route_processor_changes, true); + (*iter)->remove_aux_or_listen (_monitor_out); + } + + boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (*iter); + if (mt && mt->step_editing()) { + if (_step_editors > 0) { + _step_editors--; + } + } - boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (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<bool> 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> route) +{ + boost::shared_ptr<RouteList> rl (new RouteList); + rl->push_back (route); + remove_routes (rl); } void @@ -3468,9 +3963,11 @@ Session::maybe_update_session_range (framepos_t a, framepos_t b) return; } + framepos_t session_end_marker_shift_samples = session_end_shift * _nominal_frame_rate; + if (_session_range_location == 0) { - add_session_range_location (a, b); + set_session_range_location (a, b + session_end_marker_shift_samples); } else { @@ -3907,7 +4404,7 @@ 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 (destructive) { + if (Profile->get_trx() && destructive) { sstr << 'T'; sstr << setfill ('0') << setw (4) << cnt; sstr << legalized_base; @@ -4237,7 +4734,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; } @@ -4560,12 +5057,12 @@ void Session::reset_native_file_format () { boost::shared_ptr<RouteList> rl = routes.reader (); + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i); if (tr) { /* don't save state as we do this, there's no point */ - _state_of_the_state = StateOfTheState (_state_of_the_state|InCleanup); tr->reset_write_sources (false); _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup); @@ -4948,9 +5445,15 @@ Session::have_rec_enabled_track () const return g_atomic_int_get (const_cast<gint*>(&_have_rec_enabled_track)) == 1; } +bool +Session::have_rec_disabled_track () const +{ + return g_atomic_int_get (const_cast<gint*>(&_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<RouteList> rl = routes.reader (); RouteList::iterator i = rl->begin(); @@ -4971,6 +5474,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<Track> tr = boost::dynamic_pointer_cast<Track> (*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 @@ -5013,7 +5532,8 @@ Session::route_added_to_route_group (RouteGroup* rg, boost::weak_ptr<Route> r) void Session::route_removed_from_route_group (RouteGroup* rg, boost::weak_ptr<Route> r) { - RouteRemovedFromRouteGroup (rg, r); + update_route_record_state (); + RouteRemovedFromRouteGroup (rg, r); /* EMIT SIGNAL */ } boost::shared_ptr<RouteList> @@ -5090,7 +5610,7 @@ Session::current_end_frame () const } void -Session::add_session_range_location (framepos_t start, framepos_t end) +Session::set_session_range_location (framepos_t start, framepos_t end) { _session_range_location = new Location (*this, start, end, _("session"), Location::IsSessionRange); _locations->add (_session_range_location); @@ -5526,6 +6046,12 @@ Session::next_control_id () const subtract++; } + /* the same about masterbus in Waves Tracks */ + + if (Profile->get_trx() && _master_out) { + subtract++; + } + return nroutes() - subtract; } @@ -5543,6 +6069,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 @@ -5597,6 +6131,11 @@ Session::reconnect_ltc_input () if (src != _("None") && !src.empty()) { _ltc_input->nth (0)->connect (src); } + + if ( ARDOUR::Profile->get_trx () ) { + // Tracks need this signal to update timecode_source_dropdown + MtcOrLtcInputPortChanged (); //emit signal + } } } @@ -5605,14 +6144,40 @@ Session::reconnect_ltc_output () { if (_ltc_output) { -#if 0 - string src = Config->get_ltc_sink_port(); + string src = Config->get_ltc_output_port(); _ltc_output->disconnect (this); if (src != _("None") && !src.empty()) { _ltc_output->nth (0)->connect (src); } -#endif } } + +void +Session::set_range_selection (framepos_t start, framepos_t end) +{ + _range_selection = Evoral::Range<framepos_t> (start, end); + follow_playhead_priority (); +} + +void +Session::set_object_selection (framepos_t start, framepos_t end) +{ + _object_selection = Evoral::Range<framepos_t> (start, end); + follow_playhead_priority (); +} + +void +Session::clear_range_selection () +{ + _range_selection = Evoral::Range<framepos_t> (-1,-1); + follow_playhead_priority (); +} + +void +Session::clear_object_selection () +{ + _object_selection = Evoral::Range<framepos_t> (-1,-1); + follow_playhead_priority (); +} |