X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;ds=inline;f=libs%2Fardour%2Fsession_transport.cc;h=ace5e8e3e1ff41785b4a386a0d28528645b988eb;hb=7a76e8ae96c20b270dd3104328f8bee199c9a770;hp=f06e34849b8ad7974e4a6ec721926dc6908ca1c7;hpb=8849cb428741d7d60261d1e44ceec665912281f5;p=ardour.git diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index f06e34849b..ace5e8e3e1 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -17,14 +17,13 @@ */ -#include -#include -#include - #ifdef WAF_BUILD #include "libardour-config.h" #endif +#include +#include +#include #include "pbd/undo.h" #include "pbd/error.h" @@ -36,10 +35,10 @@ #include "midi++/port.h" #include "midi++/manager.h" -#include "ardour/ardour.h" #include "ardour/audioengine.h" #include "ardour/auditioner.h" #include "ardour/butler.h" +#include "ardour/click.h" #include "ardour/debug.h" #include "ardour/location.h" #include "ardour/session.h" @@ -196,7 +195,7 @@ Session::request_play_range (list* range, bool leave_rolling) void Session::realtime_stop (bool abort, bool clear_state) { - DEBUG_TRACE (DEBUG::Transport, "realtime stop\n"); + DEBUG_TRACE (DEBUG::Transport, string_compose ("realtime stop @ %1\n", _transport_frame)); PostTransportWork todo = PostTransportWork (0); /* assume that when we start, we'll be moving forwards */ @@ -222,14 +221,14 @@ Session::realtime_stop (bool abort, bool clear_state) past that point to pick up delayed input (and/or to delick) */ - if (worst_playback_latency() > current_block_size) { - /* we rolled past the stop point to pick up data that had - not yet arrived. move back to where the stop occured. - */ - decrement_transport_position (current_block_size + (worst_playback_latency() - current_block_size)); - } else { - decrement_transport_position (current_block_size); - } + if (worst_playback_latency() > current_block_size) { + /* we rolled past the stop point to pick up data that had + not yet arrived. move back to where the stop occured. + */ + decrement_transport_position (current_block_size + (worst_input_latency() - current_block_size)); + } else { + decrement_transport_position (current_block_size); + } /* the duration change is not guaranteed to have happened, but is likely */ @@ -252,9 +251,9 @@ Session::realtime_stop (bool abort, bool clear_state) _clear_event_type (SessionEvent::RangeStop); _clear_event_type (SessionEvent::RangeLocate); - /* if we're going to clear loop state, then force disabling record BUT only if we're not doing latched rec-enable */ + /* if we're going to clear loop state, then force disabling record BUT only if we're not doing latched rec-enable */ disable_record (true, (!Config->get_latched_record_enable() && clear_state)); - + reset_slave_state (); _transport_speed = 0; @@ -270,6 +269,15 @@ Session::realtime_stop (bool abort, bool clear_state) transport_sub_state = 0; } +void +Session::realtime_locate () +{ + boost::shared_ptr r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->realtime_locate (); + } +} + void Session::butler_transport_work () { @@ -284,26 +292,26 @@ Session::butler_transport_work () DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = %1\n", enum_2_string (ptw))); - if (ptw & PostTransportAdjustPlaybackBuffering) { + if (ptw & PostTransportAdjustPlaybackBuffering) { for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); if (tr) { tr->adjust_playback_buffering (); - /* and refill those buffers ... */ - tr->non_realtime_locate (_transport_frame); + /* and refill those buffers ... */ + tr->non_realtime_locate (_transport_frame); } } - - } - if (ptw & PostTransportAdjustCaptureBuffering) { + } + + if (ptw & PostTransportAdjustCaptureBuffering) { for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); if (tr) { tr->adjust_capture_buffering (); } } - } + } if (ptw & PostTransportCurveRealloc) { for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { @@ -416,6 +424,12 @@ Session::non_realtime_locate () tr->non_realtime_locate (_transport_frame); } } + + /* XXX: it would be nice to generate the new clicks here (in the non-RT thread) + rather than clearing them so that the RT thread has to spend time constructing + them (in Session::click). + */ + clear_clicks (); } @@ -449,7 +463,6 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) auditioner->cancel_audition (); } - clear_clicks(); cumulative_rf_motion = 0; reset_rf_scale (0); @@ -460,11 +473,11 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: DS stop\n")); - if (abort && did_record) { - /* no reason to save the session file when we remove sources - */ - _state_of_the_state = StateOfTheState (_state_of_the_state|InCleanup); - } + if (abort && did_record) { + /* no reason to save the session file when we remove sources + */ + _state_of_the_state = StateOfTheState (_state_of_the_state|InCleanup); + } for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); @@ -473,9 +486,9 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) } } - if (abort && did_record) { - _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup); - } + if (abort && did_record) { + _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup); + } boost::shared_ptr r = routes.reader (); @@ -490,7 +503,11 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) } if (_engine.running()) { - update_latency_compensation (true, abort); + PostTransportWork ptw = post_transport_work (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->nonrealtime_handle_transport_stopped (abort, (ptw & PostTransportLocate), (!(ptw & PostTransportLocate) || pending_locate_flush)); + } + update_latency_compensation (); } bool const auto_return_enabled = @@ -508,14 +525,14 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) if ((auto_return_enabled || synced_to_jack() || _requested_return_frame >= 0) && !(ptw & PostTransportLocate)) { - /* no explicit locate queued */ + /* no explicit locate queued */ bool do_locate = false; if (_requested_return_frame >= 0) { /* explicit return request pre-queued in event list. overrides everything else */ - + cerr << "explicit auto-return to " << _requested_return_frame << endl; _transport_frame = _requested_return_frame; @@ -531,7 +548,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) if (!synced_to_jack()) { Location *location = _locations->auto_loop_location(); - + if (location != 0) { _transport_frame = location->start(); } else { @@ -550,24 +567,26 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) } } else { - + /* regular auto-return */ - + _transport_frame = _last_roll_location; do_locate = true; } - } + } } - _requested_return_frame = -1; + _requested_return_frame = -1; if (do_locate) { _engine.transport_locate (_transport_frame); } - } + } } + clear_clicks(); + /* do this before seeking, because otherwise the tracks will do the wrong thing in seamless loop mode. */ @@ -595,12 +614,14 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) have_looped = false; - send_full_time_code (_transport_frame); - - if (!dynamic_cast(_slave)) { - MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop)); - send_mmc_locate (_transport_frame); - } + if (!_engine.freewheeling()) { + send_full_time_code (_transport_frame); + + if (!dynamic_cast(_slave)) { + MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop)); + send_mmc_locate (_transport_frame); + } + } if ((ptw & PostTransportLocate) && get_record_enabled()) { /* capture start has been changed, so save pending state */ @@ -608,9 +629,9 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) saved = true; } - /* always try to get rid of this */ + /* always try to get rid of this */ - remove_pending_capture_state (); + remove_pending_capture_state (); /* save the current state of things if appropriate */ @@ -641,7 +662,7 @@ Session::check_declick_out () /* this is called after a process() iteration. if PendingDeclickOut was set, it means that we were waiting to declick the output (which has just been - done) before doing something else. this is where we do that "something else". + done) before maybe doing something else. this is where we do that "something else". note: called from the audio thread. */ @@ -655,6 +676,10 @@ Session::check_declick_out () stop_transport (pending_abort); transport_sub_state &= ~(PendingDeclickOut|PendingLocate); } + + } else if (transport_sub_state & PendingLoopDeclickOut) { + /* Nothing else to do here; we've declicked, and the loop event will be along shortly */ + transport_sub_state &= ~PendingLoopDeclickOut; } } @@ -663,7 +688,8 @@ Session::unset_play_loop () { play_loop = false; clear_events (SessionEvent::AutoLoop); - + clear_events (SessionEvent::AutoLoopDeclick); + // set all tracks to NOT use internal looping boost::shared_ptr rl = routes.reader (); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { @@ -685,14 +711,15 @@ Session::set_play_loop (bool yn) /* nothing to do, or can't change loop status while recording */ return; } - + if (yn && Config->get_seamless_loop() && synced_to_jack()) { - warning << string_compose (_("Seamless looping cannot be supported while %1 is using JACK transport.\n" - "Recommend changing the configured options"), PROGRAM_NAME) + warning << string_compose ( + _("Seamless looping cannot be supported while %1 is using JACK transport.\n" + "Recommend changing the configured options"), PROGRAM_NAME) << endmsg; return; } - + if (yn) { play_loop = true; @@ -721,13 +748,19 @@ Session::set_play_loop (bool yn) } } } - - /* put the loop event into the event list */ - - SessionEvent* event = new SessionEvent (SessionEvent::AutoLoop, SessionEvent::Replace, loc->end(), loc->start(), 0.0f); - merge_event (event); - /* locate to start of loop and roll. If doing seamless loop, force a + /* Put the delick and loop events in into the event list. The declick event will + cause a de-clicking fade-out just before the end of the loop, and it will also result + in a fade-in when the loop restarts. The AutoLoop event will peform the actual loop. + */ + + framepos_t dcp; + framecnt_t dcl; + auto_loop_declick_range (loc, dcp, dcl); + merge_event (new SessionEvent (SessionEvent::AutoLoopDeclick, SessionEvent::Replace, dcp, dcl, 0.0f)); + merge_event (new SessionEvent (SessionEvent::AutoLoop, SessionEvent::Replace, loc->end(), loc->start(), 0.0f)); + + /* locate to start of loop and roll. If doing seamless loop, force a locate+buffer refill even if we are positioned there already. */ @@ -819,8 +852,11 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool return; } - if (_transport_speed) { - /* schedule a declick. we'll be called again when its done */ + if (_transport_speed && !with_loop) { + /* Schedule a declick. We'll be called again when its done. + We only do it this way for ordinary locates, not those + due to loops. + */ if (!(transport_sub_state & PendingDeclickOut)) { transport_sub_state |= (PendingDeclickOut|PendingLocate); @@ -838,8 +874,20 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool outbound_mtc_timecode_frame = _transport_frame; next_quarter_frame_to_send = 0; + /* do "stopped" stuff if: + * + * we are rolling AND + * no autoplay in effect AND + * we're not going to keep rolling after the locate AND + * !(playing a loop with JACK sync) + * + */ + if (transport_rolling() && (!auto_play_legal || !config.get_auto_play()) && !with_roll && !(synced_to_jack() && play_loop)) { realtime_stop (false, true); // XXX paul - check if the 2nd arg is really correct + } else { + /* otherwise tell the world that we located */ + realtime_locate (); } if (force || !with_loop || loop_changing) { @@ -848,7 +896,7 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool if (with_roll) { todo = PostTransportWork (todo | PostTransportRoll); - } + } add_post_transport_work (todo); _butler->schedule_transport_work (); @@ -872,12 +920,12 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool if (with_roll) { /* switch from input if we're going to roll */ if (Config->get_monitoring_model() == HardwareMonitoring) { - set_track_monitor_input_status (!config.get_auto_input()); + set_track_monitor_input_status (!config.get_auto_input()); } } else { /* otherwise we're going to stop, so do the opposite */ if (Config->get_monitoring_model() == HardwareMonitoring) { - set_track_monitor_input_status (true); + set_track_monitor_input_status (true); } } @@ -919,18 +967,24 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool } /** Set the transport speed. - * @param speed New speed - * @param abort + * Called from the process thread. + * @param speed New speed */ void Session::set_transport_speed (double speed, bool abort, bool clear_state) { - DEBUG_TRACE (DEBUG::Transport, string_compose ("Set transport speed to %1, abort = %2 clear_state = %3, current = %4\n", speed, abort, clear_state, _transport_speed)); + DEBUG_TRACE (DEBUG::Transport, string_compose ("@ %5 Set transport speed to %1, abort = %2 clear_state = %3, current = %4\n", + speed, abort, clear_state, _transport_speed, _transport_frame)); if (_transport_speed == speed) { return; } + if (actively_recording() && speed != 1.0 && speed != 0.0) { + /* no varispeed during recording */ + return; + } + _target_transport_speed = fabs(speed); /* 8.0 max speed is somewhat arbitrary but based on guestimates regarding disk i/o capability @@ -948,12 +1002,12 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) /* we are rolling and we want to stop */ if (Config->get_monitoring_model() == HardwareMonitoring) { - set_track_monitor_input_status (true); + set_track_monitor_input_status (true); } if (synced_to_jack ()) { if (clear_state) { - /* do this here because our response to the slave won't + /* do this here because our response to the slave won't take care of it. */ _play_range = false; @@ -963,7 +1017,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) } else { stop_transport (abort); } - + unset_play_loop (); } else if (transport_stopped() && speed == 1.0) { @@ -971,7 +1025,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) /* we are stopped and we want to start rolling at speed 1 */ if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { - set_track_monitor_input_status (false); + set_track_monitor_input_status (false); } if (synced_to_jack()) { @@ -982,9 +1036,12 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) } else { + /* not zero, not 1.0 ... varispeed */ + if ((synced_to_jack()) && speed != 0.0 && speed != 1.0) { - warning << string_compose (_("Global varispeed cannot be supported while %1 is connected to JACK transport control"), - PROGRAM_NAME) + warning << string_compose ( + _("Global varispeed cannot be supported while %1 is connected to JACK transport control"), + PROGRAM_NAME) << endmsg; return; } @@ -1006,7 +1063,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) /* if we are reversing relative to the current speed, or relative to the speed before the last stop, then we have to do extra work. */ - + PostTransportWork todo = PostTransportWork (0); if ((_transport_speed && speed * _transport_speed < 0.0) || (_last_transport_speed * speed < 0.0) || (_last_transport_speed == 0.0f && speed < 0.0f)) { @@ -1029,6 +1086,8 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) add_post_transport_work (todo); _butler->schedule_transport_work (); } + + TransportStateChange (); /* EMIT SIGNAL */ } } @@ -1041,7 +1100,7 @@ Session::stop_transport (bool abort, bool clear_state) return; } - if (actively_recording() && !(transport_sub_state & StopPendingCapture) && worst_playback_latency() > current_block_size) { + if (actively_recording() && !(transport_sub_state & StopPendingCapture) && worst_input_latency() > current_block_size) { boost::shared_ptr rl = routes.reader(); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { @@ -1049,7 +1108,7 @@ Session::stop_transport (bool abort, bool clear_state) if (tr) { tr->prepare_to_stop (_transport_frame); } - } + } /* we need to capture the audio that has still not yet been received by the system at the time the stop is requested, so we have to roll past that time. @@ -1059,9 +1118,14 @@ Session::stop_transport (bool abort, bool clear_state) and then we'll really be stopped. */ + DEBUG_TRACE (DEBUG::Transport, string_compose ("stop transport requested @ %1, scheduled for + %2 - %3 = %4, abort = %5\n", + _transport_frame, _worst_input_latency, current_block_size, + _transport_frame - _worst_input_latency - current_block_size, + abort)); + SessionEvent *ev = new SessionEvent (SessionEvent::StopOnce, SessionEvent::Replace, - _transport_frame + _worst_input_latency - current_block_size, - 0, 0, abort); + _transport_frame + _worst_input_latency - current_block_size, + 0, 0, abort); merge_event (ev); transport_sub_state |= StopPendingCapture; @@ -1069,19 +1133,18 @@ Session::stop_transport (bool abort, bool clear_state) return; } - if ((transport_sub_state & PendingDeclickOut) == 0) { - if (!(transport_sub_state & StopPendingCapture)) { + if (!(transport_sub_state & StopPendingCapture)) { boost::shared_ptr rl = routes.reader(); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); if (tr) { tr->prepare_to_stop (_transport_frame); } - } - } - + } + } + transport_sub_state |= PendingDeclickOut; /* we'll be called again after the declick */ pending_abort = abort; @@ -1092,12 +1155,15 @@ Session::stop_transport (bool abort, bool clear_state) _butler->schedule_transport_work (); } +/** Called from the process thread */ void Session::start_transport () { + DEBUG_TRACE (DEBUG::Transport, "start_transport\n"); + _last_roll_location = _transport_frame; _last_roll_or_reversal_location = _transport_frame; - + have_looped = false; /* if record status is Enabled, move it to Recording. if its @@ -1132,14 +1198,16 @@ Session::start_transport () if (tr) { tr->realtime_set_speed (tr->speed(), true); } - (*i)->automation_snapshot (_transport_frame, true); + (*i)->automation_snapshot (_transport_frame, true); } - Timecode::Time time; - timecode_time_subframes (_transport_frame, time); - if (!dynamic_cast(_slave)) { - MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay)); - } + if (!_engine.freewheeling()) { + Timecode::Time time; + timecode_time_subframes (_transport_frame, time); + if (!dynamic_cast(_slave)) { + MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay)); + } + } TransportStateChange (); /* EMIT SIGNAL */ } @@ -1214,6 +1282,8 @@ Session::use_sync_source (Slave* new_slave) delete _slave; _slave = new_slave; + send_full_time_code (_transport_frame); + boost::shared_ptr rl = routes.reader(); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); @@ -1269,7 +1339,7 @@ Session::switch_to_sync_source (SyncSource src) try { new_slave = new MIDIClock_Slave (*this, *MIDI::Manager::instance()->midi_clock_input_port(), 24); } - + catch (failed_constructor& err) { return; } @@ -1280,13 +1350,13 @@ Session::switch_to_sync_source (SyncSource src) return; } - if (config.get_video_pullup() != 0.0f) { - return; - } + if (config.get_video_pullup() != 0.0f) { + return; + } new_slave = new JACK_Slave (_engine.jack()); break; - + default: new_slave = 0; break; @@ -1295,13 +1365,6 @@ Session::switch_to_sync_source (SyncSource src) request_sync_source (new_slave); } -void -Session::reverse_track_buffers () -{ - add_post_transport_work (PostTransportReverse); - _butler->schedule_transport_work (); -} - void Session::set_track_speed (Track* track, double speed) { @@ -1328,7 +1391,7 @@ Session::set_play_range (list& range, bool leave_rolling) /* Called from event-processing context */ unset_play_range (); - + if (range.empty()) { /* _play_range set to false in unset_play_range() */ @@ -1346,45 +1409,45 @@ Session::set_play_range (list& range, bool leave_rolling) unset_play_loop (); list::size_type sz = range.size(); - + if (sz > 1) { - - list::iterator i = range.begin(); + + list::iterator i = range.begin(); list::iterator next; - + while (i != range.end()) { - + next = i; ++next; - + /* locating/stopping is subject to delays for declicking. */ - + framepos_t requested_frame = i->end; - + if (requested_frame > current_block_size) { requested_frame -= current_block_size; } else { requested_frame = 0; } - + if (next == range.end()) { ev = new SessionEvent (SessionEvent::RangeStop, SessionEvent::Add, requested_frame, 0, 0.0f); } else { ev = new SessionEvent (SessionEvent::RangeLocate, SessionEvent::Add, requested_frame, (*next).start, 0.0f); } - + merge_event (ev); - + i = next; } - + } else if (sz == 1) { ev = new SessionEvent (SessionEvent::RangeStop, SessionEvent::Add, range.front().end, 0, 0.0f); merge_event (ev); - - } + + } /* save range so we can do auto-return etc. */ @@ -1394,7 +1457,7 @@ Session::set_play_range (list& range, bool leave_rolling) ev = new SessionEvent (SessionEvent::LocateRoll, SessionEvent::Add, SessionEvent::Immediate, range.front().start, 0.0f, false); merge_event (ev); - + TransportStateChange (); } @@ -1427,9 +1490,11 @@ Session::engine_halted () the picture. */ - g_atomic_int_set (&_butler->should_do_transport_work, 0); - set_post_transport_work (PostTransportWork (0)); - _butler->stop (); + if (_butler) { + g_atomic_int_set (&_butler->should_do_transport_work, 0); + set_post_transport_work (PostTransportWork (0)); + _butler->stop (); + } realtime_stop (false, true); non_realtime_stop (false, 0, ignored); @@ -1442,7 +1507,7 @@ Session::engine_halted () void Session::xrun_recovery () { - Xrun (_transport_frame); //EMIT SIGNAL + Xrun (_transport_frame); /* EMIT SIGNAL */ if (Config->get_stop_recording_on_xrun() && actively_recording()) { @@ -1457,75 +1522,18 @@ Session::xrun_recovery () void Session::route_processors_changed (RouteProcessorChange c) { - if (c.type == RouteProcessorChange::MeterPointChange) { + if (ignore_route_processor_changes) { return; } - update_latency_compensation (false, false); - resort_routes (); -} - -void -Session::update_latency_compensation (bool with_stop, bool abort) -{ - bool update_jack = false; - PostTransportWork ptw; - - if (_state_of_the_state & Deletion) { + if (c.type == RouteProcessorChange::MeterPointChange) { return; } - _worst_track_latency = 0; - ptw = post_transport_work(); - - DEBUG_TRACE(DEBUG::Latency, "---------------------------- update latency\n\n") - - boost::shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - - if (with_stop) { - (*i)->nonrealtime_handle_transport_stopped (abort, (ptw & PostTransportLocate), (!(ptw & PostTransportLocate) || pending_locate_flush)); - } - - framecnt_t old_latency = (*i)->output()->signal_latency (); - framecnt_t track_latency = (*i)->update_total_latency (); - - if (old_latency != track_latency) { -#ifndef HAVE_JACK_NEW_LATENCY - (*i)->input()->update_port_total_latencies (); - (*i)->output()->update_port_total_latencies (); -#endif - update_jack = true; - } - - if (!(*i)->is_hidden() && ((*i)->active())) { - _worst_track_latency = max (_worst_track_latency, track_latency); - } - } - - if (update_jack) { - _engine.update_total_latencies (); - } - - DEBUG_TRACE(DEBUG::Latency, string_compose("worst case route internal latency was %1\n", _worst_track_latency)); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - (*i)->set_latency_delay (_worst_track_latency); - } - - set_worst_io_latencies (); + update_latency_compensation (); + resort_routes (); - /* reflect any changes in latencies into capture offsets - */ - - boost::shared_ptr rl = routes.reader(); - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr) { - tr->set_capture_offset (); - } - } + set_dirty (); } void @@ -1547,23 +1555,25 @@ Session::reset_jack_connection (jack_client_t* jack) bool Session::maybe_stop (framepos_t limit) { - if ((_transport_speed > 0.0f && _transport_frame >= limit) || (_transport_speed < 0.0f && _transport_frame == 0)) { - if (synced_to_jack () && config.get_jack_time_master ()) { - _engine.transport_stop (); - } else if (!synced_to_jack ()) { - stop_transport (); - } - return true; - } - return false; + if ((_transport_speed > 0.0f && _transport_frame >= limit) || (_transport_speed < 0.0f && _transport_frame == 0)) { + if (synced_to_jack () && config.get_jack_time_master ()) { + _engine.transport_stop (); + } else if (!synced_to_jack ()) { + stop_transport (); + } + return true; + } + return false; } void Session::send_mmc_locate (framepos_t t) { - Timecode::Time time; - timecode_time_subframes (t, time); - MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (time)); + if (!_engine.freewheeling()) { + Timecode::Time time; + timecode_time_subframes (t, time); + MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (time)); + } } /** Ask the transport to not send timecode until further notice. The suspension