bool finished;
PostTransportWork ptw;
boost::shared_ptr<RouteList> r = routes.reader ();
-
+ uint64_t before;
+
int on_entry = g_atomic_int_get (&_butler->should_do_transport_work);
finished = true;
ptw = post_transport_work();
- DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = %1\n", enum_2_string (ptw)));
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = %1 at %2\n", enum_2_string (ptw), (before = g_get_monotonic_time())));
if (ptw & PostTransportAdjustPlaybackBuffering) {
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
g_atomic_int_dec_and_test (&_butler->should_do_transport_work);
- DEBUG_TRACE (DEBUG::Transport, X_("Butler transport work all done\n"));
+ DEBUG_TRACE (DEBUG::Transport, string_compose (X_("Butler transport work all done after %1 usecs\n"), g_get_monotonic_time() - before));
DEBUG_TRACE (DEBUG::Transport, X_(string_compose ("Frame %1\n", _transport_frame)));
}
clear_clicks ();
}
+bool
+Session::select_playhead_priority_target (framepos_t& jump_to)
+{
+ jump_to = -1;
+
+ AutoReturnTarget autoreturn = Config->get_auto_return_target_list ();
+
+ if (!autoreturn) {
+ return false;
+ }
+
+ if (Profile->get_trx() && transport_rolling() ) {
+ // We're playing, so do nothing.
+ // Next stop will put us where we need to be.
+ return false;
+ }
+
+ /* Note that the order of checking each AutoReturnTarget flag defines
+ the priority each flag.
+
+ Ardour/Mixbus: Last Locate
+ Range Selection
+ Loop Range
+ Region Selection
+
+ Tracks: Range Selection
+ Loop Range
+ Region Selection
+ Last Locate
+ */
+
+#ifndef USE_TRACKS_CODE_FEATURES
+ if (autoreturn & LastLocate) {
+ jump_to = _last_roll_location;
+ }
+
+ if (jump_to < 0 && (autoreturn & RangeSelectionStart)) {
+#else
+ if (autoreturn & RangeSelectionStart) {
+#endif
+ if (!_range_selection.empty()) {
+ jump_to = _range_selection.from;
+ } else {
+ if (transport_rolling ()) {
+ /* Range selection no longer exists, but we're playing,
+ so do nothing. Next stop will put us where
+ we need to be.
+ */
+ return false;
+ }
+ }
+ }
+
+ if (jump_to < 0 && (autoreturn & Loop)) {
+ /* don't try to handle loop play when synced to JACK */
+
+ if (!synced_to_engine()) {
+ Location *location = _locations->auto_loop_location();
+
+ if (location) {
+ jump_to = location->start();
+
+ if (Config->get_seamless_loop()) {
+ /* need to get track buffers reloaded */
+ set_track_loop (true);
+ }
+ }
+ }
+ }
+
+ if (jump_to < 0 && (autoreturn & RegionSelectionStart)) {
+ if (!_object_selection.empty()) {
+ jump_to = _object_selection.from;
+ }
+ }
+
+#ifdef USE_TRACKS_CODE_FEATURES
+ if (jump_to < 0 && (autoreturn & LastLocate)) {
+ jump_to = _last_roll_location;
+ }
+#endif
+
+ return jump_to >= 0;
+}
+
+void
+Session::follow_playhead_priority ()
+{
+ framepos_t target;
+
+ if (select_playhead_priority_target (target)) {
+ request_locate (target);
+ }
+}
+
void
Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
{
update_latency_compensation ();
}
- bool const auto_return_enabled =
- (!config.get_external_sync() && (config.get_auto_return() || abort));
+ bool const auto_return_enabled = (!config.get_external_sync() && (Config->get_auto_return_target_list() || abort));
if (auto_return_enabled ||
(ptw & PostTransportLocate) ||
do_locate = true;
} else {
- if (config.get_auto_return()) {
-
- if (play_loop) {
-
- /* don't try to handle loop play when synced to JACK */
-
- if (!synced_to_engine()) {
-
- Location *location = _locations->auto_loop_location();
+ framepos_t jump_to;
- if (location != 0) {
- _transport_frame = location->start();
- } else {
- _transport_frame = _last_roll_location;
- }
- do_locate = true;
- }
-
- } else if (_play_range) {
-
- /* return to start of range */
-
- if (!current_audio_range.empty()) {
- _transport_frame = current_audio_range.front().start;
- do_locate = true;
- }
+ if (select_playhead_priority_target (jump_to)) {
- } else {
-
- /* regular auto-return */
+ _transport_frame = jump_to;
+ do_locate = true;
- _transport_frame = _last_roll_location;
- do_locate = true;
- }
} else if (abort) {
_transport_frame = _last_roll_location;
void
Session::unset_play_loop ()
{
- play_loop = false;
- clear_events (SessionEvent::AutoLoop);
- clear_events (SessionEvent::AutoLoopDeclick);
- set_track_loop (false);
-
- if (Config->get_seamless_loop()) {
- /* likely need to flush track buffers: this will locate us to wherever we are */
- add_post_transport_work (PostTransportLocate);
- _butler->schedule_transport_work ();
+ if (play_loop) {
+ play_loop = false;
+ clear_events (SessionEvent::AutoLoop);
+ clear_events (SessionEvent::AutoLoopDeclick);
+ set_track_loop (false);
+
+
+ if (Config->get_seamless_loop()) {
+ /* likely need to flush track buffers: this will locate us to wherever we are */
+ add_post_transport_work (PostTransportLocate);
+ _butler->schedule_transport_work ();
+ }
}
}
*/
bool transport_was_stopped = !transport_rolling();
-
- if (transport_was_stopped && (!auto_play_legal || !config.get_auto_play()) && !with_roll && !(synced_to_engine() && play_loop)) {
+
+ if (transport_was_stopped && (!auto_play_legal || !config.get_auto_play()) && !with_roll && !(synced_to_engine() && play_loop) &&
+ (!Profile->get_trx() || !(config.get_external_sync() && !synced_to_engine()))) {
realtime_stop (false, true); // XXX paul - check if the 2nd arg is really correct
transport_was_stopped = true;
} else {
}
_engine.transport_stop ();
} else {
- bool const auto_return_enabled = (!config.get_external_sync() && (config.get_auto_return() || abort));
+ bool const auto_return_enabled = (!config.get_external_sync() && (Config->get_auto_return_target_list() || abort));
if (!auto_return_enabled) {
_requested_return_frame = destination_frame;
}
DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC3 with speed = %1\n", _transport_speed));
- TransportStateChange (); /* EMIT SIGNAL */
+
+ /* throttle signal emissions.
+ * when slaved [_last]_transport_speed
+ * usually changes every cycle (tiny amounts due to DLL).
+ * Emitting a signal every cycle is overkill and unwarranted.
+ *
+ * Using _last_transport_speed is not acceptable,
+ * since it allows for large changes over a long period
+ * of time. Hence we introduce a dedicated variable to keep track
+ *
+ * The 0.2% dead-zone is somewhat arbitrary. Main use-case
+ * for TransportStateChange() here is the ShuttleControl display.
+ */
+ if (fabsf(_signalled_varispeed - speed) > .002f
+ // still, signal hard changes to 1.0 and 0.0:
+ || ( speed == 1.f && _signalled_varispeed != 1.f)
+ || ( speed == 0.f && _signalled_varispeed != 0.f)
+ )
+ {
+ TransportStateChange (); /* EMIT SIGNAL */
+ _signalled_varispeed = speed;
+ }
}
}
}
}
+void
+Session::mtc_status_changed (bool yn)
+{
+ g_atomic_int_set (&_mtc_active, yn);
+ MTCSyncStateChanged( yn );
+}
+
void
Session::use_sync_source (Slave* new_slave)
{
delete _slave;
_slave = new_slave;
+ MTC_Slave* mtc_slave = dynamic_cast<MTC_Slave*>(_slave);
+ if (mtc_slave) {
+ mtc_slave->ActiveChanged.connect_same_thread (mtc_status_connection, boost::bind (&Session::mtc_status_changed, this, _1));
+ MTCSyncStateChanged(mtc_slave->locked() );
+ } else {
+ if (g_atomic_int_get (&_mtc_active) ){
+ g_atomic_int_set (&_mtc_active, 0);
+ MTCSyncStateChanged( false );
+ }
+ mtc_status_connection.disconnect ();
+ }
+
DEBUG_TRACE (DEBUG::Slave, string_compose ("set new slave to %1\n", _slave));
// need to queue this for next process() cycle
*/
if (_butler) {
- g_atomic_int_set (&_butler->should_do_transport_work, 0);
- set_post_transport_work (PostTransportWork (0));
_butler->stop ();
}
void
Session::xrun_recovery ()
{
+ ++_xrun_count;
+
Xrun (_transport_frame); /* EMIT SIGNAL */
if (Config->get_stop_recording_on_xrun() && actively_recording()) {
}
if (c.type == RouteProcessorChange::MeterPointChange) {
+ set_dirty ();
+ return;
+ }
+
+ if (c.type == RouteProcessorChange::RealTimeChange) {
+ set_dirty ();
return;
}