From 4b10beec08c9c1d5ba6fd9cdb8c95ff019b00f65 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 20 Feb 2020 00:25:25 -0700 Subject: change transport API, session API, transportFSM API to move reverse management and motion state (partially) into TFSM --- libs/ardour/ardour/session.h | 6 +- libs/ardour/ardour/transport_api.h | 2 + libs/ardour/ardour/transport_fsm.h | 44 +++++++++++--- libs/ardour/enums.cc | 10 +++- libs/ardour/session.cc | 1 - libs/ardour/session_export.cc | 2 +- libs/ardour/session_process.cc | 9 ++- libs/ardour/session_transport.cc | 67 +++++++--------------- libs/ardour/transport_fsm.cc | 114 ++++++++++++++++++++++++++++++++++--- 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 EventList; @@ -180,6 +206,8 @@ struct TransportFSM EventList deferred_events; int processing; mutable boost::optional 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 @@ -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); +} + -- cgit v1.2.3