summaryrefslogtreecommitdiff
path: root/libs/ardour/session_transport.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libs/ardour/session_transport.cc')
-rw-r--r--libs/ardour/session_transport.cc226
1 files changed, 141 insertions, 85 deletions
diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc
index 0c424704bf..3ab5c64f6e 100644
--- a/libs/ardour/session_transport.cc
+++ b/libs/ardour/session_transport.cc
@@ -32,12 +32,15 @@
#include <cerrno>
#include <unistd.h>
-#include "pbd/undo.h"
+#include <boost/algorithm/string/erase.hpp>
+
#include "pbd/error.h"
#include "pbd/enumwriter.h"
-#include "pbd/pthread_utils.h"
+#include "pbd/i18n.h"
#include "pbd/memento_command.h"
+#include "pbd/pthread_utils.h"
#include "pbd/stacktrace.h"
+#include "pbd/undo.h"
#include "midi++/mmc.h"
#include "midi++/port.h"
@@ -54,6 +57,7 @@
#include "ardour/profile.h"
#include "ardour/scene_changer.h"
#include "ardour/session.h"
+#include "ardour/transport_fsm.h"
#include "ardour/transport_master.h"
#include "ardour/transport_master_manager.h"
#include "ardour/tempo.h"
@@ -61,8 +65,6 @@
#include "ardour/vca.h"
#include "ardour/vca_manager.h"
-#include "pbd/i18n.h"
-
using namespace std;
using namespace ARDOUR;
using namespace PBD;
@@ -80,8 +82,10 @@ using namespace PBD;
#endif
+#define TFSM_EVENT(ev) { DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("TFSM(%1)\n", typeid(ev).name())); _transport_fsm->enqueue (ev); }
+
/* *****************************************************************************
- * REALTIME ACTIONS (to be called on state transtion
+ * REALTIME ACTIONS (to be called on state transitions)
* ****************************************************************************/
void
@@ -89,14 +93,13 @@ Session::realtime_stop (bool abort, bool clear_state)
{
ENSURE_PROCESS_THREAD;
- DEBUG_TRACE (DEBUG::Transport, string_compose ("realtime stop @ %1\n", _transport_sample));
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("realtime stop @ %1 speed = %2\n", _transport_sample, _transport_speed));
PostTransportWork todo = PostTransportWork (0);
- /* assume that when we start, we'll be moving forwards */
-
- if (_transport_speed < 0.0f) {
+ if (_last_transport_speed < 0.0f) {
todo = (PostTransportWork (todo | PostTransportStop | PostTransportReverse));
_default_transport_speed = 1.0;
+ DiskReader::inc_no_disk_output (); // for the buffer reversal
} else {
todo = PostTransportWork (todo | PostTransportStop);
}
@@ -127,7 +130,6 @@ Session::realtime_stop (bool abort, bool clear_state)
add_post_transport_work (todo);
}
- _clear_event_type (SessionEvent::StopOnce);
_clear_event_type (SessionEvent::RangeStop);
_clear_event_type (SessionEvent::RangeLocate);
@@ -156,22 +158,13 @@ Session::realtime_stop (bool abort, bool clear_state)
waiting_for_sync_offset = true;
}
- transport_sub_state = 0;
-}
-
-void
-Session::realtime_locate ()
-{
- ENSURE_PROCESS_THREAD;
-
- boost::shared_ptr<RouteList> r = routes.reader ();
- for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- (*i)->realtime_locate ();
+ if (todo) {
+ TFSM_EVENT (TransportFSM::butler_required());
}
}
void
-Session::start_locate (samplepos_t target_sample, bool with_roll, bool with_flush, bool for_loop_enabled, bool force)
+Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, bool for_loop_enabled, bool force, bool with_mmc)
{
ENSURE_PROCESS_THREAD;
@@ -195,7 +188,7 @@ Session::start_locate (samplepos_t target_sample, bool with_roll, bool with_flus
will use the incorrect _transport_sample and report an old
and incorrect time to Jack transport
*/
- locate (target_sample, with_roll, with_flush, for_loop_enabled, force);
+ do_locate (target_sample, with_roll, with_flush, for_loop_enabled, force, with_mmc);
}
/* tell JACK to change transport position, and we will
@@ -211,13 +204,13 @@ Session::start_locate (samplepos_t target_sample, bool with_roll, bool with_flus
}
} else {
- locate (target_sample, with_roll, with_flush, for_loop_enabled, force);
+ do_locate (target_sample, with_roll, with_flush, for_loop_enabled, force, with_mmc);
}
}
/** @param with_mmc true to send a MMC locate command when the locate is done */
void
-Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, bool for_loop_enabled, bool force, bool with_mmc)
+Session::do_locate (samplepos_t target_sample, bool with_roll, bool with_flush, bool for_loop_enabled, bool force, bool with_mmc)
{
ENSURE_PROCESS_THREAD;
@@ -246,12 +239,11 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, boo
set_transport_speed (1.0, 0, false);
}
loop_changing = false;
+ TFSM_EVENT (TransportFSM::locate_done());
Located (); /* EMIT SIGNAL */
return;
}
- cerr << "... now doing the actual locate to " << target_sample << " from " << _transport_sample << endl;
-
// Update Timecode time
_transport_sample = target_sample;
// Bump seek counter so that any in-process locate in the butler
@@ -266,19 +258,30 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, boo
* 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)
+ * !(playing a loop with JACK sync) AND
+ * we're not synced to an external transport master
*
*/
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 {
- /* otherwise tell the world that we located */
- realtime_locate ();
+
+ /* Tell all routes to do the RT part of locate */
+
+ boost::shared_ptr<RouteList> r = routes.reader ();
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->realtime_locate ();
+ }
}
if (force || !for_loop_enabled || loop_changing) {
@@ -379,7 +382,9 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, boo
}
if (need_butler) {
- _butler->schedule_transport_work ();
+ TFSM_EVENT (TransportFSM::butler_required());
+ } else {
+ TFSM_EVENT (TransportFSM::locate_done());
}
loop_changing = false;
@@ -396,6 +401,17 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, boo
}
}
+void
+Session::post_locate ()
+{
+ if (transport_master_is_external() && !synced_to_engine()) {
+ const samplepos_t current_master_position = TransportMasterManager::instance().get_current_position_in_process_context();
+ if (abs (current_master_position - _transport_sample) > TransportMasterManager::instance().current()->resolution()) {
+ _last_roll_location = _last_roll_or_reversal_location = _transport_sample;
+ }
+ }
+}
+
/** Set the transport speed.
* Called from the process thread.
* @param speed New speed
@@ -468,7 +484,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
_requested_return_sample = destination_sample;
}
- stop_transport (abort);
+ TFSM_EVENT (TransportFSM::stop_transport (abort, false));
}
} else if (transport_stopped() && speed == 1.0) {
@@ -505,7 +521,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
_engine.transport_start ();
_count_in_once = false;
} else {
- start_transport ();
+ TFSM_EVENT (TransportFSM::start_transport());
}
} else {
@@ -547,6 +563,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
if ((_transport_speed && speed * _transport_speed < 0.0) || (_last_transport_speed * speed < 0.0) || (_last_transport_speed == 0.0 && speed < 0.0)) {
todo = PostTransportWork (todo | PostTransportReverse);
+ DiskReader::inc_no_disk_output (); // for the buffer reversal
_last_roll_or_reversal_location = _transport_sample;
}
@@ -559,7 +576,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
if (todo) {
add_post_transport_work (todo);
- _butler->schedule_transport_work ();
+ TFSM_EVENT (TransportFSM::butler_required());
}
DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC3 with speed = %1\n", _transport_speed));
@@ -595,14 +612,10 @@ Session::stop_transport (bool abort, bool clear_state)
ENSURE_PROCESS_THREAD;
_count_in_once = false;
- if (_transport_speed == 0.0f) {
- return;
- }
- DEBUG_TRACE (DEBUG::Transport, "time to actually stop\n");
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("time to actually stop with TS @ %1\n", _transport_sample));
realtime_stop (abort, clear_state);
- _butler->schedule_transport_work ();
}
/** Called from the process thread */
@@ -705,15 +718,26 @@ Session::start_transport ()
TransportStateChange (); /* EMIT SIGNAL */
}
+bool
+Session::should_roll_after_locate () const
+{
+ /* a locate must previously have been requested and completed */
+
+ return ((!config.get_external_sync() && (auto_play_legal && config.get_auto_play())) && !_exporting) || (post_transport_work() & PostTransportRoll);
+
+}
+
/** Do any transport work in the audio thread that needs to be done after the
- * transport thread is finished. Audio thread, realtime safe.
+ * butler thread is finished. Audio thread, realtime safe.
*/
void
-Session::post_transport ()
+Session::butler_completed_transport_work ()
{
ENSURE_PROCESS_THREAD;
PostTransportWork ptw = post_transport_work ();
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler done, RT cleanup for %1\n", enum_2_string (ptw)));
+
if (ptw & PostTransportAudition) {
if (auditioner && auditioner->auditioning()) {
process_function = &Session::process_audition;
@@ -722,19 +746,14 @@ Session::post_transport ()
}
}
- if (ptw & PostTransportStop) {
-
- transport_sub_state = 0;
- }
-
if (ptw & PostTransportLocate) {
+ post_locate ();
+ TFSM_EVENT (TransportFSM::locate_done());
+ }
- if (((!config.get_external_sync() && (auto_play_legal && config.get_auto_play())) && !_exporting) || (ptw & PostTransportRoll)) {
- _count_in_once = false;
- start_transport ();
- } else {
- transport_sub_state = 0;
- }
+ if (ptw & PostTransportAdjustPlaybackBuffering) {
+ /* we blocked output while this happened */
+ DiskReader::dec_no_disk_output ();
}
set_next_event ();
@@ -742,6 +761,20 @@ Session::post_transport ()
know were handled ?
*/
set_post_transport_work (PostTransportWork (0));
+
+ if (_transport_fsm->waiting_for_butler()) {
+ TFSM_EVENT (TransportFSM::butler_done());
+ }
+
+ DiskReader::dec_no_disk_output ();
+}
+
+void
+Session::schedule_butler_for_transport_work ()
+{
+ assert (_transport_fsm->waiting_for_butler ());
+ DEBUG_TRACE (DEBUG::Butler, "summon butler for transport work\n");
+ _butler->schedule_transport_work ();
}
bool
@@ -752,7 +785,7 @@ Session::maybe_stop (samplepos_t limit)
if (synced_to_engine () && config.get_jack_time_master ()) {
_engine.transport_stop ();
} else if (!synced_to_engine ()) {
- stop_transport ();
+ TFSM_EVENT (TransportFSM::stop_transport ());
}
return true;
}
@@ -861,11 +894,11 @@ Session::set_play_loop (bool yn, double speed)
rolling, do not locate to loop start.
*/
if (!transport_rolling() && (speed != 0.0)) {
- start_locate (loc->start(), true, true, false, true);
+ TFSM_EVENT (TransportFSM::locate (loc->start(), true, true, false, true));
}
} else {
if (speed != 0.0) {
- start_locate (loc->start(), true, true, false, true);
+ TFSM_EVENT (TransportFSM::locate (loc->start(), true, true, false, true));
}
}
}
@@ -1114,14 +1147,14 @@ Session::request_cancel_play_range ()
bool
Session::solo_selection_active ()
{
- if ( _soloSelection.empty() ) {
+ if (_soloSelection.empty()) {
return false;
}
return true;
}
void
-Session::solo_selection ( StripableList &list, bool new_state )
+Session::solo_selection (StripableList &list, bool new_state)
{
boost::shared_ptr<ControlList> solo_list (new ControlList);
boost::shared_ptr<ControlList> unsolo_list (new ControlList);
@@ -1176,7 +1209,7 @@ Session::butler_transport_work ()
PostTransportWork ptw = post_transport_work();
uint64_t before;
- DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = %1 at %2\n", enum_2_string (ptw), (before = g_get_monotonic_time())));
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = [%1] (0x%3%4%5) at %2\n", enum_2_string (ptw), (before = g_get_monotonic_time()), std::hex, ptw, std::dec));
if (ptw & PostTransportLocate) {
@@ -1311,18 +1344,6 @@ Session::non_realtime_overwrite (int on_entry, bool& finished)
}
}
-bool
-Session::declick_in_progress () const
-{
- boost::shared_ptr<RouteList> rl = routes.reader();
- for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- if ((*i)->declick_in_progress ()) {
- return true;
- }
- }
- return false;
-}
-
void
Session::non_realtime_locate ()
{
@@ -1370,6 +1391,7 @@ Session::non_realtime_locate ()
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
(*i)->non_realtime_locate (tf);
if (sc != g_atomic_int_get (&_seek_counter)) {
+ std::cerr << "\n\nLOCATE INTERRUPTED BY LOCATE!!!\n\n";
goto restart;
}
}
@@ -1757,7 +1779,7 @@ Session::unset_play_loop ()
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 ();
+ TFSM_EVENT (TransportFSM::butler_required());
}
TransportStateChange (); /* EMIT SIGNAL */
}
@@ -1901,27 +1923,37 @@ Session::request_roll_at_and_return (samplepos_t start, samplepos_t return_to)
void
Session::engine_halted ()
{
- bool ignored;
-
/* there will be no more calls to process(), so
we'd better clean up for ourselves, right now.
- but first, make sure the butler is out of
- the picture.
+ We can't queue SessionEvents because they only get
+ handled from within a process callback.
*/
- if (_butler) {
- _butler->stop ();
- }
+ /* this just stops the FSM engine ... it doesn't change the state of
+ * the FSM directly or anything else ... but the FSM will be
+ * reinitialized when we call its ::start() method from
+ * ::engine_running() (if we ever get there)
+ */
- realtime_stop (false, true);
- non_realtime_stop (false, 0, ignored);
- transport_sub_state = 0;
+ _transport_fsm->backend()->stop ();
- DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC6 with speed = %1\n", _transport_speed));
- TransportStateChange (); /* EMIT SIGNAL */
+ /* Synchronously do the realtime part of a transport stop.
+ *
+ * Calling this will cause the butler to asynchronously run
+ * ::non_realtime_stop() where the rest of the "stop" work will be
+ * done.
+ */
+
+ realtime_stop (false, true);
}
+void
+Session::engine_running ()
+{
+ initialize_latencies ();
+ _transport_fsm->backend()->start ();
+}
void
Session::xrun_recovery ()
@@ -2054,7 +2086,7 @@ Session::sync_source_changed (SyncSource type, samplepos_t pos, pframes_t cycle_
longer valid with a new slave.
*/
- DiskReader::set_no_disk_output (false);
+ DiskReader::dec_no_disk_output ();
#if 0
we should not be treating specific transport masters as special cases because there maybe > 1 of a particular type
@@ -2101,3 +2133,27 @@ Session::sync_source_changed (SyncSource type, samplepos_t pos, pframes_t cycle_
set_dirty();
}
+
+bool
+Session::transport_stopped() const
+{
+ return _transport_fsm->stopped();
+}
+
+bool
+Session::transport_rolling() const
+{
+ return _transport_speed != 0.0 && _count_in_samples == 0 && _remaining_latency_preroll == 0;
+}
+
+bool
+Session::locate_pending () const
+{
+ return _transport_fsm->locating();
+}
+
+bool
+Session::declick_in_progress () const
+{
+ return _transport_fsm->declick_in_progress();
+}