#include "ardour/revision.h"
#include "ardour/route_graph.h"
#include "ardour/route_group.h"
+#include "ardour/rt_tasklist.h"
#include "ardour/send.h"
#include "ardour/selection.h"
#include "ardour/session.h"
#include "ardour/session_directory.h"
#include "ardour/session_playlists.h"
-#include "ardour/slave.h"
#include "ardour/smf_source.h"
-#include "ardour/slave.h"
#include "ardour/solo_isolate_control.h"
#include "ardour/source_factory.h"
#include "ardour/speakers.h"
#include "ardour/tempo.h"
#include "ardour/ticker.h"
+#include "ardour/transport_master.h"
#include "ardour/track.h"
#include "ardour/types_convert.h"
#include "ardour/user_bundle.h"
, _seek_counter (0)
, _session_range_location (0)
, _session_range_end_is_free (true)
- , _slave (0)
, _silent (false)
, _remaining_latency_preroll (0)
+ , _engine_speed (1.0)
, _transport_speed (0)
, _default_transport_speed (1.0)
, _last_transport_speed (0)
, _signalled_varispeed (0)
, _target_transport_speed (0.0)
, auto_play_legal (false)
- , _last_slave_transport_sample (0)
- , maximum_output_latency (0)
, _requested_return_sample (-1)
, current_block_size (0)
, _worst_output_latency (0)
, _worst_input_latency (0)
- , _worst_track_latency (0)
- , _worst_track_out_latency (0)
+ , _worst_route_latency (0)
+ , _send_latency_changes (0)
, _have_captured (false)
, _non_soloed_outs_muted (false)
, _listening (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)
- , have_first_delta_accumulator (false)
- , _slave_state (Stopped)
- , _mtc_active (false)
- , _ltc_active (false)
+ , transport_master_tracking_state (Stopped)
+ , master_wait_end (0)
, post_export_sync (false)
, post_export_position (0)
, _exporting (false)
, _last_roll_location (0)
, _last_roll_or_reversal_location (0)
, _last_record_location (0)
- , pending_locate_roll (false)
- , pending_locate_sample (0)
- , pending_locate_flush (false)
- , pending_abort (false)
, pending_auto_loop (false)
, _mempool ("Session", 3145728)
, lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
, _play_range (false)
, _range_selection (-1,-1)
, _object_selection (-1,-1)
- , _preroll_record_punch_pos (-1)
, _preroll_record_trim_len (0)
, _count_in_once (false)
, main_outs (0)
StartTimeChanged.connect_same_thread (*this, boost::bind (&Session::start_time_changed, this, _1));
EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1));
+ Send::ChangedLatency.connect_same_thread (*this, boost::bind (&Session::send_latency_compensation_change, this));
+
emit_thread_start ();
auto_connect_thread_start ();
* session or set state for an existing one.
*/
+ _rt_tasklist.reset (new RTTaskList ());
+
if (how_many_dsp_threads () > 1) {
/* For now, only create the graph if we are using >1 DSP threads, as
it is a bit slower than the old code with 1 thread.
{
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
ltc_tx_cleanup();
- delete _slave;
- _slave = 0;
}
/* disconnect from any and all signals that we are connected to */
/* remove I/O objects before unsetting the engine session */
_click_io.reset ();
- _ltc_input.reset ();
_ltc_output.reset ();
ControlProtocolManager::instance().drop_protocols ();
EngineStateController::instance()->remove_session();
#endif
- /* drop slave, if any. We don't use use_sync_source (0) because
- * there's no reason to do all the other stuff that may happen
- * when calling that method.
- */
- delete _slave;
-
/* deregister all ports - there will be no process or any other
* callbacks from the engine any more.
*/
bool del = true;
switch (ev->type) {
case SessionEvent::AutoLoop:
- case SessionEvent::AutoLoopDeclick:
case SessionEvent::Skip:
case SessionEvent::PunchIn:
case SessionEvent::PunchOut:
- case SessionEvent::RecordStart:
case SessionEvent::StopOnce:
case SessionEvent::RangeStop:
case SessionEvent::RangeLocate:
{
XMLNode* child = 0;
- _ltc_input.reset (new IO (*this, X_("LTC In"), IO::Input));
_ltc_output.reset (new IO (*this, X_("LTC Out"), IO::Output));
- if (state_tree && (child = find_named_node (*state_tree->root(), X_("LTC In"))) != 0) {
- _ltc_input->set_state (*(child->children().front()), Stateful::loading_state_version);
- } else {
- {
- Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
- _ltc_input->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
- // TODO use auto-connect thread somehow (needs a route currently)
- // see note in Session::auto_connect_thread_run() why process lock is needed.
- reconnect_ltc_input ();
- }
- }
-
if (state_tree && (child = find_named_node (*state_tree->root(), X_("LTC Out"))) != 0) {
_ltc_output->set_state (*(child->children().front()), Stateful::loading_state_version);
} else {
* IO style of NAME/TYPE-{in,out}N
*/
- _ltc_input->nth (0)->set_name (X_("LTC-in"));
_ltc_output->nth (0)->set_name (X_("LTC-out"));
}
{
replace_event (SessionEvent::PunchIn, location->start());
- if (get_record_enabled() && config.get_punch_in()) {
+ if (get_record_enabled() && config.get_punch_in() && !actively_recording ()) {
/* capture start has been changed, so save new pending state */
save_state ("", true);
}
void
Session::auto_punch_end_changed (Location* location)
{
- samplepos_t when_to_stop = location->end();
- // when_to_stop += _worst_output_latency + _worst_input_latency;
- replace_event (SessionEvent::PunchOut, when_to_stop);
+ replace_event (SessionEvent::PunchOut, location->end());
}
void
Session::auto_punch_changed (Location* location)
{
- samplepos_t when_to_stop = location->end();
-
- replace_event (SessionEvent::PunchIn, location->start());
- //when_to_stop += _worst_output_latency + _worst_input_latency;
- replace_event (SessionEvent::PunchOut, when_to_stop);
+ auto_punch_start_changed (location);
+ auto_punch_end_changed (location);
}
/** @param loc A loop location.
samplecnt_t dcl;
auto_loop_declick_range (location, dcp, dcl);
- if (transport_rolling() && play_loop) {
-
- replace_event (SessionEvent::AutoLoopDeclick, dcp, dcl);
+ bool rolling = transport_rolling ();
- // if (_transport_sample > location->end()) {
+ if (rolling && play_loop) {
if (_transport_sample < location->start() || _transport_sample > location->end()) {
// relocate to beginning of loop
clear_events (SessionEvent::LocateRoll);
-
request_locate (location->start(), true);
}
- else if (Config->get_seamless_loop() && !loop_changing) {
-
- // schedule a locate-roll to refill the disk readers at the
- // previous loop end
- loop_changing = true;
-
- if (location->end() > last_loopend) {
- clear_events (SessionEvent::LocateRoll);
- SessionEvent *ev = new SessionEvent (SessionEvent::LocateRoll, SessionEvent::Add, last_loopend, last_loopend, 0, true);
- queue_event (ev);
- }
-
- }
} else {
- clear_events (SessionEvent::AutoLoopDeclick);
clear_events (SessionEvent::AutoLoop);
}
samplepos_t pos;
- if (!transport_rolling() && select_playhead_priority_target (pos)) {
+ if (!rolling && select_playhead_priority_target (pos)) {
if (pos == location->start()) {
request_locate (pos);
}
}
-
last_loopend = location->end();
set_dirty ();
}
if ((existing = _locations->auto_punch_location()) != 0 && existing != location) {
punch_connections.drop_connections();
existing->set_auto_punch (false, this);
- remove_event (existing->start(), SessionEvent::PunchIn);
+ clear_events (SessionEvent::PunchIn);
clear_events (SessionEvent::PunchOut);
auto_punch_location_changed (0);
}
samplepos_t dcp;
samplecnt_t dcl;
auto_loop_declick_range (existing, dcp, dcl);
- remove_event (dcp, SessionEvent::AutoLoopDeclick);
auto_loop_location_changed (0);
}
if (!rt_context) {
remove_pending_capture_state ();
}
- unset_preroll_record_punch ();
}
}
}
if (_transport_speed) {
- if (!config.get_punch_in() && !preroll_record_punch_enabled ()) {
+ if (!config.get_punch_in()) {
enable_record ();
}
} else {
}
}
-void
-Session::reconnect_mtc_ports ()
-{
- boost::shared_ptr<MidiPort> mtc_in_ptr = _midi_ports->mtc_input_port();
-
- if (!mtc_in_ptr) {
- return;
- }
-
- mtc_in_ptr->disconnect_all ();
-
- std::vector<EngineStateController::MidiPortState> midi_port_states;
- EngineStateController::instance()->get_physical_midi_input_states (midi_port_states);
-
- std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
-
- for (; state_iter != midi_port_states.end(); ++state_iter) {
- if (state_iter->available && state_iter->mtc_in) {
- mtc_in_ptr->connect (state_iter->name);
- }
- }
-
- if (!_midi_ports->mtc_input_port ()->connected () &&
- config.get_external_sync () &&
- (Config->get_sync_source () == MTC) ) {
- config.set_external_sync (false);
- }
-
- if ( ARDOUR::Profile->get_trx () ) {
- // Tracks need this signal to update timecode_source_dropdown
- MtcOrLtcInputPortChanged (); //emit signal
- }
-}
-
void
Session::reconnect_mmc_ports(bool inputs)
{
}
/* set this name in the XML description that we are about to use */
-
+#warning fixme -- no more Diskstream
if (pd == CopyPlaylist) {
XMLNode* ds_node = find_named_node (node_copy, "Diskstream");
if (ds_node) {
(*i)->remove_property (X_("bitslot"));
}
else if (type && type->value() == X_("port")) {
- // PortInsert::set_state() handles the bitslot
- (*i)->remove_property (X_("bitslot"));
- (*i)->set_property ("ignore-name", "1");
+ IOProcessor::prepare_for_reset (**i, name);
}
}
}
return rv;
}
+RouteList
+Session::get_routelist (bool mixer_order) const
+{
+ boost::shared_ptr<RouteList> r = routes.reader ();
+ RouteList rv;
+ rv.insert (rv.end(), r->begin(), r->end());
+ rv.sort (Stripable::Sorter (mixer_order));
+ return rv;
+}
+
boost::shared_ptr<RouteList>
Session::get_routes_with_internal_returns() const
{
#ifndef NDEBUG
lua.Print.connect (&_lua_print);
#endif
- lua.tweak_rt_gc ();
lua.sandbox (true);
lua.do_command (
"function ArdourSession ()"
abort(); /*NOTREACHED*/
}
+ lua_mlock (L, 1);
LuaBindings::stddef (L);
LuaBindings::common (L);
LuaBindings::dsp (L);
+ lua_mlock (L, 0);
luabridge::push <Session *> (L, this);
lua_setglobal (L, "Session");
}
resort_routes ();
/* force all diskstreams to update their capture offset values to
- reflect any changes in latencies within the graph.
- */
-
- boost::shared_ptr<RouteList> rl = routes.reader ();
- for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- (*i)->update_signal_latency (true); // XXX
- }
+ * reflect any changes in latencies within the graph.
+ */
+ update_route_latency (false, true);
}
/** @return Number of samples that there is disk space available to write,
}
void
-Session::update_latency (bool playback)
+Session::set_worst_io_latencies_x (IOChange, void *)
+{
+ set_worst_io_latencies ();
+}
+
+void
+Session::send_latency_compensation_change ()
{
+ /* As a result of Send::set_output_latency()
+ * or InternalReturn::set_playback_offset ()
+ * the send's own latency can change (source track
+ * is aligned with target bus).
+ *
+ * This can only happen be triggered by
+ * Route::update_signal_latency ()
+ * when updating the processor latency.
+ *
+ * We need to walk the graph again to take those changes into account
+ * (we should probably recurse or process the graph in a 2 step process).
+ */
+ ++_send_latency_changes;
+}
+
+bool
+Session::update_route_latency (bool playback, bool apply_to_delayline)
+{
+ /* Note: RouteList is process-graph sorted */
+ boost::shared_ptr<RouteList> r = routes.reader ();
+ if (playback) {
+ /* reverse the list so that we work backwards from the last route to run to the first,
+ * this is not needed, but can help to reduce the iterations for aux-sends.
+ */
+ RouteList* rl = routes.reader().get();
+ r.reset (new RouteList (*rl));
+ reverse (r->begin(), r->end());
+ }
+
+ bool changed = false;
+ int bailout = 0;
+restart:
+ _send_latency_changes = 0;
+ _worst_route_latency = 0;
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ // if (!(*i)->active()) { continue ; } // TODO
+ samplecnt_t l;
+ if ((*i)->signal_latency () != (l = (*i)->update_signal_latency (apply_to_delayline))) {
+ changed = true;
+ }
+ _worst_route_latency = std::max (l, _worst_route_latency);
+ }
+
+ if (_send_latency_changes > 0) {
+ // only 1 extra iteration is needed (we allow only 1 level of aux-sends)
+ // BUT.. jack'n'sends'n'bugs
+ if (++bailout < 5) {
+ cerr << "restarting Session::update_latency. # of send changes: " << _send_latency_changes << " iteration: " << bailout << endl;
+ goto restart;
+ }
+ }
+
+ DEBUG_TRACE (DEBUG::Latency, string_compose ("worst signal processing latency: %1 (changed ? %2)\n", _worst_route_latency, (changed ? "yes" : "no")));
+
+ return changed;
+}
+
+void
+Session::update_latency (bool playback)
+{
DEBUG_TRACE (DEBUG::Latency, string_compose ("JACK latency callback: %1\n", (playback ? "PLAYBACK" : "CAPTURE")));
if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _route_deletion_in_progress) {
}
if (playback) {
- post_playback_latency ();
+ set_worst_output_latency ();
+ update_route_latency (true, true);
} else {
- post_capture_latency ();
+ set_worst_input_latency ();
+ update_route_latency (false, false);
}
DEBUG_TRACE (DEBUG::Latency, "JACK latency callback: DONE\n");
}
-void
-Session::post_playback_latency ()
-{
- set_worst_playback_latency ();
-
- boost::shared_ptr<RouteList> r = routes.reader ();
-
- _worst_track_out_latency = 0; // XXX remove me
-
- for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- assert (!(*i)->is_auditioner()); // XXX remove me
- _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ());
- }
-
- for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- if (!(*i)->active()) { continue ; }
- (*i)->apply_latency_compensation ();
- }
-}
-
-void
-Session::post_capture_latency ()
-{
- set_worst_capture_latency ();
-
- /* reflect any changes in capture latencies into capture offsets */
-
- boost::shared_ptr<RouteList> rl = routes.reader();
- for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- (*i)->update_signal_latency ();
- }
-}
-
void
Session::initialize_latencies ()
{
void
Session::set_worst_io_latencies ()
{
- set_worst_playback_latency ();
- set_worst_capture_latency ();
+ set_worst_output_latency ();
+ set_worst_input_latency ();
}
void
-Session::set_worst_playback_latency ()
+Session::set_worst_output_latency ()
{
if (_state_of_the_state & (InitialConnecting|Deletion)) {
return;
}
void
-Session::set_worst_capture_latency ()
+Session::set_worst_input_latency ()
{
if (_state_of_the_state & (InitialConnecting|Deletion)) {
return;
boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (!tr) {
- continue;
- }
_worst_input_latency = max (_worst_input_latency, (*i)->input()->latency());
}
- DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst input latency: %1\n", _worst_input_latency));
+ DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst input latency: %1\n", _worst_input_latency));
}
void
Session::update_latency_compensation (bool force_whole_graph)
{
- // TODO: consolidate
- bool some_track_latency_changed = false;
-
if (_state_of_the_state & (InitialConnecting|Deletion)) {
return;
}
- DEBUG_TRACE(DEBUG::Latency, "---------------------------- update latency compensation\n\n");
-
- _worst_track_latency = 0;
-
- boost::shared_ptr<RouteList> r = routes.reader ();
-
- for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- assert (!(*i)->is_auditioner()); // XXX remove me
- if ((*i)->active()) {
- samplecnt_t tl;
- if ((*i)->signal_latency () != (tl = (*i)->update_signal_latency () /* - (*i)->output()->user_latency()*/)) {
- some_track_latency_changed = true;
- }
- _worst_track_latency = max (tl, _worst_track_latency);
- }
- }
-
- DEBUG_TRACE (DEBUG::Latency, string_compose ("worst signal processing latency: %1 (changed ? %2)\n", _worst_track_latency,
- (some_track_latency_changed ? "yes" : "no")));
-
- DEBUG_TRACE(DEBUG::Latency, "---------------------------- DONE update latency compensation\n\n");
+ bool some_track_latency_changed = update_route_latency (false, false);
if (some_track_latency_changed || force_whole_graph) {
_engine.update_latencies ();
- }
-
- for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- (*i)->update_signal_latency (true);
+ /* above call will ask the backend up update its latencies, which
+ * eventually will trigger AudioEngine::latency_callback () and
+ * call Session::update_latency ()
+ */
+ } else {
+ boost::shared_ptr<RouteList> r = routes.reader ();
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->apply_latency_compensation ();
+ }
}
}
return (find (_current_trans_quarks.begin(), _current_trans_quarks.end(), op) != _current_trans_quarks.end());
}
-boost::shared_ptr<Port>
-Session::ltc_input_port () const
-{
- assert (_ltc_input);
- return _ltc_input->nth (0);
-}
-
boost::shared_ptr<Port>
Session::ltc_output_port () const
{
return _ltc_output ? _ltc_output->nth (0) : boost::shared_ptr<Port> ();
}
-void
-Session::reconnect_ltc_input ()
-{
- if (_ltc_input) {
-
- string src = Config->get_ltc_source_port();
-
- _ltc_input->disconnect (this);
-
- if (src != _("None") && !src.empty()) {
- _ltc_input->nth (0)->connect (src);
- }
-
- if ( ARDOUR::Profile->get_trx () ) {
- // Tracks need this signal to update timecode_source_dropdown
- MtcOrLtcInputPortChanged (); //emit signal
- }
- }
-}
-
void
Session::reconnect_ltc_output ()
{
* modifies the capture-offset, which can be a problem.
*/
while (g_atomic_int_and (&_latency_recompute_pending, 0)) {
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
update_latency_compensation ();
}
}