#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/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"
#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"
#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"
, _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)
, 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)
, _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)
- , _order_hint (0)
+ , _order_hint (-1)
, ignore_route_processor_changes (false)
, _scene_changer (0)
, _midi_ports (0)
{
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) {
* 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 */
_is_new = false;
+ emit_thread_start ();
+
/* hook us up to the engine since we are now completely constructed */
BootMessage (_("Connect to engine"));
_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 ()
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;
}
_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.
*/
/* 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;
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
void
Session::setup_bundles ()
{
+
+ {
+ RCUWriter<BundleList> writer (_bundles);
+ boost::shared_ptr<BundleList> b = writer.get_copy ();
+ for (BundleList::iterator i = b->begin(); i != b->end();) {
+ if (boost::dynamic_pointer_cast<UserBundle>(*i)) {
+ ++i;
+ continue;
+ }
+ i = b->erase(i);
+ }
+ }
+
vector<string> inputs[DataType::num_types];
vector<string> outputs[DataType::num_types];
for (uint32_t i = 0; i < DataType::num_types; ++i) {
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<Bundle> 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 */
c->add_channel (_("R"), DataType::AUDIO);
c->set_port (1, outputs[DataType::AUDIO][np + 1]);
- add_bundle (c);
+ add_bundle (c, false);
}
}
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<Bundle> 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 */
c->add_channel (_("R"), DataType::AUDIO);
c->set_port (1, inputs[DataType::AUDIO][np + 1]);
- add_bundle (c);
+ add_bundle (c, false);
}
}
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<Bundle> 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<Bundle> 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
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;
}
}
}
+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<AudioPort> p = _monitor_out->input()->ports().nth_audio_port (n);
+ boost::shared_ptr<AudioPort> 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<Bundle> 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<string> 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<Port> 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<RouteList> rls = routes.reader ();
+
+ PBD::Unwinder<bool> 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 ()
{
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()) {
}
}
+ } else {
+ clear_events (SessionEvent::AutoLoopDeclick);
+ clear_events (SessionEvent::AutoLoop);
}
last_loopend = location->end();
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;
+ }
+
Locations::LocationList skips;
- if (consolidate) {
- consolidate_skips (loc);
- }
+ if (consolidate) {
+ PBD::Unwinder<bool> uw (_ignore_skips_updates, true);
+ consolidate_skips (loc);
+ }
sync_locations_to_skips ();
_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 ();
}
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<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);
+ for (Locations::LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
+ location_added (*i);
+ }
}
void
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 */
}
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);
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) {
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);
}
}
}
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);
}
}
}
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
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);
}
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<space_and_path>::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);
}
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<string> sdirs = source_search_path (DataType::AUDIO);
vector<space_and_path>::iterator i;
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;
}
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;
}
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;
}
}
void
-Session::add_bundle (boost::shared_ptr<Bundle> bundle)
+Session::add_bundle (boost::shared_ptr<Bundle> bundle, bool emit_signal)
{
{
RCUWriter<BundleList> writer (_bundles);
b->push_back (bundle);
}
- BundleAdded (bundle); /* EMIT SIGNAL */
+ if (emit_signal) {
+ BundleAddedOrRemoved (); /* EMIT SIGNAL */
+ }
set_dirty();
}
}
if (removed) {
- BundleRemoved (bundle); /* EMIT SIGNAL */
+ BundleAddedOrRemoved (); /* EMIT SIGNAL */
}
set_dirty();
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
{
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