diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 0c00e1ede9..0ef708ea4f 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -797,12 +797,8 @@ public: bool synced_to_engine() const; double engine_speed() const { return _engine_speed; } - double actual_speed() const { - if (_transport_speed > 0) return _engine_speed; - if (_transport_speed < 0) return - _engine_speed; - return 0; - } - double transport_speed() const { return _count_in_samples > 0 ? 0. : _transport_speed; } + double actual_speed() const; + double transport_speed() const; /** @return true if the transport state (TFSM) is stopped */ bool transport_stopped() const; /** @return true if the transport state (TFSM) is stopped or stopping */ @@ -1336,16 +1332,17 @@ protected: /* transport API */ - void locate (samplepos_t, bool with_roll, bool for_loop_end=false, bool force=false, bool with_mmc=true); + void locate (samplepos_t, bool for_loop_end=false, bool force=false, bool with_mmc=true); void stop_transport (bool abort = false, bool clear_state = false); void start_transport (); void butler_completed_transport_work (); void post_locate (); void schedule_butler_for_transport_work (); bool should_roll_after_locate () const; - double speed() const { return _transport_speed; } + bool user_roll_after_locate () const; + bool should_stop_before_locate () const; samplepos_t position() const { return _transport_sample; } - void set_transport_speed (double speed, bool as_default, bool at_next_start); + void set_transport_speed (double speed); bool need_declick_before_locate () const; private: @@ -1382,7 +1379,6 @@ private: // varispeed playback -- TODO: move out of session to backend. double _engine_speed; - double _transport_speed; // only: -1, 0, +1 double _default_transport_speed; double _default_engine_speed; double _last_transport_speed; diff --git a/libs/ardour/ardour/transport_api.h b/libs/ardour/ardour/transport_api.h index 89299a0178..8cd5a4ea91 100644 --- a/libs/ardour/ardour/transport_api.h +++ b/libs/ardour/ardour/transport_api.h @@ -34,15 +34,15 @@ class LIBARDOUR_API TransportAPI private: friend struct TransportFSM; - virtual void locate (samplepos_t, bool with_roll, bool with_loop=false, bool force=false, bool with_mmc=true) = 0; + virtual void locate (samplepos_t, bool with_loop=false, bool force=false, bool with_mmc=true) = 0; virtual bool should_stop_before_locate () const = 0; virtual void stop_transport (bool abort = false, bool clear_state = false) = 0; virtual void start_transport () = 0; virtual void butler_completed_transport_work () = 0; 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 as_default, bool at_next_start) = 0; + virtual bool user_roll_after_locate() const = 0; + virtual void set_transport_speed (double speed) = 0; virtual samplepos_t position() const = 0; virtual bool need_declick_before_locate () const = 0; }; diff --git a/libs/ardour/ardour/transport_fsm.h b/libs/ardour/ardour/transport_fsm.h index 6efaa84f1b..7912d7ad58 100644 --- a/libs/ardour/ardour/transport_fsm.h +++ b/libs/ardour/ardour/transport_fsm.h @@ -147,12 +147,13 @@ struct TransportFSM std::string current_state () const; - int transport_speed () const; + double transport_speed() const { return _transport_speed; } private: MotionState _motion_state; ButlerState _butler_state; DirectionState _direction_state; + double _transport_speed; void init(); @@ -161,7 +162,7 @@ struct TransportFSM void schedule_butler_for_transport_work () const; void start_playback (); void stop_playback (Event const &); - void start_locate_after_declick () const; + void start_locate_after_declick (); void locate_for_loop (Event const &); void roll_after_locate () const; void start_locate_while_stopped (Event const &) const; @@ -199,7 +200,6 @@ struct TransportFSM bool process_event (Event&, bool was_deferred, bool& deferred); mutable Event _last_locate; - Event last_speed_request; TransportAPI* api; typedef boost::intrusive::list EventList; @@ -207,12 +207,14 @@ struct TransportFSM EventList deferred_events; int processing; mutable boost::optional current_roll_after_locate_status; - double most_recently_requested_speed; + mutable double most_recently_requested_speed; + mutable double default_speed; void defer (Event& ev); void bad_transition (Event const &); void set_roll_after (bool) const; bool compute_should_roll (LocateTransportDisposition) const; + int compute_transport_speed () const; }; } /* end namespace ARDOUR */ diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 5e5ee50d5b..fc8a147b8f 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -191,9 +191,6 @@ Session::Session (AudioEngine &eng, , _silent (false) , _remaining_latency_preroll (0) , _engine_speed (1.0) - , _transport_speed (0) - , _default_transport_speed (1.0) - , _default_engine_speed (1.0) , _last_transport_speed (1.0) , _requested_transport_speed (std::numeric_limits::max()) , _signalled_varispeed (0) @@ -1872,7 +1869,7 @@ Session::_locations_changed (const Locations::LocationList& locations) void Session::enable_record () { - if (_transport_speed != 0.0 && _transport_speed != 1.0) { + if (_transport_fsm->transport_speed() != 0.0 && _transport_fsm->transport_speed() != 1.0) { /* no recording at anything except normal speed */ return; } @@ -1964,11 +1961,11 @@ Session::maybe_enable_record (bool rt_context) /* Save pending state of which sources the next record will use, * which gives us some chance of recovering from a crash during the record. */ - if (!rt_context && (!quick_start || _transport_speed == 0)) { + if (!rt_context && (!quick_start || _transport_fsm->transport_speed() == 0)) { save_state ("", true); } - if (_transport_speed != 0) { + if (_transport_fsm->transport_speed() != 0) { maybe_allow_only_punch (); if (!config.get_punch_in()) { enable_record (); @@ -2020,7 +2017,7 @@ Session::audible_sample (bool* latent_locate) const } #if 0 // TODO looping - if (_transport_speed > 0.0f) { + if (_transport_fsm->transport_speed() > 0.0f) { if (play_loop && have_looped) { /* the play-position wrapped at the loop-point * ardour is already playing the beginning of the loop, @@ -2036,7 +2033,7 @@ Session::audible_sample (bool* latent_locate) const } } } - } else if (_transport_speed < 0.0f) { + } else if (_transport_fsm->transport_speed() < 0.0f) { /* XXX wot? no backward looping? */ } #endif diff --git a/libs/ardour/session_export.cc b/libs/ardour/session_export.cc index 5df0c6f60d..8870d6e2ac 100644 --- a/libs/ardour/session_export.cc +++ b/libs/ardour/session_export.cc @@ -42,6 +42,9 @@ using namespace std; using namespace ARDOUR; using namespace PBD; +#define TFSM_ROLL() { _transport_fsm->enqueue (new TransportFSM::Event (TransportFSM::StartTransport)); } +#define TFSM_SPEED(speed,as_default) { _transport_fsm->enqueue (new TransportFSM::Event (speed,as_default)); } + boost::shared_ptr Session::get_export_handler () { @@ -116,7 +119,7 @@ Session::start_audio_export (samplepos_t position, bool realtime, bool region_ex if (!_exporting) { pre_export (); - } else if (_transport_speed != 0) { + } else if (_transport_fsm->transport_speed() != 0) { realtime_stop (true, true); } @@ -302,13 +305,13 @@ Session::process_export_fw (pframes_t nframes) return; } - set_transport_speed (1.0, false, true); - start_transport (); + TFSM_SPEED (1.0, false); + TFSM_ROLL (); butler_transport_work (true); g_atomic_int_set (&_butler->should_do_transport_work, 0); butler_completed_transport_work (); /* Session::process_with_events () sets _remaining_latency_preroll = 0 - * when being called with _transport_speed == 0.0. + * when being called with _transport_fsm->transport_speed() == 0. * * This can happen wit JACK, there is a process-callback before * freewheeling becomes active, after Session::start_audio_export(). diff --git a/libs/ardour/session_midi.cc b/libs/ardour/session_midi.cc index 752d865998..9ae6b2db19 100644 --- a/libs/ardour/session_midi.cc +++ b/libs/ardour/session_midi.cc @@ -52,6 +52,7 @@ #include "ardour/profile.h" #include "ardour/session.h" #include "ardour/transport_master.h" +#include "ardour/transport_fsm.h" #include "ardour/ticker.h" #include "pbd/i18n.h" @@ -142,7 +143,7 @@ Session::mmc_record_strobe (MIDI::MachineControl &/*mmc*/) /* record strobe does an implicit "Play" command */ - if (_transport_speed != 1.0) { + if (_transport_fsm->transport_speed() != 1.0) { /* start_transport() will move from Enabled->Recording, so we don't need to do anything here except enable recording. @@ -222,7 +223,7 @@ Session::mmc_step (MIDI::MachineControl &/*mmc*/, int steps) double diff_secs = diff.tv_sec + (diff.tv_usec / 1000000.0); double cur_speed = (((steps * 0.5) * timecode_frames_per_second()) / diff_secs) / timecode_frames_per_second(); - if (_transport_speed == 0 || cur_speed * _transport_speed < 0) { + if (_transport_fsm->transport_speed() == 0 || cur_speed * _transport_fsm->transport_speed() < 0) { /* change direction */ step_speed = cur_speed; } else { @@ -233,7 +234,7 @@ Session::mmc_step (MIDI::MachineControl &/*mmc*/, int steps) #if 0 cerr << "delta = " << diff_secs - << " ct = " << _transport_speed + << " ct = " << _transport_fsm->transport_speed() << " steps = " << steps << " new speed = " << cur_speed << " speed = " << step_speed @@ -493,7 +494,7 @@ Session::send_midi_time_code_for_cycle (samplepos_t start_sample, samplepos_t en return 0; } - if (_transport_speed < 0) { + if (_transport_fsm->transport_speed() < 0) { // we don't support rolling backwards return 0; } @@ -565,7 +566,7 @@ Session::send_midi_time_code_for_cycle (samplepos_t start_sample, samplepos_t en assert (msg_time < end_sample); /* convert from session samples back to JACK samples using the transport speed */ - ARDOUR::pframes_t const out_stamp = (msg_time - start_sample) / _transport_speed; + ARDOUR::pframes_t const out_stamp = (msg_time - start_sample) / _transport_fsm->transport_speed(); assert (out_stamp < nframes); MidiBuffer& mb (_midi_ports->mtc_output_port()->get_midi_buffer(nframes)); @@ -623,7 +624,7 @@ Session::mmc_step_timeout () timersub (&now, &last_mmc_step, &diff); diff_usecs = diff.tv_sec * 1000000 + diff.tv_usec; - if (diff_usecs > 1000000.0 || fabs (_transport_speed) < 0.0000001) { + if (diff_usecs > 1000000.0 || fabs (_transport_fsm->transport_speed()) < 0.0000001) { /* too long or too slow, stop transport */ request_stop (); step_queued = false; @@ -637,7 +638,7 @@ Session::mmc_step_timeout () /* slow it down */ - request_transport_speed_nonzero (_transport_speed * 0.75); + request_transport_speed_nonzero (actual_speed() * 0.75); return true; } diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc index 643841bf0c..fabc1ebb18 100644 --- a/libs/ardour/session_process.cc +++ b/libs/ardour/session_process.cc @@ -165,9 +165,11 @@ Session::fail_roll (pframes_t nframes) int Session::no_roll (pframes_t nframes) { + assert (_transport_fsm->transport_speed() == 0); + PT_TIMING_CHECK (4); - samplepos_t end_sample = _transport_sample + floor (nframes * _transport_speed); + samplepos_t end_sample = _transport_sample + floor (nframes * _transport_fsm->transport_speed()); int ret = 0; boost::shared_ptr r = routes.reader (); @@ -215,7 +217,7 @@ Session::process_routes (pframes_t nframes, bool& need_butler) boost::shared_ptr r = routes.reader (); const samplepos_t start_sample = _transport_sample; - const samplepos_t end_sample = _transport_sample + floor (nframes * _transport_speed); + const samplepos_t end_sample = _transport_sample + floor (nframes * _transport_fsm->transport_speed()); if (actively_recording ()) { _capture_duration += nframes; @@ -291,13 +293,13 @@ Session::get_track_statistics () bool Session::compute_audible_delta (samplepos_t& pos_and_delta) const { - if (_transport_speed == 0.0 || _count_in_samples > 0 || _remaining_latency_preroll > 0) { + if (_transport_fsm->transport_speed() == 0.0 || _count_in_samples > 0 || _remaining_latency_preroll > 0) { /* cannot compute audible delta, because the session is generating silence that does not correspond to the timeline, but is instead filling playback buffers to manage latency alignment. */ - DEBUG_TRACE (DEBUG::Slave, string_compose ("still adjusting for latency (%1) and/or count-in (%2) or stopped %1\n", _remaining_latency_preroll, _count_in_samples, _transport_speed)); + DEBUG_TRACE (DEBUG::Slave, string_compose ("still adjusting for latency (%1) and/or count-in (%2) or stopped %1\n", _remaining_latency_preroll, _count_in_samples, _transport_fsm->transport_speed())); return false; } @@ -361,10 +363,10 @@ Session::process_with_events (pframes_t nframes) process_event (ev); } /* only count-in when going to roll at speed 1.0 */ - if (_transport_speed != 1.0 && _count_in_samples > 0) { + if (_transport_fsm->transport_speed() != 1.0 && _count_in_samples > 0) { _count_in_samples = 0; } - if (_transport_speed == 0.0) { + if (_transport_fsm->transport_speed() == 0.0) { _remaining_latency_preroll = 0; } @@ -438,11 +440,11 @@ Session::process_with_events (pframes_t nframes) bool const was_sending_qf_mtc = _send_qf_mtc; double const tolerance = Config->get_mtc_qf_speed_tolerance() / 100.0; - if (_transport_speed != 0) { + if (_transport_fsm->transport_speed() != 0) { _send_qf_mtc = ( Config->get_send_mtc () && - _transport_speed >= (1 - tolerance) && - _transport_speed <= (1 + tolerance) + _transport_fsm->transport_speed() >= (1 - tolerance) && + _transport_fsm->transport_speed() <= (1 + tolerance) ); if (_send_qf_mtc && !was_sending_qf_mtc) { @@ -488,10 +490,10 @@ Session::process_with_events (pframes_t nframes) } } - assert (_transport_speed == 0 || _transport_speed == 1.0 || _transport_speed == -1.0); + assert (_transport_fsm->transport_speed() == 0 || _transport_fsm->transport_speed() == 1.0 || _transport_fsm->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)); + samples_moved = (samplecnt_t) nframes * _transport_fsm->transport_speed(); + // DEBUG_TRACE (DEBUG::Transport, string_compose ("plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_fsm->transport_speed())); end_sample = _transport_sample + samples_moved; @@ -511,7 +513,7 @@ Session::process_with_events (pframes_t nframes) } } - if (_transport_speed == 0) { + if (_transport_fsm->transport_speed() == 0) { no_roll (nframes); return; } @@ -539,15 +541,15 @@ Session::process_with_events (pframes_t nframes) while (nframes) { this_nframes = nframes; /* real (jack) time relative */ - samples_moved = (samplecnt_t) floor (_transport_speed * nframes); /* transport relative */ - // DEBUG_TRACE (DEBUG::Transport, string_compose ("sub-loop plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_speed)); + samples_moved = (samplecnt_t) floor (_transport_fsm->transport_speed() * nframes); /* transport relative */ + // DEBUG_TRACE (DEBUG::Transport, string_compose ("sub-loop plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_fsm->transport_speed())); /* running an event, position transport precisely to its time */ if (this_event && this_event->action_sample <= end_sample && this_event->action_sample >= _transport_sample) { /* this isn't quite right for reverse play */ samples_moved = (samplecnt_t) (this_event->action_sample - _transport_sample); - // DEBUG_TRACE (DEBUG::Transport, string_compose ("sub-loop2 (for %4)plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_speed, enum_2_string (this_event->type))); - this_nframes = abs (floor(samples_moved / _transport_speed)); + // DEBUG_TRACE (DEBUG::Transport, string_compose ("sub-loop2 (for %4)plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_fsm->transport_speed(), enum_2_string (this_event->type))); + this_nframes = abs (floor(samples_moved / _transport_fsm->transport_speed())); } try_run_lua (this_nframes); @@ -605,7 +607,7 @@ Session::process_with_events (pframes_t nframes) } /* this is necessary to handle the case of seamless looping */ - end_sample = _transport_sample + floor (nframes * _transport_speed); + end_sample = _transport_sample + floor (nframes * _transport_fsm->transport_speed()); } set_next_event (); @@ -646,15 +648,15 @@ Session::process_without_events (pframes_t nframes) } } - assert (_transport_speed == 0 || _transport_speed == 1.0 || _transport_speed == -1.0); + assert (_transport_fsm->transport_speed() == 0 || _transport_fsm->transport_speed() == 1.0 || _transport_fsm->transport_speed() == -1.0); - if (_transport_speed == 0) { + if (_transport_fsm->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)); + samples_moved = (samplecnt_t) nframes * _transport_fsm->transport_speed(); + // DEBUG_TRACE (DEBUG::Transport, string_compose ("plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_fsm->transport_speed())); } if (!_exporting && !timecode_transmission_suspended()) { @@ -683,10 +685,10 @@ Session::process_without_events (pframes_t nframes) if (samples_moved < 0) { decrement_transport_position (-samples_moved); - //DEBUG_TRACE (DEBUG::Transport, string_compose ("DEcrement transport by %1 to %2\n", samples_moved, _transport_sample)); + // DEBUG_TRACE (DEBUG::Transport, string_compose ("DEcrement transport by %1 to %2\n", samples_moved, _transport_sample)); } else if (samples_moved) { increment_transport_position (samples_moved); - //DEBUG_TRACE (DEBUG::Transport, string_compose ("INcrement transport by %1 to %2\n", samples_moved, _transport_sample)); + // DEBUG_TRACE (DEBUG::Transport, string_compose ("INcrement transport by %1 to %2\n", samples_moved, _transport_sample)); } else { DEBUG_TRACE (DEBUG::Transport, "no transport motion\n"); } @@ -1193,7 +1195,7 @@ Session::plan_master_strategy_engine (pframes_t nframes, double master_speed, sa /* master rolling, we should be too */ - if (_transport_speed == 0.0f) { + if (_transport_fsm->transport_speed() == 0.0f) { DEBUG_TRACE (DEBUG::Slave, string_compose ("slave starts transport: %1 sample %2 tf %3\n", master_speed, master_transport_sample, _transport_sample)); transport_master_strategy.action = TransportMasterStart; return 1.0; @@ -1201,7 +1203,7 @@ Session::plan_master_strategy_engine (pframes_t nframes, double master_speed, sa } else if (!tmm.current()->starting()) { /* master stopped, not in "starting" state */ - if (_transport_speed != 0.0f) { + if (_transport_fsm->transport_speed() != 0.0f) { DEBUG_TRACE (DEBUG::Slave, string_compose ("slave stops transport: %1 sample %2 tf %3\n", master_speed, master_transport_sample, _transport_sample)); transport_master_strategy.action = TransportMasterStop; return 1.0; @@ -1468,7 +1470,7 @@ Session::plan_master_strategy (pframes_t nframes, double master_speed, samplepos /* master rolling, we should be too */ - if (_transport_speed == 0.0f) { + if (_transport_fsm->transport_speed() == 0.0f) { DEBUG_TRACE (DEBUG::Slave, string_compose ("slave starts transport: %1 sample %2 tf %3\n", master_speed, master_transport_sample, _transport_sample)); transport_master_strategy.action = TransportMasterStart; transport_master_strategy.catch_speed = catch_speed; @@ -1477,7 +1479,7 @@ Session::plan_master_strategy (pframes_t nframes, double master_speed, samplepos } else if (!tmm.current()->starting()) { /* master stopped, not in "starting" state */ - if (_transport_speed != 0.0f) { + if (_transport_fsm->transport_speed() != 0.0f) { DEBUG_TRACE (DEBUG::Slave, string_compose ("slave stops transport: %1 sample %2 tf %3\n", master_speed, master_transport_sample, _transport_sample)); transport_master_strategy.action = TransportMasterStop; return catch_speed; diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index c5057f4e86..d79640e879 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -97,7 +97,7 @@ Session::realtime_stop (bool abort, bool clear_state) { ENSURE_PROCESS_THREAD; - DEBUG_TRACE (DEBUG::Transport, string_compose ("realtime stop @ %1 speed = %2\n", _transport_sample, _transport_speed)); + DEBUG_TRACE (DEBUG::Transport, string_compose ("realtime stop @ %1 speed = %2\n", _transport_sample, _transport_fsm->transport_speed())); PostTransportWork todo = PostTransportStop; /* we are rolling and we want to stop */ @@ -117,34 +117,6 @@ Session::realtime_stop (bool abort, bool clear_state) } } - if (!_transport_fsm->declicking_for_locate()) { - - if (Config->get_reset_default_speed_on_stop()) { - if (_engine_speed != 1.0) { - TFSM_SPEED (1.0, true); - } - } else { - /* We're not resetting back to 1.0, but we may need to handle a - * speed change from whatever we have been rolling at to - * whatever the current default is. We could have been - * rewinding at -4.5 ... when we restart, we need to play at - * the current _default_transport_speed - * - * don't do this is there's already a requested transport speed waiting - */ - - if (_requested_transport_speed == std::numeric_limits::max()) { - /* no pending speed request */ - - const double dts = (_default_transport_speed < 0) ? -1 : 1; - - if (_engine_speed != _default_engine_speed || dts != _transport_speed) { - TFSM_SPEED (_default_transport_speed, false); - } - } - } - } - /* call routes */ boost::shared_ptr r = routes.reader (); @@ -179,11 +151,13 @@ Session::realtime_stop (bool abort, bool clear_state) reset_punch_loop_constraint (); - _transport_speed = 0; - g_atomic_int_set (&_playback_load, 100); g_atomic_int_set (&_capture_load, 100); + if (Config->get_monitoring_model() == HardwareMonitoring) { + set_track_monitor_input_status (true); + } + if (config.get_use_video_sync()) { waiting_for_sync_offset = true; } @@ -195,7 +169,7 @@ Session::realtime_stop (bool abort, bool clear_state) /** @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 for_loop_end, bool force, bool with_mmc) +Session::locate (samplepos_t target_sample, bool for_loop_end, bool force, bool with_mmc) { ENSURE_PROCESS_THREAD; @@ -214,19 +188,10 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool for_loop_end, b * changes in the value of _transport_sample. */ - DEBUG_TRACE (DEBUG::Transport, string_compose ("rt-locate to %1 ts = %7, roll %2 for loop end %3 force %4 mmc %5\n", - target_sample, with_roll, for_loop_end, force, with_mmc, _transport_sample)); + DEBUG_TRACE (DEBUG::Transport, string_compose ("rt-locate to %1 ts = %7, for loop end %2 force %3 mmc %4\n", + target_sample, for_loop_end, force, with_mmc, _transport_sample)); if (!force && (_transport_sample == target_sample) && !for_loop_end) { - - /* already at the desired position. Not forced to locate, so - unless we're told to start rolling also, there's nothing to - do but tell the world where we are (again). - */ - - if (with_roll) { - start_transport (); - } TFSM_EVENT (TransportFSM::LocateDone); Located (); /* EMIT SIGNAL */ return; @@ -244,38 +209,13 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool for_loop_end, b } timecode_time(_transport_sample, transmitting_timecode_time); // XXX here? - /* do "stopped" stuff if: - * - * 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) AND - * we're not synced to an external transport master - * - */ + assert (_transport_fsm->locating() || _transport_fsm->declicking_for_locate()); - /* it is important here that we use the internal state of the transport - FSM, not the public facing result of ::transport_rolling() - */ - bool transport_was_stopped = _transport_fsm->stopped(); + /* Tell all routes to do the RT part of locate */ - if (!transport_was_stopped && - (!auto_play_legal || !config.get_auto_play()) && - !with_roll && - !(synced_to_engine() && get_play_loop ()) && - !(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 { - - /* Tell all routes to do the RT part of locate */ - - boost::shared_ptr r = routes.reader (); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - (*i)->realtime_locate (for_loop_end); - } + boost::shared_ptr r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->realtime_locate (for_loop_end); } if (force || !for_loop_end) { @@ -300,18 +240,6 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool for_loop_end, b } } - if (with_roll) { - /* switch from input if we're going to roll */ - if (Config->get_monitoring_model() == HardwareMonitoring) { - set_track_monitor_input_status (!config.get_auto_input()); - } - } else { - /* otherwise we're going to stop, so do the opposite */ - if (Config->get_monitoring_model() == HardwareMonitoring) { - set_track_monitor_input_status (true); - } - } - /* cancel looped playback if transport pos outside of loop range */ if (get_play_loop ()) { @@ -390,10 +318,10 @@ Session::post_locate () * @param speed New speed */ void -Session::set_transport_speed (double speed, bool as_default, bool at_next_start) +Session::set_transport_speed (double speed) { ENSURE_PROCESS_THREAD; - DEBUG_TRACE (DEBUG::Transport, string_compose ("@ %1 Set transport speed to %2 from %3 (es = %4) (def %5), as_default %6\n", _transport_sample, speed, _transport_speed, _engine_speed, _default_transport_speed, as_default)); + DEBUG_TRACE (DEBUG::Transport, string_compose ("@ %1 Set transport speed to %2 from %3 (es = %4)\n", _transport_sample, speed, _transport_fsm->transport_speed(), _engine_speed)); assert (speed != 0.0); @@ -411,7 +339,7 @@ Session::set_transport_speed (double speed, bool as_default, bool at_next_start) */ - if ((_engine_speed != 1) && (_engine_speed == fabs (speed)) && ((speed * _transport_speed) > 0)) { + if ((_engine_speed != 1) && (_engine_speed == fabs (speed)) && ((speed * _transport_fsm->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; @@ -428,7 +356,7 @@ Session::set_transport_speed (double speed, bool as_default, bool at_next_start) } double new_engine_speed = fabs (speed); - double new_transport_speed = (speed < 0) ? -1 : 1; + // double new_transport_speed = (speed < 0) ? -1 : 1; if ((synced_to_engine()) && speed != 0.0 && speed != 1.0) { warning << string_compose ( @@ -438,27 +366,17 @@ Session::set_transport_speed (double speed, bool as_default, bool at_next_start) return; } - if (at_next_start) { - _requested_transport_speed = speed; - } else { - clear_clicks (); - _transport_speed = new_transport_speed; - _engine_speed = new_engine_speed; - } + clear_clicks (); + _engine_speed = new_engine_speed; - if (as_default) { - _default_engine_speed = new_engine_speed; - _default_transport_speed = new_transport_speed; - } - - DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC3 with speed = %1\n", _transport_speed)); + DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC3 with speed = %1\n", _transport_fsm->transport_speed())); /* throttle signal emissions. - * when slaved [_last]_transport_speed + * when slaved [_last]_transport_fsm->transport_speed() * usually changes every cycle (tiny amounts due to DLL). * Emitting a signal every cycle is overkill and unwarranted. * - * Using _transport_speed is not acceptable, + * Using _transport_fsm->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 * @@ -517,8 +435,8 @@ Session::start_transport () } } - if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { - set_track_monitor_input_status (false); + if (Config->get_monitoring_model() == HardwareMonitoring) { + set_track_monitor_input_status (!config.get_auto_input()); } _last_roll_location = _transport_sample; @@ -560,15 +478,6 @@ Session::start_transport () maybe_allow_only_loop (); maybe_allow_only_punch (); - if (_requested_transport_speed != std::numeric_limits::max()) { - _engine_speed = fabs (_requested_transport_speed); - _transport_speed = _requested_transport_speed > 0 ? 1 : -1; - _requested_transport_speed = std::numeric_limits::max(); - } else { - _transport_speed = _default_transport_speed; - _engine_speed = _default_engine_speed; - } - clear_clicks (); if (!_engine.freewheeling()) { @@ -625,7 +534,7 @@ Session::start_transport () } } - DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC4 with speed = %1\n", _transport_speed)); + DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC4 with speed = %1\n", transport_speed())); TransportStateChange (); /* EMIT SIGNAL */ } @@ -638,6 +547,31 @@ Session::need_declick_before_locate () const return naudiotracks() > 0; } +bool +Session::should_stop_before_locate () const +{ + /* do "stopped" stuff if: + * + * we are rolling AND + * no autoplay in effect AND + * we're not synced to an external transport master + * + */ + + if ((!auto_play_legal || !config.get_auto_play()) && + !(config.get_external_sync() && !synced_to_engine())) { + + return true; + } + return false; +} + +bool +Session::user_roll_after_locate () const +{ + return auto_play_legal && config.get_auto_play(); +} + bool Session::should_roll_after_locate () const { @@ -688,13 +622,6 @@ Session::butler_completed_transport_work () if (_transport_fsm->waiting_for_butler()) { TFSM_EVENT (TransportFSM::ButlerDone); } - - if (start_after_butler_done_msg) { - if (_transport_speed) { - /* reversal is done ... tell TFSM that it is time to start*/ - TFSM_EVENT (TransportFSM::StartTransport); - } - } } void @@ -709,7 +636,7 @@ bool Session::maybe_stop (samplepos_t limit) { ENSURE_PROCESS_THREAD; - if ((_transport_speed > 0.0f && _transport_sample >= limit) || (_transport_speed < 0.0f && _transport_sample == 0)) { + if ((_transport_fsm->transport_speed() > 0.0f && _transport_sample >= limit) || (_transport_fsm->transport_speed() < 0.0f && _transport_sample == 0)) { if (synced_to_engine ()) { _engine.transport_stop (); } else { @@ -868,8 +795,8 @@ Session::request_roll (TransportRequestSource origin) return; } - SessionEvent* ev = new SessionEvent (SessionEvent::StartRoll, SessionEvent::Add, SessionEvent::Immediate, audible_sample(), _default_engine_speed * _default_transport_speed); - DEBUG_TRACE (DEBUG::Transport, string_compose ("Request transport roll, requested %1 from %2 * %3 transport @ %4\n", _default_engine_speed * _default_transport_speed, _default_engine_speed, _default_transport_speed, _transport_sample)); + SessionEvent* ev = new SessionEvent (SessionEvent::StartRoll, SessionEvent::Add, SessionEvent::Immediate, 0, false); /* final 2 argumment do not matter */ + DEBUG_TRACE (DEBUG::Transport, "Request transport roll\n"); queue_event (ev); } @@ -1580,7 +1507,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) } PositionChanged (_transport_sample); /* EMIT SIGNAL */ - DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC with speed = %1\n", _transport_speed)); + DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC with speed = %1\n", _transport_fsm->transport_speed())); TransportStateChange (); /* EMIT SIGNAL */ AutomationWatch::instance().transport_stop_automation_watches (_transport_sample); } @@ -1641,7 +1568,7 @@ Session::set_play_loop (bool yn, bool change_transport_state) unset_play_loop (); } - DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC2 with speed = %1\n", _transport_speed)); + DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC2 with speed = %1\n", _transport_fsm->transport_speed())); } void @@ -1780,7 +1707,7 @@ Session::set_play_range (list& range, bool leave_rolling) ev = new SessionEvent (SessionEvent::LocateRoll, SessionEvent::Add, SessionEvent::Immediate, range.front().start, 0.0f, false); merge_event (ev); - DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC5 with speed = %1\n", _transport_speed)); + DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC5 with speed = %1\n", _transport_fsm->transport_speed())); TransportStateChange (); /* EMIT SIGNAL */ } @@ -2053,7 +1980,7 @@ Session::transport_state_rolling() const bool Session::transport_rolling() const { - return _transport_speed != 0.0 && _count_in_samples == 0 && _remaining_latency_preroll == 0; + return _transport_fsm->transport_speed() != 0.0 && _count_in_samples == 0 && _remaining_latency_preroll == 0; } bool @@ -2079,3 +2006,20 @@ Session::transport_will_roll_forwards () const { return _transport_fsm->will_roll_fowards (); } + +double +Session::transport_speed() const +{ + if (_transport_fsm->transport_speed() != _transport_fsm->transport_speed()) { + // cerr << "\n\n!!TS " << _transport_fsm->transport_speed() << " TFSM::speed " << _transport_fsm->transport_speed() << " via " << _transport_fsm->current_state() << endl; + } + return _count_in_samples > 0 ? 0. : _transport_fsm->transport_speed(); +} + +double +Session::actual_speed() const +{ + if (_transport_fsm->transport_speed() > 0) return _engine_speed; + if (_transport_fsm->transport_speed() < 0) return - _engine_speed; + return 0; +} diff --git a/libs/ardour/transport_fsm.cc b/libs/ardour/transport_fsm.cc index 062a3be493..3e4c155af5 100644 --- a/libs/ardour/transport_fsm.cc +++ b/libs/ardour/transport_fsm.cc @@ -37,6 +37,29 @@ using namespace PBD; Pool* TransportFSM::Event::pool = 0; +/* + +autoplay off (hint: state remains the same): + +[STOPPED] -> LOCATE -> [STOPPED] +[ROLLING] -> LOCATE -> [ROLLING] +[RWD|FFWD] -> LOCATE -> [RWD|FFWD] + +autoplay on (hint: state is always rolling): + +[STOPPED] -> LOCATE -> [ROLLING] +[ROLLING] -> LOCATE -> [ROLLING] +[RWD|FFWD] -> LOCATE -> [ROLLING] + +the problem child is the last one. The final ROLLING state is intended to be +forwards at the default speed. But if we were rewinding, we need a reverse. + +if autoplay is the determining factor in differentiating these two, we must +make it available via TransportAPI. but let's rename it as something slightly +more on-topic. + +*/ + void TransportFSM::Event::init_pool () { @@ -56,11 +79,15 @@ TransportFSM::Event::operator delete (void *ptr, size_t /*size*/) } TransportFSM::TransportFSM (TransportAPI& tapi) - : _last_locate (Locate, 0, MustRoll, false, false) /* all but first argument don't matter */ - , last_speed_request (0, false) /* SetSpeed request */ + : _motion_state (Stopped) + , _butler_state (NotWaitingForButler) + , _direction_state (Forwards) + , _transport_speed (0) + , _last_locate (Locate, max_samplepos, MustRoll, false, false) /* all but first two argument don't matter */ , api (&tapi) , processing (0) , most_recently_requested_speed (std::numeric_limits::max()) + , default_speed (1.0) { init (); } @@ -68,10 +95,6 @@ TransportFSM::TransportFSM (TransportAPI& tapi) void TransportFSM::init () { - _motion_state = Stopped; - _butler_state = NotWaitingForButler; - _direction_state = Forwards; - _last_locate.target = max_samplepos; } void @@ -119,7 +142,7 @@ TransportFSM::process_events () e = deferred_events.erase (e); delete deferred_ev; } else { - DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("deferred event %1 re-deferred\n", enum_2_string (deferred_ev->type))); + DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("Re-Defer deferred event %1\n", enum_2_string (deferred_ev->type))); ++e; } } else { /* process error */ @@ -203,7 +226,6 @@ void TransportFSM::bad_transition (Event const & ev) { error << "bad transition, current state = " << current_state() << " event = " << enum_2_string (ev.type) << endmsg; - std::cerr << "bad transition, current state = " << current_state() << " event = " << enum_2_string (ev.type) << std::endl; PBD::stacktrace (std::cerr, 30); } @@ -292,7 +314,7 @@ TransportFSM::process_event (Event& ev, bool already_deferred, bool& deferred) break; case Locate: - DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("locate, ltd = %1 target = %2 loop %3 force %4\n", + DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("locate, ltd = %1 target = %2 for loop end %3 force %4\n", enum_2_string (ev.ltd), ev.target, ev.for_loop_end, @@ -447,6 +469,15 @@ TransportFSM::start_playback () _last_locate.target = max_samplepos; current_roll_after_locate_status = boost::none; + if (most_recently_requested_speed == std::numeric_limits::max()) { + /* we started rolling without ever setting speed; that's an implicit + * call to set_speed (1.0) + */ + most_recently_requested_speed = 1.0; + } else { + api->set_transport_speed (most_recently_requested_speed); + } + api->start_transport(); } @@ -458,21 +489,40 @@ TransportFSM::stop_playback (Event const & s) _last_locate.target = max_samplepos; current_roll_after_locate_status = boost::none; - api->stop_transport (s.abort_capture, s.clear_state); -} + if (!declicking_for_locate()) { -void -TransportFSM::set_roll_after (bool with_roll) const -{ - current_roll_after_locate_status = with_roll; + if (Config->get_reset_default_speed_on_stop()) { + + if (most_recently_requested_speed != 1.0) { + set_speed (Event (1.0, false)); + } + + } else { + + /* We're not resetting back to 1.0, but we may need to handle a + * speed change from whatever we have been rolling at to + * whatever the current default is. We could have been + * rewinding at -4.5 ... when we restart, we need to play at + * the current _default_transport_speed + */ + + + if (most_recently_requested_speed != default_speed) { + set_speed (Event (default_speed, false)); + + } + } + } + + api->stop_transport (s.abort_capture, s.clear_state); } void TransportFSM::start_declick_for_locate (Event const & l) { assert (l.type == Locate); - DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("start_declick_for_locate, crals %1 ltd %2 speed %3 sral %4\n", (bool) current_roll_after_locate_status, - enum_2_string (l.ltd), api->speed(), api->should_roll_after_locate())); + DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("start_declick_for_locate, crals %1 ltd %2 sral %3\n", (bool) current_roll_after_locate_status, + enum_2_string (l.ltd), api->should_roll_after_locate())); _last_locate = l; if (!current_roll_after_locate_status) { @@ -481,17 +531,6 @@ TransportFSM::start_declick_for_locate (Event const & l) api->stop_transport (false, false); } -void -TransportFSM::start_locate_while_stopped (Event const & l) const -{ - assert (l.type == Locate); - DEBUG_TRACE (DEBUG::TFSMEvents, "start_locate_while_stopped\n"); - - set_roll_after (compute_should_roll (l.ltd)); - - api->locate (l.target, current_roll_after_locate_status.get(), l.for_loop_end, l.force); -} - bool TransportFSM::compute_should_roll (LocateTransportDisposition ltd) const { @@ -500,19 +539,37 @@ TransportFSM::compute_should_roll (LocateTransportDisposition ltd) const return true; case MustStop: return false; - case RollIfAppropriate: - /* by the time we call this, if we were rolling before the - locate, we've already transitioned into DeclickToLocate - */ - if (_motion_state == DeclickToLocate) { - return true; - } else { - return api->should_roll_after_locate (); - } + default: break; } - /*NOTREACHED*/ - return true; + + /* case RollIfAppropriate */ + + /* by the time we call this, if we were rolling before the + locate, we've already transitioned into DeclickToLocate, + so DeclickToLocate is essentially a synonym for "was Rolling". + */ + if (_motion_state == DeclickToLocate) { + return true; + } + + return api->should_roll_after_locate (); +} + +void +TransportFSM::set_roll_after (bool with_roll) const +{ + current_roll_after_locate_status = with_roll; +} + +void +TransportFSM::start_locate_while_stopped (Event const & l) const +{ + assert (l.type == Locate); + DEBUG_TRACE (DEBUG::TFSMEvents, "start_locate_while_stopped\n"); + + set_roll_after (compute_should_roll (l.ltd)); + api->locate (l.target, l.for_loop_end, l.force); } void @@ -521,20 +578,43 @@ TransportFSM::locate_for_loop (Event const & l) assert (l.type == Locate); DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("locate_for_loop, wl = %1\n", l.for_loop_end)); - const bool should_roll = compute_should_roll (l.ltd); - current_roll_after_locate_status = should_roll; _last_locate = l; - api->locate (l.target, should_roll, l.for_loop_end, l.force); + set_roll_after (compute_should_roll (l.ltd)); + api->locate (l.target, l.for_loop_end, l.force); } void -TransportFSM::start_locate_after_declick () const +TransportFSM::start_locate_after_declick () { DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("start_locate_after_declick, have crals ? %1 roll will be %2\n", (bool) current_roll_after_locate_status, current_roll_after_locate_status ? current_roll_after_locate_status.get() : compute_should_roll (_last_locate.ltd))); - const bool roll = current_roll_after_locate_status ? current_roll_after_locate_status.get() : compute_should_roll (_last_locate.ltd); - api->locate (_last_locate.target, roll, _last_locate.for_loop_end, _last_locate.force); + /* we only get here because a locate request arrived while we were rolling. We declicked and that is now finished */ + + /* Special case: we were rolling. If the user has set auto-play, then + post-locate, we should roll at the default speed, which may involve + a reversal and that needs to be setup before we actually locate. + */ + + double post_locate_speed; + + if (api->user_roll_after_locate()) { + post_locate_speed = default_speed; + } else { + post_locate_speed = most_recently_requested_speed; + } + + if (post_locate_speed * most_recently_requested_speed < 0) { + /* different directions */ + transition (Reversing); + } + + if (api->user_roll_after_locate()) { + most_recently_requested_speed = post_locate_speed; + } + + + api->locate (_last_locate.target, _last_locate.for_loop_end, _last_locate.force); } void @@ -563,7 +643,7 @@ TransportFSM::interrupt_locate (Event const & l) const * we are interrupting the locate to start a new one. */ _last_locate = l; - api->locate (l.target, false, l.for_loop_end, l.force); + api->locate (l.target, l.for_loop_end, l.force); } void @@ -593,6 +673,15 @@ TransportFSM::roll_after_locate () const { DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("rolling after locate, was for_loop ? %1\n", _last_locate.for_loop_end)); current_roll_after_locate_status = boost::none; + + if (most_recently_requested_speed == std::numeric_limits::max()) { + /* we started rolling without ever setting speed; that's an implicit + * call to set_speed (1.0) + */ + most_recently_requested_speed = 1.0; + } + + api->set_transport_speed (most_recently_requested_speed); api->start_transport (); } @@ -608,6 +697,7 @@ TransportFSM::transition (MotionState ms) { const MotionState old = _motion_state; _motion_state = ms; + _transport_speed = compute_transport_speed (); DEBUG_TRACE (DEBUG::TFSMState, string_compose ("Leave %1, enter %2\n", enum_2_string (old), current_state())); } @@ -616,6 +706,7 @@ TransportFSM::transition (ButlerState bs) { const ButlerState old = _butler_state; _butler_state = bs; + _transport_speed = compute_transport_speed (); DEBUG_TRACE (DEBUG::TFSMState, string_compose ("Leave %1, enter %2\n", enum_2_string (old), current_state())); } @@ -624,6 +715,7 @@ TransportFSM::transition (DirectionState ds) { const DirectionState old = _direction_state; _direction_state = ds; + _transport_speed = compute_transport_speed (); DEBUG_TRACE (DEBUG::TFSMState, string_compose ("Leave %1, enter %2\n", enum_2_string (old), current_state())); } @@ -638,7 +730,7 @@ TransportFSM::enqueue (Event* ev) } int -TransportFSM::transport_speed() const +TransportFSM::compute_transport_speed () const { if (_motion_state != Rolling || _direction_state == Reversing) { return 0; @@ -687,7 +779,7 @@ TransportFSM::set_speed (Event const & ev) must_reverse = true; } - api->set_transport_speed (ev.speed, ev.as_default, !rolling() || must_reverse); + api->set_transport_speed (ev.speed); /* corner case: first call to ::set_speed() has a negative * speed @@ -695,19 +787,27 @@ TransportFSM::set_speed (Event const & ev) most_recently_requested_speed = ev.speed; + if (ev.as_default) { + default_speed = ev.speed; + } + if (must_reverse) { /* direction change */ DEBUG_TRACE (DEBUG::TFSMState, string_compose ("switch-directions, target speed %1 state %2 IR %3\n", ev.speed, current_state(), initial_reverse)); - last_speed_request = ev; transition (Reversing); Event lev (Locate, api->position(), must_roll ? MustRoll : MustStop, false, true); - transition (DeclickToLocate); - start_declick_for_locate (lev); + if (_transport_speed) { + transition (DeclickToLocate); + start_declick_for_locate (lev); + } else { + transition (WaitingForLocate); + start_locate_while_stopped (lev); + } } }