X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=ce1fbca84624fe5410e70f92a0412dc09ecd0f35;hb=b0e41486f3b68d2d5f803a761dc49a42c4599339;hp=02f868a4c07e4ed2b9d6e4f60e3cea1f7f6d66f9;hpb=015fc7b39fab97cee1875231694adce43155ceb5;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 02f868a4c0..ce1fbca846 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -43,9 +43,11 @@ #include "pbd/stacktrace.h" #include "pbd/file_utils.h" +#include "ardour/amp.h" #include "ardour/analyser.h" #include "ardour/audio_buffer.h" #include "ardour/audio_diskstream.h" +#include "ardour/audio_port.h" #include "ardour/audio_track.h" #include "ardour/audioengine.h" #include "ardour/audiofilesource.h" @@ -54,12 +56,14 @@ #include "ardour/auditioner.h" #include "ardour/buffer_set.h" #include "ardour/bundle.h" +#include "ardour/butler.h" #include "ardour/click.h" #include "ardour/configuration.h" #include "ardour/crossfade.h" #include "ardour/cycle_timer.h" #include "ardour/data_type.h" #include "ardour/filename_extensions.h" +#include "ardour/internal_send.h" #include "ardour/io_processor.h" #include "ardour/midi_diskstream.h" #include "ardour/midi_playlist.h" @@ -70,6 +74,7 @@ #include "ardour/plugin_insert.h" #include "ardour/port_insert.h" #include "ardour/processor.h" +#include "ardour/rc_configuration.h" #include "ardour/recent_sessions.h" #include "ardour/region_factory.h" #include "ardour/return.h" @@ -100,7 +105,7 @@ sigc::signal Session::AskAboutPendingState; sigc::signal Session::AskAboutSampleRateMismatch; sigc::signal Session::SendFeedback; -sigc::signal Session::SMPTEOffsetChanged; +sigc::signal Session::TimecodeOffsetChanged; sigc::signal Session::StartTimeChanged; sigc::signal Session::EndTimeChanged; sigc::signal Session::AutoBindingOn; @@ -113,9 +118,7 @@ Session::Session (AudioEngine &eng, string mix_template) : _engine (eng), - phi (0), - target_phi (0), - phase (0), + _target_transport_speed (0.0), _requested_return_frame (-1), _scratch_buffers(new BufferSet()), _silent_buffers(new BufferSet()), @@ -128,10 +131,9 @@ Session::Session (AudioEngine &eng, _session_dir (new SessionDirectory(fullpath)), pending_events (2048), state_tree (0), - butler_mixdown_buffer (0), - butler_gain_buffer (0), - post_transport_work((PostTransportWork)0), - _send_smpte_update (false), + _butler (new Butler (this)), + _post_transport_work (0), + _send_timecode_update (false), midi_thread (pthread_t (0)), midi_requests (128), // the size of this should match the midi request pool size diskstreams (new DiskstreamList), @@ -149,11 +151,13 @@ Session::Session (AudioEngine &eng, { bool new_session; + interpolation.add_channel_to (0, 0); + if (!eng.connected()) { throw failed_constructor(); } - cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl; + info << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl; n_physical_outputs = _engine.n_physical_outputs(DataType::AUDIO); n_physical_inputs = _engine.n_physical_inputs(DataType::AUDIO); @@ -180,7 +184,8 @@ Session::Session (AudioEngine &eng, _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty); - Config->ParameterChanged.connect (mem_fun (*this, &Session::config_changed)); + Config->ParameterChanged.connect (bind (mem_fun (*this, &Session::config_changed), false)); + config.ParameterChanged.connect (bind (mem_fun (*this, &Session::config_changed), true)); if (was_dirty) { DirtyChanged (); /* EMIT SIGNAL */ @@ -199,9 +204,7 @@ Session::Session (AudioEngine &eng, nframes_t initial_length) : _engine (eng), - phi (0), - target_phi (0), - phase (0), + _target_transport_speed (0.0), _requested_return_frame (-1), _scratch_buffers(new BufferSet()), _silent_buffers(new BufferSet()), @@ -214,10 +217,9 @@ Session::Session (AudioEngine &eng, _session_dir ( new SessionDirectory(fullpath)), pending_events (2048), state_tree (0), - butler_mixdown_buffer (0), - butler_gain_buffer (0), - post_transport_work((PostTransportWork)0), - _send_smpte_update (false), + _butler (new Butler (this)), + _post_transport_work (0), + _send_timecode_update (false), midi_thread (pthread_t (0)), midi_requests (16), diskstreams (new DiskstreamList), @@ -234,11 +236,13 @@ Session::Session (AudioEngine &eng, { bool new_session; + interpolation.add_channel_to (0, 0); + if (!eng.connected()) { throw failed_constructor(); } - cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl; + info << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl; n_physical_outputs = _engine.n_physical_outputs (DataType::AUDIO); n_physical_inputs = _engine.n_physical_inputs (DataType::AUDIO); @@ -268,20 +272,11 @@ Session::Session (AudioEngine &eng, RouteList rl; int control_id = 1; - if (control_out_channels) { - ChanCount count(DataType::AUDIO, control_out_channels); - shared_ptr r (new Route (*this, _("monitor"), Route::ControlOut, - DataType::AUDIO, count, count)); - r->set_remote_control_id (control_id++); - - rl.push_back (r); - } - if (master_out_channels) { ChanCount count(DataType::AUDIO, master_out_channels); - cerr << "new MO with " << count << endl; - shared_ptr r (new Route (*this, _("master"), Route::MasterOut, - DataType::AUDIO, count, count)); + shared_ptr r (new Route (*this, _("master"), Route::MasterOut, DataType::AUDIO)); + r->input()->ensure_io (count, false, this); + r->output()->ensure_io (count, false, this); r->set_remote_control_id (control_id); rl.push_back (r); @@ -290,12 +285,27 @@ Session::Session (AudioEngine &eng, output_ac = AutoConnectOption (output_ac & ~AutoConnectMaster); } + if (control_out_channels) { + ChanCount count(DataType::AUDIO, control_out_channels); + shared_ptr r (new Route (*this, _("monitor"), Route::ControlOut, DataType::AUDIO)); + r->input()->ensure_io (count, false, this); + r->output()->ensure_io (count, false, this); + r->set_remote_control_id (control_id++); + + rl.push_back (r); + } + if (!rl.empty()) { add_routes (rl, false); } } + if (no_auto_connect()) { + input_ac = AutoConnectOption (0); + output_ac = AutoConnectOption (0); + } + Config->set_input_auto_connect (input_ac); Config->set_output_auto_connect (output_ac); @@ -308,7 +318,7 @@ Session::Session (AudioEngine &eng, _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty); - Config->ParameterChanged.connect (mem_fun (*this, &Session::config_changed)); + Config->ParameterChanged.connect (bind (mem_fun (*this, &Session::config_changed), false)); } Session::~Session () @@ -343,7 +353,11 @@ Session::destroy () delete state_tree; - terminate_butler_thread (); + /* reset dynamic state version back to default */ + + Stateful::loading_state_version = 0; + + _butler->terminate_thread (); //terminate_midi_thread (); if (click_data != default_click) { @@ -466,37 +480,14 @@ Session::destroy () } sources.clear (); -#ifdef TRACK_DESTRUCTION - cerr << "delete mix groups\n"; -#endif /* TRACK_DESTRUCTION */ - for (list::iterator i = mix_groups.begin(); i != mix_groups.end(); ) { - list::iterator tmp; - - tmp = i; - ++tmp; - - delete *i; - - i = tmp; - } #ifdef TRACK_DESTRUCTION - cerr << "delete edit groups\n"; + cerr << "delete route groups\n"; #endif /* TRACK_DESTRUCTION */ - for (list::iterator i = edit_groups.begin(); i != edit_groups.end(); ) { - list::iterator tmp; - - tmp = i; - ++tmp; - + for (list::iterator i = _route_groups.begin(); i != _route_groups.end(); ++i) { delete *i; - - i = tmp; } - delete [] butler_mixdown_buffer; - delete [] butler_gain_buffer; - Crossfade::set_buffer_size (0); delete mmc; @@ -515,8 +506,8 @@ Session::set_worst_io_latencies () boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - _worst_output_latency = max (_worst_output_latency, (*i)->output_latency()); - _worst_input_latency = max (_worst_input_latency, (*i)->input_latency()); + _worst_output_latency = max (_worst_output_latency, (*i)->output()->latency()); + _worst_input_latency = max (_worst_input_latency, (*i)->input()->latency()); } } @@ -532,7 +523,7 @@ Session::when_engine_running () BootMessage (_("Using configuration")); - Config->map_parameters (mem_fun (*this, &Session::config_changed)); + Config->map_parameters (bind (mem_fun (*this, &Session::config_changed), false)); /* every time we reconnect, recompute worst case output latencies */ @@ -542,7 +533,7 @@ Session::when_engine_running () _engine.transport_stop (); } - if (Config->get_jack_time_master()) { + if (config.get_jack_time_master()) { _engine.transport_locate (_transport_frame); } @@ -556,17 +547,25 @@ Session::when_engine_running () if (state_tree && (child = find_named_node (*state_tree->root(), "Click")) != 0) { /* existing state for Click */ + int c; - if (_click_io->set_state (*child->children().front()) == 0) { + if (Stateful::loading_state_version < 3000) { + c = _click_io->set_state_2X (*child->children().front(), Stateful::loading_state_version, false); + } else { + c = _click_io->set_state (*child->children().front(), Stateful::loading_state_version); + } + + if (c == 0) { _clicking = Config->get_clicking (); - + } else { - + error << _("could not setup Click I/O") << endmsg; _clicking = false; } + } else { /* default state for Click: dual-mono to first 2 physical outputs */ @@ -575,13 +574,13 @@ Session::when_engine_running () string physical_output = _engine.get_nth_physical_output (DataType::AUDIO, physport); if (physical_output.length()) { - if (_click_io->add_output_port (physical_output, this)) { + if (_click_io->add_port (physical_output, this)) { // relax, even though its an error } } } - - if (_click_io->n_outputs () > ChanCount::ZERO) { + + if (_click_io->n_ports () > ChanCount::ZERO) { _clicking = Config->get_clicking (); } } @@ -606,20 +605,24 @@ Session::when_engine_running () mono and stereo bundles, so that the common cases of mono and stereo tracks get bundles to put in their mixer strip in / out menus. There may be a nicer way of achieving that; - it doesn't really scale that well to higher channel counts + it doesn't really scale that well to higher channel counts */ + /* mono output bundles */ + for (uint32_t np = 0; np < n_physical_outputs; ++np) { char buf[32]; snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1); - shared_ptr c (new Bundle (buf, true)); + shared_ptr c (new Bundle (buf, true)); c->add_channel (_("mono")); - c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); + c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); - add_bundle (c); + add_bundle (c); } + /* stereo output bundles */ + for (uint32_t np = 0; np < n_physical_outputs; np += 2) { if (np + 1 < n_physical_outputs) { char buf[32]; @@ -634,17 +637,21 @@ Session::when_engine_running () } } + /* mono input bundles */ + for (uint32_t np = 0; np < n_physical_inputs; ++np) { char buf[32]; snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1); - shared_ptr c (new Bundle (buf, false)); + shared_ptr c (new Bundle (buf, false)); c->add_channel (_("mono")); - c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); + c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); - add_bundle (c); + add_bundle (c); } + /* stereo input bundles */ + for (uint32_t np = 0; np < n_physical_inputs; np += 2) { if (np + 1 < n_physical_inputs) { char buf[32]; @@ -660,72 +667,108 @@ Session::when_engine_running () } } - /* create master/control ports */ - - if (_master_out) { + BootMessage (_("Setup signal flow and plugins")); - /* force the master to ignore any later call to this - */ - if (_master_out->pending_state_node) { - _master_out->ports_became_legal(); - } - - /* create ports, without any connections - */ - _master_out->ensure_io (_master_out->input_minimum (), _master_out->output_minimum (), true, this); + hookup_io (); + + if (!no_auto_connect()) { + + if (_master_out && Config->get_auto_connect_standard_busses()) { + + /* if requested auto-connect the outputs to the first N physical ports. + */ - /* if requested auto-connect the outputs to the first N physical ports. - */ - if (Config->get_auto_connect_master()) { uint32_t limit = _master_out->n_outputs().n_total(); for (uint32_t n = 0; n < limit; ++n) { - Port* p = _master_out->output (n); + Port* p = _master_out->output()->nth (n); string connect_to = _engine.get_nth_physical_output (DataType (p->type()), n); - if (!connect_to.empty()) { - if (_master_out->connect_output (p, connect_to, this)) { - error << string_compose (_("cannot connect master output %1 to %2"), n, connect_to) + if (!connect_to.empty() && p->connected_to (connect_to) == false) { + if (_master_out->output()->connect (p, connect_to, this)) { + error << string_compose (_("cannot connect master output %1 to %2"), n, connect_to) << endmsg; break; } } } } - } - - BootMessage (_("Setup signal flow and plugins")); - hookup_io (); + if (_control_out) { - /* catch up on send+insert cnts */ + /* AUDIO ONLY as of june 29th 2009, because listen semantics for anything else + are undefined, at best. + */ - BootMessage (_("Catch up with send/insert state")); + /* control out listens to master bus (but ignores it + under some conditions) + */ - insert_cnt = 0; + uint32_t limit = _control_out->n_inputs().n_audio(); - for (list::iterator i = _port_inserts.begin(); i != _port_inserts.end(); ++i) { - uint32_t id; + if (_master_out) { + for (uint32_t n = 0; n < limit; ++n) { + AudioPort* p = _control_out->input()->ports().nth_audio_port (n); + AudioPort* o = _master_out->output()->ports().nth_audio_port (n); - if (sscanf ((*i)->name().c_str(), "%*s %u", &id) == 1) { - if (id > insert_cnt) { - insert_cnt = id; + if (o) { + string connect_to = o->name(); + if (_control_out->input()->connect (p, connect_to, this)) { + error << string_compose (_("cannot connect control input %1 to %2"), n, connect_to) + << endmsg; + break; + } + } + } } - } - } - send_cnt = 0; + /* if control out is not connected, + connect control out to physical outs, but use ones after the master if possible + */ + + if (!_control_out->output()->connected_to (boost::shared_ptr())) { + + if (!Config->get_monitor_bus_preferred_bundle().empty()) { + + boost::shared_ptr b = bundle_by_name (Config->get_monitor_bus_preferred_bundle()); + + if (b) { + _control_out->output()->connect_ports_to_bundle (b, 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 { + + /* XXX this logic is wrong for mixed port types */ - for (list::iterator i = _sends.begin(); i != _sends.end(); ++i) { - uint32_t id; + uint32_t shift = _master_out->n_outputs().n_audio(); + uint32_t mod = _engine.n_physical_outputs (DataType::AUDIO); + limit = _control_out->n_outputs().n_audio(); - if (sscanf ((*i)->name().c_str(), "%*s %u", &id) == 1) { - if (id > send_cnt) { - send_cnt = id; + cerr << "Connecting " << limit << " control out ports, shift is " << shift << " mod is " << mod << endl; + + for (uint32_t n = 0; n < limit; ++n) { + + Port* p = _control_out->output()->nth (n); + string connect_to = _engine.get_nth_physical_output (DataType (p->type()), (n+shift) % mod); + + if (!connect_to.empty()) { + if (_control_out->output()->connect (p, connect_to, this)) { + error << string_compose (_("cannot connect control output %1 to %2"), n, connect_to) + << endmsg; + break; + } + } + } + } } } } + /* catch up on send+insert cnts */ _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty)); @@ -762,28 +805,6 @@ Session::hookup_io () } } - /* Tell all IO objects to create their ports */ - - IO::enable_ports (); - - /* Connect track to listen/solo etc. busses XXX generalize this beyond control_out */ - - if (_control_out) { - - _control_out->ensure_io (_control_out->input_minimum(), _control_out->output_minimum(), false, this); - - boost::shared_ptr r = routes.reader (); - - for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { - - boost::shared_ptr t = boost::dynamic_pointer_cast (*x); - - if (t) { - t->listen_via (_control_out, X_("listen")); - } - } - } - /* load bundles, which we may have postponed earlier on */ if (_bundle_xml_node) { load_bundles (*_bundle_xml_node); @@ -796,7 +817,25 @@ Session::hookup_io () /* Now reset all panners */ - IO::reset_panners (); + Delivery::reset_panners (); + + /* Connect tracks to listen/solo etc. busses XXX generalize this beyond control_out */ + + if (_control_out) { + + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { + + if ((*x)->is_control() || (*x)->is_master()) { + continue; + } + + (*x)->listen_via (_control_out, + (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader), + false, false); + } + } /* Anyone who cares about input state, wake up and do something */ @@ -804,16 +843,19 @@ Session::hookup_io () _state_of_the_state = StateOfTheState (_state_of_the_state & ~InitialConnecting); - /* now handle the whole enchilada as if it was one graph reorder event. */ graph_reordered (); - /* update mixer solo state */ + /* update the full solo state, which can't be + correctly determined on a per-route basis, but + needs the global overview that only the session + has. + */ - catch_up_on_solo(); + update_route_solo_state (); } void @@ -829,8 +871,13 @@ Session::playlist_length_changed () } void -Session::diskstream_playlist_changed (boost::shared_ptr dstream) +Session::diskstream_playlist_changed (boost::weak_ptr wp) { + boost::shared_ptr dstream = wp.lock (); + if (!dstream) { + return; + } + boost::shared_ptr playlist; if ((playlist = dstream->playlist()) != 0) { @@ -845,9 +892,9 @@ bool Session::record_enabling_legal () const { /* this used to be in here, but survey says.... we don't need to restrict it */ - // if (record_status() == Recording) { - // return false; - // } + // if (record_status() == Recording) { + // return false; + // } if (Config->get_all_safe()) { return false; @@ -916,9 +963,10 @@ Session::auto_loop_changed (Location* location) if (transport_rolling() && play_loop) { - //if (_transport_frame < location->start() || _transport_frame > location->end()) { - if (_transport_frame > location->end()) { + // if (_transport_frame > location->end()) { + + if (_transport_frame < location->start() || _transport_frame > location->end()) { // relocate to beginning of loop clear_events (Event::LocateRoll); @@ -1016,9 +1064,12 @@ Session::set_auto_loop_location (Location* location) auto_loop_end_changed_connection.disconnect(); auto_loop_changed_connection.disconnect(); - auto_loop_start_changed_connection = location->start_changed.connect (mem_fun (this, &Session::auto_loop_changed)); - auto_loop_end_changed_connection = location->end_changed.connect (mem_fun (this, &Session::auto_loop_changed)); - auto_loop_changed_connection = location->changed.connect (mem_fun (this, &Session::auto_loop_changed)); + auto_loop_start_changed_connection = location->start_changed.connect ( + mem_fun (this, &Session::auto_loop_changed)); + auto_loop_end_changed_connection = location->end_changed.connect ( + mem_fun (this, &Session::auto_loop_changed)); + auto_loop_changed_connection = location->changed.connect ( + mem_fun (this, &Session::auto_loop_changed)); location->set_auto_loop (true, this); @@ -1032,7 +1083,7 @@ Session::set_auto_loop_location (Location* location) } void -Session::locations_added (Location* ignored) +Session::locations_added (Location *) { set_dirty (); } @@ -1186,12 +1237,12 @@ Session::maybe_enable_record () set_dirty(); } -nframes_t +nframes64_t Session::audible_frame () const { - nframes_t ret; + nframes64_t ret; + nframes64_t tf; nframes_t offset; - nframes_t tf; /* the first of these two possible settings for "offset" mean that the audible frame is stationary until @@ -1221,7 +1272,7 @@ Session::audible_frame () const } else { tf = _transport_frame; } - + ret = tf; if (!non_realtime_work_pending()) { @@ -1240,8 +1291,8 @@ Session::audible_frame () const if (tf < _last_roll_location + offset) { return _last_roll_location; } - } - + } + /* forwards */ ret -= offset; @@ -1323,7 +1374,7 @@ Session::set_block_size (nframes_t nframes) } void -Session::set_default_fade (float steepness, float fade_msecs) +Session::set_default_fade (float /*steepness*/, float /*fade_msecs*/) { #if 0 nframes_t fade_frames; @@ -1495,7 +1546,7 @@ Session::resort_routes_using (shared_ptr r) } list > -Session::new_midi_track (TrackMode mode, uint32_t how_many) +Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_many) { char track_name[32]; uint32_t track_id = 0; @@ -1555,7 +1606,13 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) try { track = boost::shared_ptr((new MidiTrack (*this, track_name, Route::Flag (0), mode))); - if (track->ensure_io (ChanCount(DataType::MIDI, 1), ChanCount(DataType::AUDIO, 1), false, this)) { + if (track->input()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) { + error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; + goto failed; + } + + + if (track->output()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) { error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; goto failed; } @@ -1598,6 +1655,7 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) */ track->midi_diskstream()->non_realtime_input_change(); + track->set_route_group (route_group, 0); track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); //track->set_remote_control_id (control_id); @@ -1654,7 +1712,7 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) } list > -Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, uint32_t how_many) +Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group, uint32_t how_many) { char track_name[32]; uint32_t track_id = 0; @@ -1713,7 +1771,14 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod try { track = boost::shared_ptr((new AudioTrack (*this, track_name, Route::Flag (0), mode))); - if (track->ensure_io (ChanCount(DataType::AUDIO, input_channels), ChanCount(DataType::AUDIO, output_channels), false, this)) { + if (track->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) { + error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; + goto failed; + } + + if (track->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) { error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), input_channels, output_channels) << endmsg; @@ -1731,7 +1796,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod port = physinputs[(channels_used+x)%nphysical_in]; } - if (port.length() && track->connect_input (track->input (x), port, this)) { + if (port.length() && track->input()->connect (track->input()->nth(x), port, this)) { break; } } @@ -1742,16 +1807,16 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod for (uint32_t x = 0; x < track->n_outputs().n_audio(); ++x) { port = ""; - + if (Config->get_output_auto_connect() & AutoConnectPhysical) { port = physoutputs[(channels_used+x)%nphysical_out]; } else if (Config->get_output_auto_connect() & AutoConnectMaster) { if (_master_out && _master_out->n_inputs().n_audio() > 0) { - port = _master_out->input (x % _master_out->n_inputs().n_audio())->name(); + port = _master_out->input()->nth (x % _master_out->input()->n_ports().n_audio())->name(); } } - - if (port.length() && track->connect_output (track->output (x), port, this)) { + + if (port.length() && track->output()->connect (track->output()->nth(x), port, this)) { break; } } @@ -1759,6 +1824,8 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod channels_used += track->n_inputs ().n_audio(); + track->set_route_group (route_group, 0); + track->audio_diskstream()->non_realtime_input_change(); track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); @@ -1837,7 +1904,7 @@ Session::set_remote_control_ids () RouteList -Session::new_audio_route (int input_channels, int output_channels, uint32_t how_many) +Session::new_audio_route (bool aux, int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many) { char bus_name[32]; uint32_t bus_id = 1; @@ -1891,7 +1958,7 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ try { shared_ptr bus (new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO)); - if (bus->ensure_io (ChanCount(DataType::AUDIO, input_channels), ChanCount(DataType::AUDIO, output_channels), false, this)) { + if (bus->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) { error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), input_channels, output_channels) << endmsg; @@ -1899,21 +1966,24 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ } + if (bus->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) { + error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; + goto failure; + } - /* - for (uint32_t x = 0; n_physical_audio_inputs && x < bus->n_inputs(); ++x) { - + for (uint32_t x = 0; n_physical_audio_inputs && x < bus->input()->n_ports().n_audio(); ++x) { port = ""; - + if (Config->get_input_auto_connect() & AutoConnectPhysical) { port = physinputs[((n+x)%n_physical_audio_inputs)]; - } - - if (port.length() && bus->connect_input (bus->input (x), port, this)) { + } + + if (port.length() && bus->input()->connect (bus->input()->nth (x), port, this)) { break; } } - */ for (uint32_t x = 0; n_physical_audio_outputs && x < bus->n_outputs().n_audio(); ++x) { port = ""; @@ -1922,20 +1992,25 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ port = physoutputs[((n+x)%n_physical_outputs)]; } else if (Config->get_output_auto_connect() & AutoConnectMaster) { if (_master_out) { - port = _master_out->input (x%_master_out->n_inputs().n_audio())->name(); + port = _master_out->input()->nth (x%_master_out->input()->n_ports().n_audio())->name(); } } - if (port.length() && bus->connect_output (bus->output (x), port, this)) { + if (port.length() && bus->output()->connect (bus->output()->nth(x), port, this)) { break; } } channels_used += bus->n_inputs ().n_audio(); + bus->set_route_group (route_group, 0); bus->set_remote_control_id (control_id); ++control_id; + if (aux) { + bus->add_internal_return (); + } + ret.push_back (bus); } @@ -1970,6 +2045,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template RouteList ret; uint32_t control_id; XMLTree tree; + uint32_t number = 1; if (!tree.read (template_path.c_str())) { return ret; @@ -1982,38 +2058,33 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template while (how_many) { XMLNode node_copy (*node); // make a copy so we can change the name if we need to - - std::string node_name = IO::name_from_state (*node_copy.children().front()); - if (route_by_name (node_name) != 0) { + std::string node_name = IO::name_from_state (*node_copy.children().front()); - /* generate a new name by adding a number to the end of the template name */ + /* generate a new name by adding a number to the end of the template name */ - uint32_t number = 1; + do { + snprintf (name, sizeof (name), "%s %" PRIu32, node_name.c_str(), number); - do { - snprintf (name, sizeof (name), "%s %" PRIu32, node_name.c_str(), number); - - number++; - - if (route_by_name (name) == 0) { - break; - } - - } while (number < UINT_MAX); + number++; - if (number == UINT_MAX) { - fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; - /*NOTREACHED*/ + if (route_by_name (name) == 0) { + break; } - IO::set_name_in_state (*node_copy.children().front(), name); + } while (number < UINT_MAX); + + if (number == UINT_MAX) { + fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; + /*NOTREACHED*/ } + IO::set_name_in_state (*node_copy.children().front(), name); + Track::zero_diskstream_id_in_xml (node_copy); try { - shared_ptr route (XMLRouteFactory (node_copy)); + shared_ptr route (XMLRouteFactory (node_copy, 3000)); if (route == 0) { error << _("Session: cannot create track/bus from template description") << endmsg; @@ -2025,26 +2096,26 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template picks up the configuration of the route. During session loading this normally happens in a different way. */ - route->input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), this); - route->output_changed (IOChange (ConfigurationChanged|ConnectionsChanged), this); + route->input()->changed (IOChange (ConfigurationChanged|ConnectionsChanged), this); + route->output()->changed (IOChange (ConfigurationChanged|ConnectionsChanged), this); } - + route->set_remote_control_id (control_id); ++control_id; - + ret.push_back (route); } - + catch (failed_constructor &err) { error << _("Session: could not create new route from template") << endmsg; goto out; } - + catch (AudioEngine::PortRegistrationFailure& pfe) { error << pfe.what() << endmsg; goto out; } - + --how_many; } @@ -2063,17 +2134,29 @@ Session::add_routes (RouteList& new_routes, bool save) RCUWriter writer (routes); shared_ptr r = writer.get_copy (); r->insert (r->end(), new_routes.begin(), new_routes.end()); - resort_routes_using (r); + + + /* if there is no control out and we're not in the middle of loading, + resort the graph here. if there is a control out, we will resort + toward the end of this method. if we are in the middle of loading, + we will resort when done. + */ + + if (!_control_out && IO::connecting_legal) { + resort_routes_using (r); + } } for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { boost::weak_ptr wpr (*x); + (*x)->listen_changed.connect (sigc::bind (mem_fun (*this, &Session::route_listen_changed), wpr)); (*x)->solo_changed.connect (sigc::bind (mem_fun (*this, &Session::route_solo_changed), wpr)); (*x)->mute_changed.connect (mem_fun (*this, &Session::route_mute_changed)); - (*x)->output_changed.connect (mem_fun (*this, &Session::set_worst_io_latencies_x)); + (*x)->output()->changed.connect (mem_fun (*this, &Session::set_worst_io_latencies_x)); (*x)->processors_changed.connect (bind (mem_fun (*this, &Session::update_latency_compensation), false, false)); + (*x)->route_group_changed.connect (hide (mem_fun (*this, &Session::route_group_changed))); if ((*x)->is_master()) { _master_out = (*x); @@ -2087,8 +2170,15 @@ Session::add_routes (RouteList& new_routes, bool save) if (_control_out && IO::connecting_legal) { for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { - (*x)->listen_via (_control_out, "control"); + if ((*x)->is_control() || (*x)->is_master()) { + continue; + } + (*x)->listen_via (_control_out, + (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader), + false, false); } + + resort_routes (); } set_dirty(); @@ -2100,6 +2190,97 @@ Session::add_routes (RouteList& new_routes, bool save) RouteAdded (new_routes); /* EMIT SIGNAL */ } +void +Session::globally_set_send_gains_to_zero (boost::shared_ptr dest) +{ + boost::shared_ptr r = routes.reader (); + boost::shared_ptr s; + + /* only tracks */ + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (boost::dynamic_pointer_cast(*i)) { + if ((s = (*i)->internal_send_for (dest)) != 0) { + s->amp()->gain_control()->set_value (0.0); + } + } + } +} + +void +Session::globally_set_send_gains_to_unity (boost::shared_ptr dest) +{ + boost::shared_ptr r = routes.reader (); + boost::shared_ptr s; + + /* only tracks */ + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (boost::dynamic_pointer_cast(*i)) { + if ((s = (*i)->internal_send_for (dest)) != 0) { + s->amp()->gain_control()->set_value (1.0); + } + } + } +} + +void +Session::globally_set_send_gains_from_track(boost::shared_ptr dest) +{ + boost::shared_ptr r = routes.reader (); + boost::shared_ptr s; + + /* only tracks */ + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (boost::dynamic_pointer_cast(*i)) { + if ((s = (*i)->internal_send_for (dest)) != 0) { + s->amp()->gain_control()->set_value ((*i)->gain_control()->get_value()); + } + } + } +} + +void +Session::globally_add_internal_sends (boost::shared_ptr dest, Placement p) +{ + boost::shared_ptr r = routes.reader (); + boost::shared_ptr t (new RouteList); + + /* only send tracks */ + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (boost::dynamic_pointer_cast(*i)) { + t->push_back (*i); + } + } + + add_internal_sends (dest, p, t); +} + +void +Session::add_internal_sends (boost::shared_ptr dest, Placement p, boost::shared_ptr senders) +{ + if (dest->is_control() || dest->is_master()) { + return; + } + + if (!dest->internal_return()) { + dest->add_internal_return(); + } + + for (RouteList::iterator i = senders->begin(); i != senders->end(); ++i) { + + if ((*i)->is_control() || (*i)->is_master() || (*i) == dest) { + continue; + } + + (*i)->listen_via (dest, p, true, true); + } + + graph_reordered (); +} + void Session::add_diskstream (boost::shared_ptr dstream) { @@ -2115,9 +2296,9 @@ Session::add_diskstream (boost::shared_ptr dstream) /* writer goes out of scope, copies ds back to main */ } - dstream->PlaylistChanged.connect (sigc::bind (mem_fun (*this, &Session::diskstream_playlist_changed), dstream)); + dstream->PlaylistChanged.connect (sigc::bind (mem_fun (*this, &Session::diskstream_playlist_changed), boost::weak_ptr (dstream))); /* this will connect to future changes, and check the current length */ - diskstream_playlist_changed (dstream); + diskstream_playlist_changed (boost::weak_ptr (dstream)); dstream->RecordEnableChanged.connect (mem_fun (*this, &Session::update_have_rec_enabled_diskstream)); @@ -2144,13 +2325,14 @@ Session::remove_route (shared_ptr route) } if (route == _control_out) { + /* cancel control outs for all routes */ for (RouteList::iterator r = rs->begin(); r != rs->end(); ++r) { (*r)->drop_listen (_control_out); } - _control_out = shared_ptr (); + _control_out.reset (); } update_route_solo_state (); @@ -2178,8 +2360,8 @@ Session::remove_route (shared_ptr route) // We need to disconnect the routes inputs and outputs - route->disconnect_inputs (0); - route->disconnect_outputs (0); + route->input()->disconnect (0); + route->output()->disconnect (0); update_latency_compensation (false, false); set_dirty(); @@ -2204,20 +2386,35 @@ Session::remove_route (shared_ptr route) } void -Session::route_mute_changed (void* src) +Session::route_mute_changed (void* /*src*/) { set_dirty (); } void -Session::route_solo_changed (void* src, boost::weak_ptr wpr) +Session::route_listen_changed (void* /*src*/, boost::weak_ptr wpr) +{ + boost::shared_ptr route = wpr.lock(); + if (!route) { + error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_changed")) << endmsg; + return; + } + + if (route->listening()) { + _listen_cnt++; + } else if (_listen_cnt > 0) { + _listen_cnt--; + } +} + +void +Session::route_solo_changed (void* /*src*/, boost::weak_ptr wpr) { if (solo_update_disabled) { // We know already return; } - bool is_track; boost::shared_ptr route = wpr.lock (); if (!route) { @@ -2226,218 +2423,90 @@ Session::route_solo_changed (void* src, boost::weak_ptr wpr) return; } - is_track = (boost::dynamic_pointer_cast(route) != 0); - shared_ptr r = routes.reader (); + int32_t delta; - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - - /* soloing a track mutes all other tracks, soloing a bus mutes all other busses */ - - if (is_track) { - - /* don't mess with busses */ - - if (boost::dynamic_pointer_cast(*i) == 0) { - continue; - } - - } else { - - /* don't mess with tracks */ - - if (boost::dynamic_pointer_cast(*i) != 0) { - continue; - } - } - - if ((*i) != route && - ((*i)->mix_group () == 0 || - (*i)->mix_group () != route->mix_group () || - !route->mix_group ()->is_active())) { - - if ((*i)->soloed()) { - - /* if its already soloed, and solo latching is enabled, - then leave it as it is. - */ + if (route->self_soloed()) { + delta = 1; + } else { + delta = -1; + } - if (Config->get_solo_latched()) { - continue; - } - } + /* now mod the solo level of all other routes except master & control outs + so that they will be silent if appropriate. + */ - /* do it */ + solo_update_disabled = true; - solo_update_disabled = true; - (*i)->set_solo (false, src); - solo_update_disabled = false; - } - } + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + bool via_sends_only; - bool something_soloed = false; - bool same_thing_soloed = false; - bool signal = false; - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ((*i)->soloed()) { - something_soloed = true; - if (boost::dynamic_pointer_cast(*i)) { - if (is_track) { - same_thing_soloed = true; - break; - } - } else { - if (!is_track) { - same_thing_soloed = true; - break; - } + if ((*i) == route || !(*i)->solo_isolated() || !(*i)->is_master() || !(*i)->is_control() || (*i)->is_hidden()) { + continue; + } else if ((*i)->feeds (route, &via_sends_only)) { + if (!via_sends_only) { + (*i)->mod_solo_by_others (delta); } - break; - } + } } - if (something_soloed != currently_soloing) { - signal = true; - currently_soloing = something_soloed; - } + /* make sure master is never muted by solo */ - modify_solo_mute (is_track, same_thing_soloed); + if (_master_out && route != _master_out && _master_out->soloed_by_others() == 0 && !_master_out->soloed()) { + _master_out->mod_solo_by_others (1); + } + + /* ditto for control outs make sure master is never muted by solo */ - if (signal) { - SoloActive (currently_soloing); /* EMIT SIGNAL */ + if (_control_out && route != _control_out && _control_out && _control_out->soloed_by_others() == 0) { + _control_out->mod_solo_by_others (1); } + solo_update_disabled = false; + update_route_solo_state (r); SoloChanged (); /* EMIT SIGNAL */ - set_dirty(); } void -Session::update_route_solo_state () +Session::update_route_solo_state (boost::shared_ptr r) { - bool mute = false; - bool is_track = false; - bool signal = false; + /* now figure out if anything that matters is soloed */ - /* this is where we actually implement solo by changing - the solo mute setting of each track. - */ + bool something_soloed = false; - shared_ptr r = routes.reader (); + if (!r) { + r = routes.reader(); + } for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ((*i)->soloed()) { - mute = true; - if (boost::dynamic_pointer_cast(*i)) { - is_track = true; - } + if (!(*i)->is_master() && !(*i)->is_control() && !(*i)->is_hidden() && (*i)->self_soloed()) { + something_soloed = true; break; } } - if (mute != currently_soloing) { - signal = true; - currently_soloing = mute; - } - - if (!is_track && !mute) { - - /* nothing is soloed */ - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - (*i)->set_solo_mute (false); - } - - if (signal) { - SoloActive (false); - } - - return; - } - - modify_solo_mute (is_track, mute); - - if (signal) { - SoloActive (currently_soloing); + if (something_soloed != _non_soloed_outs_muted) { + _non_soloed_outs_muted = something_soloed; + SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */ } } -void -Session::modify_solo_mute (bool is_track, bool mute) +boost::shared_ptr +Session::get_routes_with_internal_returns() const { shared_ptr r = routes.reader (); + boost::shared_ptr rl (new RouteList); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - - if (is_track) { - - /* only alter track solo mute */ - - if (boost::dynamic_pointer_cast(*i)) { - if ((*i)->soloed()) { - (*i)->set_solo_mute (!mute); - } else { - (*i)->set_solo_mute (mute); - } - } - - } else { - - /* only alter bus solo mute */ - - if (!boost::dynamic_pointer_cast(*i)) { - - if ((*i)->soloed()) { - - (*i)->set_solo_mute (false); - - } else { - - /* don't mute master or control outs - in response to another bus solo - */ - - if ((*i) != _master_out && - (*i) != _control_out) { - (*i)->set_solo_mute (mute); - } - } - } - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if ((*i)->internal_return ()) { + rl->push_back (*i); } } + return rl; } - -void -Session::catch_up_on_solo () -{ - /* this is called after set_state() to catch the full solo - state, which can't be correctly determined on a per-route - basis, but needs the global overview that only the session - has. - */ - update_route_solo_state(); -} - -void -Session::catch_up_on_solo_mute_override () -{ - if (Config->get_solo_model() != InverseMute) { - return; - } - - /* this is called whenever the param solo-mute-override is - changed. - */ - shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - (*i)->catch_up_on_solo_mute_override (); - } -} - shared_ptr Session::route_by_name (string name) { @@ -2749,17 +2818,17 @@ void Session::update_region_name_map (boost::shared_ptr region) { string::size_type last_period = region->name().find_last_of ('.'); - + if (last_period != string::npos && last_period < region->name().length() - 1) { - + string base = region->name().substr (0, last_period); string number = region->name().substr (last_period+1); map::iterator x; - + /* note that if there is no number, we get zero from atoi, which is just fine */ - + region_name_map[base] = atoi (number); } } @@ -2970,6 +3039,23 @@ Session::remove_source (boost::weak_ptr src) } } +/** Return the number of playlists (not regions) that contain @a src */ +uint32_t +Session::source_use_count (boost::shared_ptr src) const +{ + uint32_t count = 0; + for (PlaylistList::const_iterator p = playlists.begin(); p != playlists.end(); ++p) { + for (Playlist::RegionList::const_iterator r = (*p)->region_list().begin(); + r != (*p)->region_list().end(); ++r) { + if ((*r)->uses_source(src)) { + ++count; + break; + } + } + } + return count; +} + boost::shared_ptr Session::source_by_id (const PBD::ID& id) { @@ -3464,8 +3550,8 @@ void Session::set_audition (boost::shared_ptr r) { pending_audition_region = r; - post_transport_work = PostTransportWork (post_transport_work | PostTransportAudition); - schedule_butler_transport_work (); + add_post_transport_work (PostTransportAudition); + _butler->schedule_transport_work (); } void @@ -3572,6 +3658,20 @@ Session::set_all_solo (bool yn) set_dirty(); } +void +Session::set_all_listen (bool yn) +{ + shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (!(*i)->is_hidden()) { + (*i)->set_listen (yn, this); + } + } + + set_dirty(); +} + void Session::set_all_mute (bool yn) { @@ -3662,26 +3762,7 @@ Session::record_enable_change_all (bool yn) void Session::add_processor (Processor* processor) { - Send* send; - Return* retrn; - PortInsert* port_insert; - PluginInsert* plugin_insert; - - if ((port_insert = dynamic_cast (processor)) != 0) { - _port_inserts.insert (_port_inserts.begin(), port_insert); - } else if ((plugin_insert = dynamic_cast (processor)) != 0) { - _plugin_inserts.insert (_plugin_inserts.begin(), plugin_insert); - } else if ((send = dynamic_cast (processor)) != 0) { - _sends.insert (_sends.begin(), send); - } else if ((retrn = dynamic_cast (processor)) != 0) { - _returns.insert (_returns.begin(), retrn); - } else { - fatal << _("programming error: unknown type of Insert created!") << endmsg; - /*NOTREACHED*/ - } - processor->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_processor), processor)); - set_dirty(); } @@ -3691,31 +3772,13 @@ Session::remove_processor (Processor* processor) Send* send; Return* retrn; PortInsert* port_insert; - PluginInsert* plugin_insert; if ((port_insert = dynamic_cast (processor)) != 0) { - list::iterator x = find (_port_inserts.begin(), _port_inserts.end(), port_insert); - if (x != _port_inserts.end()) { - insert_bitset[port_insert->bit_slot()] = false; - _port_inserts.erase (x); - } - } else if ((plugin_insert = dynamic_cast (processor)) != 0) { - _plugin_inserts.remove (plugin_insert); + insert_bitset[port_insert->bit_slot()] = false; } else if ((send = dynamic_cast (processor)) != 0) { - list::iterator x = find (_sends.begin(), _sends.end(), send); - if (x != _sends.end()) { - send_bitset[send->bit_slot()] = false; - _sends.erase (x); - } + send_bitset[send->bit_slot()] = false; } else if ((retrn = dynamic_cast (processor)) != 0) { - list::iterator x = find (_returns.begin(), _returns.end(), retrn); - if (x != _returns.end()) { - return_bitset[send->bit_slot()] = false; - _returns.erase (x); - } - } else { - fatal << _("programming error: unknown type of Insert deleted!") << endmsg; - /*NOTREACHED*/ + return_bitset[send->bit_slot()] = false; } set_dirty(); @@ -3797,7 +3860,7 @@ shared_ptr Session::bundle_by_name (string name) const { boost::shared_ptr b = _bundles.reader (); - + for (BundleList::const_iterator i = b->begin(); i != b->end(); ++i) { if ((*i)->name() == name) { return* i; @@ -3808,7 +3871,7 @@ Session::bundle_by_name (string name) const } void -Session::tempo_map_changed (Change ignored) +Session::tempo_map_changed (Change) { clear_clicks (); @@ -4092,8 +4155,8 @@ Session::freeze (InterThreadInfo& itt) } boost::shared_ptr -Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, - bool overwrite, vector >& srcs, +Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, + bool /*overwrite*/, vector >& srcs, InterThreadInfo& itt, bool enable_processing) { boost::shared_ptr result; @@ -4222,7 +4285,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, /* construct a region to represent the bounced material */ result = RegionFactory::create (srcs, 0, - srcs.front()->length(srcs.front()->timeline_position()), + srcs.front()->length(srcs.front()->timeline_position()), region_name_from_path (srcs.front()->name(), true)); } @@ -4234,7 +4297,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, if (afs) { afs->mark_for_remove (); } - + (*src)->drop_references (); } @@ -4331,7 +4394,7 @@ Session::compute_initial_length () } void -Session::sync_order_keys (const char* base) +Session::sync_order_keys (std::string const & base) { if (!Config->get_sync_all_route_ordering()) { /* leave order keys as they are */ @@ -4373,3 +4436,61 @@ Session::update_have_rec_enabled_diskstream () RecordStateChanged (); /* EMIT SIGNAL */ } } + +void +Session::listen_position_changed () +{ + Placement p; + + switch (Config->get_listen_position()) { + case AfterFaderListen: + p = PostFader; + break; + + case PreFaderListen: + p = PreFader; + break; + } + + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->put_control_outs_at (p); + } +} + +void +Session::solo_control_mode_changed () +{ + /* cancel all solo or all listen when solo control mode changes */ + + if (Config->get_solo_control_is_listen_control()) { + set_all_solo (false); + } else { + set_all_listen (false); + } +} + +void +Session::route_group_changed () +{ + RouteGroupChanged (); /* EMIT SIGNAL */ +} + +vector +Session::get_available_sync_options () const +{ + vector ret; + + ret.push_back (JACK); + + if (mtc_port()) { + ret.push_back (MTC); + } + + if (midi_clock_port()) { + ret.push_back (MIDIClock); + } + + return ret; +}