summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/ardour/ardour/session.h6
-rw-r--r--libs/ardour/ardour/transport_api.h2
-rw-r--r--libs/ardour/ardour/transport_fsm.h44
-rw-r--r--libs/ardour/enums.cc10
-rw-r--r--libs/ardour/session.cc1
-rw-r--r--libs/ardour/session_export.cc2
-rw-r--r--libs/ardour/session_process.cc9
-rw-r--r--libs/ardour/session_transport.cc67
-rw-r--r--libs/ardour/transport_fsm.cc114
9 files changed, 185 insertions, 70 deletions
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index 9170c0253c..6ab67fbd25 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -771,7 +771,8 @@ public:
bool transport_stopped() const;
bool transport_stopped_or_stopping() const;
bool transport_rolling() const;
-
+ bool transport_will_roll_forwards() const;
+
bool silent () { return _silent; }
TempoMap& tempo_map() { return *_tempo_map; }
@@ -1254,6 +1255,8 @@ protected:
void schedule_butler_for_transport_work ();
bool should_roll_after_locate () const;
double speed() const { return _transport_speed; }
+ samplepos_t position() const { return _transport_sample; }
+ void set_transport_speed (double speed, bool abort, bool clear_state, bool as_default);
private:
int create (const std::string& mix_template, BusProfile const *);
@@ -1692,7 +1695,6 @@ private:
int micro_locate (samplecnt_t distance);
void force_locate (samplepos_t sample, LocateTransportDisposition);
- void set_transport_speed (double speed, bool abort = false, bool clear_state = false, bool as_default = false);
void realtime_stop (bool abort, bool clear_state);
void realtime_locate (bool);
void non_realtime_start_scrub ();
diff --git a/libs/ardour/ardour/transport_api.h b/libs/ardour/ardour/transport_api.h
index d7fcc7dea9..5f6d674e3f 100644
--- a/libs/ardour/ardour/transport_api.h
+++ b/libs/ardour/ardour/transport_api.h
@@ -41,6 +41,8 @@ class LIBARDOUR_API TransportAPI
virtual void schedule_butler_for_transport_work () = 0;
virtual bool should_roll_after_locate () const = 0;
virtual double speed() const = 0;
+ virtual void set_transport_speed (double speed, bool abort_capture, bool clear_state, bool as_default) = 0;
+ virtual samplepos_t position() const = 0;
};
} /* end namespace ARDOUR */
diff --git a/libs/ardour/ardour/transport_fsm.h b/libs/ardour/ardour/transport_fsm.h
index f13a012447..dce30cb665 100644
--- a/libs/ardour/ardour/transport_fsm.h
+++ b/libs/ardour/ardour/transport_fsm.h
@@ -39,13 +39,14 @@ struct TransportFSM
StartTransport,
StopTransport,
Locate,
- LocateDone
+ LocateDone,
+ SetSpeed,
};
struct Event : public boost::intrusive::list_base_hook<> {
EventType type;
- /* for stop */
- bool abort;
+ /* for stop and speed */
+ bool abort_capture;
bool clear_state;
/* for locate */
LocateTransportDisposition ltd;
@@ -53,10 +54,13 @@ struct TransportFSM
samplepos_t target;
bool for_loop_end;
bool force;
+ /* for SetSpeed */
+ double speed;
+ bool as_default;
Event (EventType t)
: type (t)
- , abort (false)
+ , abort_capture (false)
, clear_state (false)
, ltd (MustStop)
, with_flush (false)
@@ -69,7 +73,7 @@ struct TransportFSM
}
Event (EventType t, bool ab, bool cl)
: type (t)
- , abort (ab)
+ , abort_capture (ab)
, clear_state (cl)
, ltd (MustStop)
, with_flush (false)
@@ -81,7 +85,7 @@ struct TransportFSM
}
Event (EventType t, samplepos_t pos, LocateTransportDisposition l, bool fl, bool lp, bool f4c)
: type (t)
- , abort (false)
+ , abort_capture (false)
, clear_state (false)
, ltd (l)
, with_flush (fl)
@@ -91,6 +95,15 @@ struct TransportFSM
{
assert (t == Locate);
}
+ Event (EventType t, double sp, bool ab, bool cs, bool ad)
+ : type (t)
+ , abort_capture (ab)
+ , clear_state (cs)
+ , speed (sp)
+ , as_default (ad)
+ {
+ assert (t == SetSpeed);
+ }
void* operator new (size_t);
void operator delete (void *ptr, size_t /*size*/);
@@ -128,11 +141,18 @@ struct TransportFSM
WaitingForButler
};
+ enum DirectionState {
+ Forwards,
+ Backwards,
+ Reversing,
+ };
+
std::string current_state () const;
private:
MotionState _motion_state;
ButlerState _butler_state;
+ DirectionState _direction_state;
void init();
@@ -147,6 +167,7 @@ struct TransportFSM
void start_locate_while_stopped (Event const &) const;
void interrupt_locate (Event const &) const;
void start_declick_for_locate (Event const &);
+ void set_speed (Event const &);
/* guards */
@@ -161,18 +182,23 @@ struct TransportFSM
bool waiting_for_butler() const { return _butler_state == WaitingForButler; }
bool declick_in_progress() const { return _motion_state == DeclickToLocate || _motion_state == DeclickToStop; }
bool declicking_for_locate() const { return _motion_state == DeclickToLocate; }
+ bool forwards() const { return _direction_state == Forwards; }
+ bool backwards() const { return _direction_state == Backwards; }
+ bool will_roll_fowards() const;
void enqueue (Event* ev);
private:
- void transition (MotionState ms);
- void transition (ButlerState bs);
+ void transition (MotionState);
+ void transition (ButlerState);
+ void transition (DirectionState);
void process_events ();
bool process_event (Event&, bool was_deferred, bool& deferred);
Event _last_locate;
+ Event last_speed_request;
TransportAPI* api;
typedef boost::intrusive::list<Event> EventList;
@@ -180,6 +206,8 @@ struct TransportFSM
EventList deferred_events;
int processing;
mutable boost::optional<bool> current_roll_after_locate_status;
+ double most_recently_requested_speed;
+ bool _reversing;
void defer (Event& ev);
void bad_transition (Event const &);
diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc
index 69bbb0bb61..7ecb40a463 100644
--- a/libs/ardour/enums.cc
+++ b/libs/ardour/enums.cc
@@ -155,10 +155,11 @@ setup_enum_writer ()
TransportFSM::EventType _TransportFSM_EventType;
TransportFSM::MotionState _TransportFSM_MotionState;
TransportFSM::ButlerState _TransportFSM_ButlerState;
+ TransportFSM::DirectionState _TransportFSM_DirectionState;
LoopFadeChoice _LoopFadeChooice;
TransportState _TransportState;
LocateTransportDisposition _LocateTransportDisposition;
-
+
#define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear()
#define REGISTER_BITS(e) enum_writer.register_bits (typeid(e).name(), i, s); i.clear(); s.clear()
#define REGISTER_ENUM(e) i.push_back (e); s.push_back (#e)
@@ -809,6 +810,7 @@ setup_enum_writer ()
REGISTER_CLASS_ENUM (TransportFSM, StopTransport);
REGISTER_CLASS_ENUM (TransportFSM, Locate);
REGISTER_CLASS_ENUM (TransportFSM, LocateDone);
+ REGISTER_CLASS_ENUM (TransportFSM, SetSpeed);
REGISTER (_TransportFSM_EventType);
REGISTER_CLASS_ENUM (TransportFSM, Stopped);
@@ -818,11 +820,15 @@ setup_enum_writer ()
REGISTER_CLASS_ENUM (TransportFSM, WaitingForLocate);
REGISTER (_TransportFSM_MotionState);
-
REGISTER_CLASS_ENUM (TransportFSM, NotWaitingForButler);
REGISTER_CLASS_ENUM (TransportFSM, WaitingForButler);
REGISTER (_TransportFSM_ButlerState);
+ REGISTER_CLASS_ENUM (TransportFSM, Forwards);
+ REGISTER_CLASS_ENUM (TransportFSM, Backwards);
+ REGISTER_CLASS_ENUM (TransportFSM, Reversing);
+ REGISTER (_TransportFSM_DirectionState);
+
REGISTER_ENUM (NoLoopFade);
REGISTER_ENUM (EndLoopFade);
REGISTER_ENUM (BothLoopFade);
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index cfc7f9b278..ead2e97f75 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -191,7 +191,6 @@ Session::Session (AudioEngine &eng,
, _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)
diff --git a/libs/ardour/session_export.cc b/libs/ardour/session_export.cc
index 7ed9498507..c4ef004550 100644
--- a/libs/ardour/session_export.cc
+++ b/libs/ardour/session_export.cc
@@ -253,7 +253,7 @@ Session::process_export_fw (pframes_t nframes)
return;
}
- set_transport_speed (1.0, false);
+ set_transport_speed (1.0, false, false, false);
butler_transport_work ();
g_atomic_int_set (&_butler->should_do_transport_work, 0);
butler_completed_transport_work ();
diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc
index f98970bf2c..9d55e8cc97 100644
--- a/libs/ardour/session_process.cc
+++ b/libs/ardour/session_process.cc
@@ -61,6 +61,7 @@ using namespace std;
#define TFSM_EVENT(evtype) { _transport_fsm->enqueue (new TransportFSM::Event (evtype)); }
#define TFSM_STOP(abort,clear) { _transport_fsm->enqueue (new TransportFSM::Event (TransportFSM::StopTransport,abort,clear)); }
+#define TFSM_SPEED(speed,abort,clear_state,as_default) { _transport_fsm->enqueue (new TransportFSM::Event (TransportFSM::SetSpeed,speed,abort,clear_state,as_default)); }
#define TFSM_LOCATE(target,ltd,flush,loop,force) { _transport_fsm->enqueue (new TransportFSM::Event (TransportFSM::Locate,target,ltd,flush,loop,force)); }
@@ -480,7 +481,7 @@ Session::process_with_events (pframes_t nframes)
assert (_transport_speed == 0 || _transport_speed == 1.0 || _transport_speed == -1.0);
samples_moved = (samplecnt_t) nframes * _transport_speed;
- // DEBUG_TRACE (DEBUG::Transport, string_compose ("plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_speed));
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_speed));
end_sample = _transport_sample + samples_moved;
@@ -641,10 +642,12 @@ Session::process_without_events (pframes_t nframes)
assert (_transport_speed == 0 || _transport_speed == 1.0 || _transport_speed == -1.0);
if (_transport_speed == 0) {
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("transport not moving @ %1\n", _transport_sample));
no_roll (nframes);
return;
} else {
samples_moved = (samplecnt_t) nframes * _transport_speed;
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_speed));
}
if (!_exporting && !timecode_transmission_suspended()) {
@@ -894,8 +897,8 @@ Session::process_event (SessionEvent* ev)
break;
- case SessionEvent::SetTransportSpeed:
- set_transport_speed (ev->speed, ev->yes_or_no, ev->second_yes_or_no, ev->third_yes_or_no);
+ case SessionEvent::SetTransportSpeed:
+ TFSM_SPEED (ev->speed, ev->yes_or_no, ev->second_yes_or_no, ev->third_yes_or_no);
break;
case SessionEvent::SetTransportMaster:
diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc
index 1b7f3c7593..2cc62118b5 100644
--- a/libs/ardour/session_transport.cc
+++ b/libs/ardour/session_transport.cc
@@ -99,10 +99,9 @@ Session::realtime_stop (bool abort, bool clear_state)
DEBUG_TRACE (DEBUG::Transport, string_compose ("realtime stop @ %1 speed = %2\n", _transport_sample, _transport_speed));
PostTransportWork todo = PostTransportWork (0);
- if (_last_transport_speed < 0.0f) {
- todo = (PostTransportWork (todo | PostTransportStop | PostTransportReverse));
+ if (_transport_speed < 0.0f) {
+ todo = (PostTransportWork (todo | PostTransportStop));
_default_transport_speed = 1.0;
- DiskReader::inc_no_disk_output (); // for the buffer reversal
} else {
todo = PostTransportWork (todo | PostTransportStop);
}
@@ -195,7 +194,7 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, boo
*/
if (with_roll) {
- set_transport_speed (1.0, 0, false);
+ set_transport_speed (1.0, false, false, false);
}
loop_changing = false;
TFSM_EVENT (TransportFSM::LocateDone);
@@ -383,11 +382,17 @@ void
Session::set_transport_speed (double speed, bool abort, bool clear_state, bool as_default)
{
ENSURE_PROCESS_THREAD;
- DEBUG_TRACE (DEBUG::Transport, string_compose ("@ %5 Set transport speed to %1, abort = %2 clear_state = %3, current = %4 as_default %6\n",
- speed, abort, clear_state, _transport_speed, _transport_sample, as_default));
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("@ %5 Set transport speed to %1 from %4 (es = %7), abort = %2 clear_state = %3, as_default %6\n",
+ speed, abort, clear_state, _transport_speed, _transport_sample, as_default, _engine_speed));
+
+ if ((_engine_speed != 1) && (_engine_speed == fabs (speed)) && (speed * _transport_speed) >= 0) {
+ /* engine speed is not changing and no direction change, do nothing */
+ DEBUG_TRACE (DEBUG::Transport, "no reason to change speed, do nothing\n");
+ return;
+ }
/* max speed is somewhat arbitrary but based on guestimates regarding disk i/o capability
- and user needs. We really need CD-style "skip" playback for ffwd and rewind.
+ and user needs. XXX We really need CD-style "skip" playback for ffwd and rewind.
*/
if (speed > 0) {
@@ -397,6 +402,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state, bool a
}
double new_engine_speed = 1.0;
+
if (speed != 0) {
new_engine_speed = fabs (speed);
if (speed < 0) speed = -1;
@@ -510,26 +516,12 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state, bool a
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.0 && speed < 0.0)) {
- todo = PostTransportWork (todo | PostTransportReverse);
- DiskReader::inc_no_disk_output (); // for the buffer reversal
- _last_roll_or_reversal_location = _transport_sample;
- }
-
- _last_transport_speed = _transport_speed;
_transport_speed = speed;
if (as_default) {
_default_transport_speed = speed;
}
- if (todo) {
- add_post_transport_work (todo);
- TFSM_EVENT (TransportFSM::ButlerRequired);
- }
-
DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC3 with speed = %1\n", _transport_speed));
/* throttle signal emissions.
@@ -537,7 +529,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state, bool a
* 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,
+ * Using _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
*
@@ -713,7 +705,7 @@ Session::butler_completed_transport_work ()
bool start_after_butler_done_msg = false;
- if ((ptw & (PostTransportReverse|PostTransportRoll))) {
+ if (ptw & PostTransportRoll) {
start_after_butler_done_msg = true;
}
@@ -1188,29 +1180,6 @@ Session::butler_transport_work ()
}
}
- if (ptw & PostTransportReverse) {
-
- clear_clicks();
-
- /* don't seek if locate will take care of that in non_realtime_stop() */
-
- if (!(ptw & PostTransportLocate)) {
- for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- (*i)->non_realtime_locate (_transport_sample);
-
- if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) {
- /* new request, stop seeking, and start again */
- g_atomic_int_dec_and_test (&_butler->should_do_transport_work);
- goto restart;
- }
- }
- VCAList v = _vca_manager->vcas ();
- for (VCAList::const_iterator i = v.begin(); i != v.end(); ++i) {
- (*i)->non_realtime_locate (_transport_sample);
- }
- }
- }
-
if (ptw & PostTransportLocate) {
DEBUG_TRACE (DEBUG::Transport, "nonrealtime locate invoked from BTW\n");
non_realtime_locate ();
@@ -2043,3 +2012,9 @@ Session::declick_in_progress () const
{
return _transport_fsm->declick_in_progress();
}
+
+bool
+Session::transport_will_roll_forwards () const
+{
+ return _transport_fsm->will_roll_fowards ();
+}
diff --git a/libs/ardour/transport_fsm.cc b/libs/ardour/transport_fsm.cc
index f6b17ca071..6b8ee1fd61 100644
--- a/libs/ardour/transport_fsm.cc
+++ b/libs/ardour/transport_fsm.cc
@@ -55,6 +55,7 @@ TransportFSM::Event::operator delete (void *ptr, size_t /*size*/)
TransportFSM::TransportFSM (TransportAPI& tapi)
: _last_locate (Locate, 0, MustRoll, false, false, false) /* all but first argument don't matter */
+ , last_speed_request (SetSpeed, 0, false, false, false) /* ditto */
, api (&tapi)
, processing (0)
{
@@ -66,6 +67,7 @@ TransportFSM::init ()
{
_motion_state = Stopped;
_butler_state = NotWaitingForButler;
+ _direction_state = Forwards;
_last_locate.target = max_samplepos;
}
@@ -179,7 +181,7 @@ std::string
TransportFSM::current_state () const
{
std::stringstream s;
- s << enum_2_string (_motion_state) << '/' << enum_2_string (_butler_state);
+ s << enum_2_string (_motion_state) << '/' << enum_2_string (_butler_state) << '/' << enum_2_string (_direction_state);
return s.str();
}
@@ -200,6 +202,20 @@ TransportFSM::process_event (Event& ev, bool already_deferred, bool& deferred)
switch (ev.type) {
+ case SetSpeed:
+ switch (_motion_state) {
+ case Stopped:
+ case Rolling:
+ set_speed (ev);
+ break;
+ default:
+ if (!already_deferred) {
+ defer (ev);
+ deferred = true;
+ }
+ }
+ break;
+
case StartTransport:
switch (_motion_state) {
case Stopped:
@@ -298,12 +314,33 @@ TransportFSM::process_event (Event& ev, bool already_deferred, bool& deferred)
case LocateDone:
switch (_motion_state) {
case WaitingForLocate:
- if (should_not_roll_after_locate()) {
- transition (Stopped);
- /* already stopped, nothing to do */
- } else {
+
+ if (_reversing) {
+
+ _reversing = false;
transition (Rolling);
- roll_after_locate ();
+
+ if (most_recently_requested_speed > 0) {
+ transition (Forwards);
+ } else {
+ transition (Forwards);
+ }
+
+ api->set_transport_speed (last_speed_request.speed, last_speed_request.abort_capture, last_speed_request.clear_state, last_speed_request.as_default);
+
+ if (most_recently_requested_speed != 0.0) {
+ roll_after_locate ();
+ }
+
+ } else {
+ if (should_not_roll_after_locate()) {
+ transition (Stopped);
+ /* already stopped, nothing to do */
+ } else {
+ transition (Rolling);
+ roll_after_locate ();
+ }
+ break;
}
break;
default:
@@ -375,7 +412,7 @@ TransportFSM::stop_playback (Event const & s)
_last_locate.target = max_samplepos;
current_roll_after_locate_status = boost::none;
- api->stop_transport (s.abort, s.clear_state);
+ api->stop_transport (s.abort_capture, s.clear_state);
}
void
@@ -536,6 +573,14 @@ TransportFSM::transition (ButlerState bs)
}
void
+TransportFSM::transition (DirectionState ds)
+{
+ const DirectionState old = _direction_state;
+ _direction_state = ds;
+ DEBUG_TRACE (DEBUG::TFSMState, string_compose ("Leave %1, enter %2\n", enum_2_string (old), current_state()));
+}
+
+void
TransportFSM::enqueue (Event* ev)
{
DEBUG_TRACE (DEBUG::TFSMState, string_compose ("queue tfsm event %1\n", enum_2_string (ev->type)));
@@ -544,3 +589,58 @@ TransportFSM::enqueue (Event* ev)
process_events ();
}
}
+
+void
+TransportFSM::set_speed (Event const & ev)
+{
+ if ((rolling() && ev.speed * most_recently_requested_speed < 0.0) ||
+ (stopped() && ev.speed < 0.0) ||
+ (rolling() && most_recently_requested_speed < 0.0 && ev.speed == 0.0)) {
+
+ /* Transport was rolling, and new speed has opposite sign to
+ * the last requested speed.
+ *
+ * OR
+ *
+ * Transport was stopped, and new speed is negative.
+ *
+ * OR
+ *
+ * new speed is zero, last requested speed was negative
+ *
+ * SO ... we need to reverse.
+ *
+ * The plan: stop normally (with a declick, and schedule a
+ * locate to our current position, with "force" set to true,
+ * and roll right after it is complete.
+ */
+
+ most_recently_requested_speed = ev.speed;
+ last_speed_request = ev;
+ _reversing = true;
+
+ DEBUG_TRACE (DEBUG::TFSMState, string_compose ("reverse, target speed %1 MRRS %2 state %3\n", ev.speed, most_recently_requested_speed, current_state()));
+
+ Event lev (Locate, api->position(), MustRoll, true, false, true);
+
+ transition (DeclickToLocate);
+ start_declick_for_locate (lev);
+
+ } else {
+
+ most_recently_requested_speed = ev.speed;
+ api->set_transport_speed (ev.speed, ev.abort_capture, ev.clear_state, ev.as_default);
+
+ }
+
+}
+
+bool
+TransportFSM::will_roll_fowards () const
+{
+ if (_reversing) {
+ return most_recently_requested_speed >= 0; /* note: future speed of zero is equivalent to Forwards */
+ }
+ return (_direction_state == Forwards);
+}
+