#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"
#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"
#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"
, _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)
, 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)
_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 ()
_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.
*/
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.
*/
void
Session::remove_monitor_section ()
{
- if (!_monitor_out) {
+ if (!_monitor_out || Profile->get_trx()) {
return;
}
{
RouteList rl;
- if (_monitor_out || !_master_out) {
+ if (_monitor_out || !_master_out || Profile->get_trx()) {
return;
}
{
/* Process lock should be held by the caller.*/
- if (!_monitor_out) {
+ if (!_monitor_out || Profile->get_trx()) {
return;
}
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);
void
Session::update_skips (Location* loc, bool consolidate)
{
- if (_ignore_skips_updates) {
- return;
- }
-
+ if (_ignore_skips_updates) {
+ return;
+ }
+
Locations::LocationList skips;
if (consolidate) {
_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 */
update_skips (location, true);
}
-
+
set_dirty ();
}
new_routes.push_back (track);
ret.push_back (track);
+
+ RouteAddedOrRemoved (true); /* EMIT SIGNAL */
}
catch (failed_constructor &err) {
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) {
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)) {
}
}
+#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()
+{
+#if 0
+ boost::shared_ptr<MidiPort> mtc_in_ptr = _midi_ports->mtc_input_port();
+
+ if (mtc_in_ptr) {
+ 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
+ }
+ }
+#endif
+}
+
+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".
RouteList new_routes;
list<boost::shared_ptr<AudioTrack> > 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;
}
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
new_routes.push_back (track);
ret.push_back (track);
+
+ RouteAddedOrRemoved (true); /* EMIT SIGNAL */
}
catch (failed_constructor &err) {
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;
ret.push_back (bus);
+ RouteAddedOrRemoved (true); /* EMIT SIGNAL */
+
ARDOUR::GUIIdle ();
}
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;
++control_id;
ret.push_back (route);
+
+ RouteAddedOrRemoved (true); /* EMIT SIGNAL */
}
catch (failed_constructor &err) {
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 ();
}
reassign_track_numbers();
+ update_route_record_state ();
+
RouteAdded (new_routes); /* EMIT SIGNAL */
}
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) {
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
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;
}
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();
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
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>
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