change transport API, session API, transportFSM API to move reverse management and motion state (partially) into TFSM

This commit is contained in:
Paul Davis 2020-02-20 00:25:25 -07:00
parent efd6a856f8
commit 4b10beec08
9 changed files with 185 additions and 70 deletions

View File

@ -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 ();

View File

@ -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 */

View File

@ -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 &);

View File

@ -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);

View File

@ -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)

View File

@ -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 ();

View File

@ -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:

View File

@ -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 ();
}

View File

@ -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
@ -535,6 +572,14 @@ TransportFSM::transition (ButlerState bs)
DEBUG_TRACE (DEBUG::TFSMState, string_compose ("Leave %1, enter %2\n", enum_2_string (old), current_state()));
}
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)
{
@ -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);
}