summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2019-09-17 18:26:03 -0600
committerPaul Davis <paul@linuxaudiosystems.com>2019-09-17 18:26:03 -0600
commitbd229936ec2cb30fe3360f2d83ffc7197fd6fb97 (patch)
tree84e41e048d76541d5d491ccefe8c7feaa14f4d5c /libs
parentfc3e7623e7a4c7fdeebf84c4d47eaf04d9bc744d (diff)
add finite state machine to control/manage transport state
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/ardour/debug.h4
-rw-r--r--libs/ardour/ardour/disk_io.h2
-rw-r--r--libs/ardour/ardour/disk_reader.h15
-rw-r--r--libs/ardour/ardour/export_status.h4
-rw-r--r--libs/ardour/ardour/session.h38
-rw-r--r--libs/ardour/ardour/session_event.h1
-rw-r--r--libs/ardour/ardour/transport_api.h47
-rw-r--r--libs/ardour/ardour/transport_fsm.h213
-rw-r--r--libs/ardour/ardour/transport_master.h14
-rw-r--r--libs/ardour/ardour/transport_master_manager.h3
-rw-r--r--libs/ardour/bundle.cc4
-rw-r--r--libs/ardour/debug.cc4
-rw-r--r--libs/ardour/disk_reader.cc72
-rw-r--r--libs/ardour/enums.cc1
-rw-r--r--libs/ardour/export_status.cc4
-rw-r--r--libs/ardour/globals.cc6
-rw-r--r--libs/ardour/ltc_slave.cc12
-rw-r--r--libs/ardour/midi_clock_slave.cc11
-rw-r--r--libs/ardour/mtc_slave.cc12
-rw-r--r--libs/ardour/playlist.cc2
-rw-r--r--libs/ardour/port.cc2
-rw-r--r--libs/ardour/rt_tasklist.cc18
-rw-r--r--libs/ardour/session.cc22
-rw-r--r--libs/ardour/session_butler.cc3
-rw-r--r--libs/ardour/session_events.cc1
-rw-r--r--libs/ardour/session_export.cc18
-rw-r--r--libs/ardour/session_process.cc295
-rw-r--r--libs/ardour/session_state.cc1
-rw-r--r--libs/ardour/session_time.cc5
-rw-r--r--libs/ardour/session_transport.cc226
-rw-r--r--libs/ardour/track.cc2
-rw-r--r--libs/ardour/transport_fsm.cc101
-rw-r--r--libs/ardour/transport_master.cc66
-rw-r--r--libs/ardour/transport_master_manager.cc97
-rw-r--r--libs/ardour/wscript1
-rw-r--r--libs/pbd/transmitter.cc5
36 files changed, 885 insertions, 447 deletions
diff --git a/libs/ardour/ardour/debug.h b/libs/ardour/ardour/debug.h
index f8cfef4009..cd96aba33a 100644
--- a/libs/ardour/ardour/debug.h
+++ b/libs/ardour/ardour/debug.h
@@ -34,7 +34,7 @@ namespace PBD {
namespace DEBUG {
LIBARDOUR_API extern DebugBits MidiSourceIO;
LIBARDOUR_API extern DebugBits MidiPlaylistIO;
- LIBARDOUR_API extern DebugBits MidiDiskstreamIO;
+ LIBARDOUR_API extern DebugBits MidiDiskIO;
LIBARDOUR_API extern DebugBits MidiRingBuffer;
LIBARDOUR_API extern DebugBits SnapBBT;
LIBARDOUR_API extern DebugBits Latency;
@@ -49,6 +49,8 @@ namespace PBD {
LIBARDOUR_API extern DebugBits LTC;
LIBARDOUR_API extern DebugBits TXLTC;
LIBARDOUR_API extern DebugBits Transport;
+ LIBARDOUR_API extern DebugBits TFSMEvents;
+ LIBARDOUR_API extern DebugBits TFSMState;
LIBARDOUR_API extern DebugBits Slave;
LIBARDOUR_API extern DebugBits SessionEvents;
LIBARDOUR_API extern DebugBits MidiIO;
diff --git a/libs/ardour/ardour/disk_io.h b/libs/ardour/ardour/disk_io.h
index 60ac7be124..52cfe45d0e 100644
--- a/libs/ardour/ardour/disk_io.h
+++ b/libs/ardour/ardour/disk_io.h
@@ -117,8 +117,6 @@ protected:
protected:
Flag _flags;
uint32_t i_am_the_modifier;
- double _actual_speed;
- double _target_speed;
bool _slaved;
bool in_set_state;
samplepos_t playback_sample;
diff --git a/libs/ardour/ardour/disk_reader.h b/libs/ardour/ardour/disk_reader.h
index a7a74027f0..47812f54c8 100644
--- a/libs/ardour/ardour/disk_reader.h
+++ b/libs/ardour/ardour/disk_reader.h
@@ -97,8 +97,17 @@ public:
static void set_midi_readahead_samples (samplecnt_t samples_ahead) { midi_readahead = samples_ahead; }
- static void set_no_disk_output (bool yn);
- static bool no_disk_output() { return _no_disk_output; }
+ /* inc/dec variants MUST be called as part of the process call tree, before any
+ disk readers are invoked. We use it when the session needs the
+ transport (and thus effective read position for DiskReaders) to keep
+ advancing as part of syncing up with a transport master, but we
+ don't want any actual disk output yet because we are still not
+ synced.
+ */
+
+ static void inc_no_disk_output () { g_atomic_int_inc (&_no_disk_output); }
+ static void dec_no_disk_output();
+ static bool no_disk_output () { return g_atomic_int_get (&_no_disk_output); }
protected:
friend class Track;
@@ -156,7 +165,7 @@ private:
static samplecnt_t _chunk_samples;
static samplecnt_t midi_readahead;
- static bool _no_disk_output;
+ static gint _no_disk_output;
int audio_read (PBD::PlaybackBuffer<Sample>*,
Sample* sum_buffer,
diff --git a/libs/ardour/ardour/export_status.h b/libs/ardour/ardour/export_status.h
index 0fb4e39a78..7f36e60e06 100644
--- a/libs/ardour/ardour/export_status.h
+++ b/libs/ardour/ardour/export_status.h
@@ -54,8 +54,8 @@ class LIBARDOUR_API ExportStatus {
}
Glib::Threads::Mutex& lock () { return _run_lock; }
- PBD::Signal0<void> Finished;
- void finish ();
+ PBD::Signal1<void,TransportRequestSource> Finished;
+ void finish (TransportRequestSource);
void cleanup ();
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index d2d0b39dbd..337b88a8e8 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -88,7 +88,7 @@
#include "ardour/presentation_info.h"
#include "ardour/route.h"
#include "ardour/route_graph.h"
-
+#include "ardour/transport_api.h"
class XMLTree;
class XMLNode;
@@ -167,6 +167,7 @@ class Source;
class Speakers;
class TempoMap;
class TransportMaster;
+struct TransportFSM;
class Track;
class UI_TransportMaster;
class VCAManager;
@@ -186,7 +187,7 @@ private:
};
/** Ardour Session */
-class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionList, public SessionEventManager
+class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionList, public SessionEventManager, public TransportAPI
{
private:
@@ -457,7 +458,8 @@ public:
void adjust_capture_buffering();
bool global_locate_pending() const { return _global_locate_pending; }
- bool locate_pending() const { return static_cast<bool>(post_transport_work()&PostTransportLocate); }
+ bool locate_pending() const;
+ bool declick_in_progress () const;
bool transport_locked () const;
int wipe ();
@@ -752,8 +754,8 @@ public:
return 0;
}
double transport_speed() const { return _count_in_samples > 0 ? 0. : _transport_speed; }
- bool transport_stopped() const { return _transport_speed == 0.0; }
- bool transport_rolling() const { return _transport_speed != 0.0 && _count_in_samples == 0 && _remaining_latency_preroll == 0; }
+ bool transport_stopped() const;
+ bool transport_rolling() const;
bool silent () { return _silent; }
@@ -1237,6 +1239,16 @@ protected:
friend class Route;
void update_latency_compensation (bool force = false);
+ /* transport API */
+
+ void locate (samplepos_t, bool with_roll, bool with_flush, bool with_loop=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;
+
private:
int create (const std::string& mix_template, BusProfile*);
void destroy ();
@@ -1262,7 +1274,6 @@ private:
samplecnt_t _base_sample_rate; // sample-rate of the session at creation time, "native" SR
samplecnt_t _nominal_sample_rate; // overridden by audioengine setting
samplecnt_t _current_sample_rate; // this includes video pullup offset
- int transport_sub_state;
mutable gint _record_status;
samplepos_t _transport_sample;
gint _seek_counter;
@@ -1357,7 +1368,7 @@ private:
int pre_export ();
int stop_audio_export ();
- void finalize_audio_export ();
+ void finalize_audio_export (TransportRequestSource trs);
void finalize_export_internal (bool stop_freewheel);
bool _pre_export_mmc_enabled;
@@ -1436,11 +1447,11 @@ private:
Butler* _butler;
+ boost::shared_ptr<TransportFSM> _transport_fsm;
+
static const PostTransportWork ProcessCannotProceedMask =
PostTransportWork (
- PostTransportReverse|
PostTransportAudition|
- PostTransportStop|
PostTransportClearSubstate);
gint _post_transport_work; /* accessed only atomic ops */
@@ -1671,19 +1682,16 @@ private:
int start_midi_thread ();
bool should_ignore_transport_request (TransportRequestSource, TransportRequestType) const;
- bool declick_in_progress () const;
void set_play_loop (bool yn, double speed);
void unset_play_loop ();
void overwrite_some_buffers (Track *);
void flush_all_inserts ();
int micro_locate (samplecnt_t distance);
- void locate (samplepos_t, bool with_roll, bool with_flush, bool with_loop=false, bool force=false, bool with_mmc=true);
- void start_locate (samplepos_t, bool with_roll, bool with_flush, bool for_loop_enabled=false, bool force=false);
+
+ void do_locate (samplepos_t, bool with_roll, bool with_flush, bool for_loop_enabled, bool force, bool with_mmc);
void force_locate (samplepos_t sample, bool with_roll = false);
void set_transport_speed (double speed, samplepos_t destination_sample, bool abort = false, bool clear_state = false, bool as_default = false);
- void stop_transport (bool abort = false, bool clear_state = false);
- void start_transport ();
void realtime_stop (bool abort, bool clear_state);
void realtime_locate ();
void non_realtime_start_scrub ();
@@ -1691,8 +1699,8 @@ private:
void non_realtime_locate ();
void non_realtime_stop (bool abort, int entry_request_count, bool& finished);
void non_realtime_overwrite (int entry_request_count, bool& finished);
- void post_transport ();
void engine_halted ();
+ void engine_running ();
void xrun_recovery ();
void set_track_loop (bool);
bool select_playhead_priority_target (samplepos_t&);
diff --git a/libs/ardour/ardour/session_event.h b/libs/ardour/ardour/session_event.h
index bb85624305..98fe6838f5 100644
--- a/libs/ardour/ardour/session_event.h
+++ b/libs/ardour/ardour/session_event.h
@@ -63,7 +63,6 @@ public:
/* only one of each of these events can be queued at any one time */
- StopOnce,
AutoLoop,
};
diff --git a/libs/ardour/ardour/transport_api.h b/libs/ardour/ardour/transport_api.h
new file mode 100644
index 0000000000..8ca7aeb074
--- /dev/null
+++ b/libs/ardour/ardour/transport_api.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 Paul Davis <paul@linuxaudiosystems.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _ardour_transport_api_h_
+#define _ardour_transport_api_h_
+
+#include "ardour/types.h"
+#include "ardour/libardour_visibility.h"
+
+namespace ARDOUR
+{
+
+class LIBARDOUR_API TransportAPI
+{
+ public:
+ TransportAPI() {}
+ virtual ~TransportAPI() {}
+
+ private:
+ friend struct TransportFSM;
+
+ virtual void locate (samplepos_t, bool with_roll, bool with_flush, bool with_loop=false, bool force=false, bool with_mmc=true) = 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;
+};
+
+} /* end namespace ARDOUR */
+
+#endif
diff --git a/libs/ardour/ardour/transport_fsm.h b/libs/ardour/ardour/transport_fsm.h
new file mode 100644
index 0000000000..6a920eef4d
--- /dev/null
+++ b/libs/ardour/ardour/transport_fsm.h
@@ -0,0 +1,213 @@
+#ifndef _ardour_transport_fsm_h_
+#define _ardour_transport_fsm_h_
+
+#include <boost/weak_ptr.hpp>
+#include <boost/msm/back/state_machine.hpp>
+#include <boost/msm/back/tools.hpp>
+#include <boost/msm/front/state_machine_def.hpp>
+#include <boost/msm/front/functor_row.hpp>
+
+#include "pbd/demangle.h"
+#include "pbd/stacktrace.h"
+
+#include "ardour/debug.h"
+
+/* state machine */
+namespace msm = boost::msm;
+namespace mpl = boost::mpl;
+
+namespace ARDOUR
+{
+
+class TransportAPI;
+
+struct TransportFSM : public msm::front::state_machine_def<TransportFSM>
+{
+ /* events to be delivered to the FSM */
+
+ struct butler_done {};
+ struct butler_required {};
+ struct declick_done {};
+ struct start_transport {};
+
+ struct stop_transport {
+ stop_transport (bool ab = false, bool cl = false)
+ : abort (ab)
+ , clear_state (cl) {}
+
+ bool abort;
+ bool clear_state;
+ };
+
+ struct locate {
+ locate ()
+ : target (0)
+ , with_roll (false)
+ , with_flush (false)
+ , with_loop (false)
+ , force (false) {}
+
+ locate (samplepos_t target, bool roll, bool flush, bool loop, bool f4c)
+ : target (target)
+ , with_roll (roll)
+ , with_flush (flush)
+ , with_loop (loop)
+ , force (f4c) {}
+
+ samplepos_t target;
+ bool with_roll;
+ bool with_flush;
+ bool with_loop;
+ bool force;
+ };
+
+ struct locate_done {};
+
+ /* Flags */
+
+ struct DeclickInProgress {};
+ struct LocateInProgress {};
+ struct IsRolling {};
+ struct IsStopped {};
+ struct IsWaitingForButler {};
+
+ typedef msm::active_state_switch_before_transition active_state_switch_policy;
+
+ /* transition actions */
+
+ void start_playback (start_transport const& p);
+ void roll_after_locate (locate_done const& p);
+ void stop_playback (declick_done const& s);
+ void start_locate (locate const& s);
+ void start_saved_locate (declick_done const& s);
+ void interrupt_locate (locate const& s);
+ void schedule_butler_for_transport_work (butler_required const&);
+ void save_locate_and_start_declick (locate const &);
+ void start_declick (stop_transport const &);
+
+ /* guards */
+
+ bool should_roll_after_locate (locate_done const &);
+ bool should_not_roll_after_locate (locate_done const & e) { return !should_roll_after_locate (e); }
+
+#define define_state(State) \
+ struct State : public msm::front::state<> \
+ { \
+ template <class Event,class FSM> void on_entry (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "entering: " # State "\n"); } \
+ template <class Event,class FSM> void on_exit (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "leaving: " # State "\n"); } \
+ }
+
+#define define_state_flag(State,Flag) \
+ struct State : public msm::front::state<> \
+ { \
+ template <class Event,class FSM> void on_entry (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "entering: " # State "\n"); } \
+ template <class Event,class FSM> void on_exit (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "leaving: " # State "\n"); } \
+ typedef mpl::vector1<Flag> flag_list; \
+ }
+
+#define define_state_flag2(State,Flag1,Flag2) \
+ struct State : public msm::front::state<> \
+ { \
+ template <class Event,class FSM> void on_entry (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "entering: " # State "\n"); } \
+ template <class Event,class FSM> void on_exit (Event const&, FSM&) { DEBUG_TRACE (PBD::DEBUG::TFSMState, "leaving: " # State "\n"); } \
+ typedef mpl::vector2<Flag1,Flag2> flag_list; \
+ }
+
+ /* FSM states */
+
+ define_state_flag (WaitingForButler, IsWaitingForButler);
+ define_state (NotWaitingForButler);
+ define_state_flag (Stopped,IsStopped);
+ define_state_flag (Rolling,IsRolling);
+ define_state_flag (DeclickToLocate,DeclickInProgress);
+ define_state_flag (WaitingForLocate,LocateInProgress);
+ define_state_flag (DeclickToStop,DeclickInProgress);
+
+ // Pick a back-end
+ typedef msm::back::state_machine<TransportFSM> back;
+
+ boost::weak_ptr<back> wp;
+
+ bool locating () { return backend()->is_flag_active<LocateInProgress>(); }
+ bool locating (declick_done const &) { return locating(); }
+ bool rolling () { return backend()->is_flag_active<IsRolling>(); }
+ bool stopped () { return backend()->is_flag_active<IsStopped>(); }
+ bool waiting_for_butler() { return backend()->is_flag_active<IsWaitingForButler>(); }
+ bool declick_in_progress() { return backend()->is_flag_active<DeclickInProgress>(); }
+
+ static boost::shared_ptr<back> create(TransportAPI& api) {
+
+ boost::shared_ptr<back> p (new back ());
+
+ p->wp = p;
+ p->api = &api;
+ return p;
+ }
+
+ boost::shared_ptr<back> backend() { return wp.lock(); }
+
+ template<typename Event> void enqueue (Event const & e) {
+ backend()->process_event (e);
+ }
+
+ /* the initial state */
+ typedef boost::mpl::vector<Stopped,NotWaitingForButler> initial_state;
+
+ /* transition table */
+ typedef TransportFSM T; // makes transition table cleaner
+
+ struct transition_table : mpl::vector<
+ // Start Event Next Action Guard
+ // +----------------------+----------------+------------------+---------------------+----------------------+
+ a_row < Stopped, start_transport, Rolling, &T::start_playback >,
+ _row < Stopped, stop_transport, Stopped >,
+ a_row < Stopped, locate, WaitingForLocate, &T::start_locate >,
+ g_row < WaitingForLocate, locate_done, Stopped, &T::should_not_roll_after_locate >,
+ _row < Rolling, butler_done, Rolling >,
+ _row < Rolling, start_transport, Rolling >,
+ a_row < Rolling, stop_transport, DeclickToStop, &T::start_declick >,
+ a_row < DeclickToStop, declick_done, Stopped, &T::stop_playback >,
+ a_row < Rolling, locate, DeclickToLocate, &T::save_locate_and_start_declick >,
+ a_row < DeclickToLocate, declick_done, WaitingForLocate, &T::start_saved_locate >,
+ row < WaitingForLocate, locate_done, Rolling, &T::roll_after_locate, &T::should_roll_after_locate >,
+ a_row < NotWaitingForButler, butler_required, WaitingForButler, &T::schedule_butler_for_transport_work >,
+ a_row < WaitingForButler, butler_required, WaitingForButler, &T::schedule_butler_for_transport_work >,
+ _row < WaitingForButler, butler_done, NotWaitingForButler >,
+ a_row < WaitingForLocate, locate, WaitingForLocate, &T::interrupt_locate >,
+ a_row < DeclickToLocate, locate, DeclickToLocate, &T::interrupt_locate >,
+
+ // Deferrals
+
+#define defer(start_state,ev) boost::msm::front::Row<start_state, ev, start_state, boost::msm::front::Defer, boost::msm::front::none >
+
+ defer (DeclickToLocate, start_transport),
+ defer (DeclickToLocate, stop_transport),
+ defer (DeclickToStop, start_transport),
+ defer (WaitingForLocate, start_transport),
+ defer (WaitingForLocate, stop_transport)
+
+#undef defer
+ > {};
+
+ typedef int activate_deferred_events;
+
+ locate _last_locate;
+ stop_transport _last_stop;
+
+ TransportAPI* api;
+
+ // Replaces the default no-transition response.
+ template <class FSM,class Event>
+ void no_transition(Event const& e, FSM&,int state)
+ {
+ typedef typename boost::msm::back::recursive_get_transition_table<FSM>::type recursive_stt;
+ typedef typename boost::msm::back::generate_state_set<recursive_stt>::type all_states;
+ std::string stateName;
+ boost::mpl::for_each<all_states,boost::msm::wrap<boost::mpl::placeholders::_1> >(boost::msm::back::get_state_name<recursive_stt>(stateName, state));
+ std::cout << "No transition from state: " << PBD::demangle (stateName) << " on event " << typeid(e).name() << std::endl;
+ }
+};
+
+} /* end namespace ARDOUR */
+
+#endif
diff --git a/libs/ardour/ardour/transport_master.h b/libs/ardour/ardour/transport_master.h
index 3dcfe4cd8f..98c4f11156 100644
--- a/libs/ardour/ardour/transport_master.h
+++ b/libs/ardour/ardour/transport_master.h
@@ -346,8 +346,6 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful {
TransportRequestType request_mask() const { return _request_mask; }
void set_request_mask (TransportRequestType);
- void get_current (double&, samplepos_t&, samplepos_t);
-
/* this is set at construction, and not changeable later, so it is not
* a property
*/
@@ -358,6 +356,8 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful {
std::string display_name (bool sh/*ort*/ = true) const;
virtual void unregister_port ();
+ void connect_port_using_state ();
+ virtual void create_port () = 0;
protected:
SyncSource _type;
@@ -385,6 +385,8 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful {
boost::shared_ptr<Port> _port;
+ XMLNode port_node;
+
PBD::ScopedConnection port_connection;
bool connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn);
@@ -452,6 +454,8 @@ class LIBARDOUR_API MTC_TransportMaster : public TimecodeTransportMaster, public
std::string position_string() const;
std::string delta_string() const;
+ void create_port ();
+
private:
PBD::ScopedConnectionList port_connections;
PBD::ScopedConnection config_connection;
@@ -518,6 +522,8 @@ public:
std::string position_string() const;
std::string delta_string() const;
+ void create_port ();
+
private:
void parse_ltc(const pframes_t, const Sample* const, const samplecnt_t);
void process_ltc(samplepos_t const);
@@ -586,6 +592,8 @@ class LIBARDOUR_API MIDIClock_TransportMaster : public TransportMaster, public T
float bpm() const { return _bpm; }
+ void create_port ();
+
protected:
PBD::ScopedConnectionList port_connections;
@@ -648,6 +656,8 @@ class LIBARDOUR_API Engine_TransportMaster : public TransportMaster
std::string position_string() const;
std::string delta_string() const;
+ void create_port () { }
+
private:
AudioEngine& engine;
bool _starting;
diff --git a/libs/ardour/ardour/transport_master_manager.h b/libs/ardour/ardour/transport_master_manager.h
index a10b014098..e0926f74ec 100644
--- a/libs/ardour/ardour/transport_master_manager.h
+++ b/libs/ardour/ardour/transport_master_manager.h
@@ -33,6 +33,7 @@ class UI_TransportMaster;
class LIBARDOUR_API TransportMasterManager : public boost::noncopyable
{
public:
+ static TransportMasterManager& create ();
~TransportMasterManager ();
int set_default_configuration ();
@@ -82,6 +83,8 @@ class LIBARDOUR_API TransportMasterManager : public boost::noncopyable
static const std::string state_node_name;
+ void reconnect_ports ();
+
private:
TransportMasterManager();
diff --git a/libs/ardour/bundle.cc b/libs/ardour/bundle.cc
index 8d3d52158e..b165155e90 100644
--- a/libs/ardour/bundle.cc
+++ b/libs/ardour/bundle.cc
@@ -478,8 +478,8 @@ Bundle::connected_to (boost::shared_ptr<Bundle> other, AudioEngine & engine,
Bundle::PortList const & other_ports =
other->channel_ports (other->type_channel_to_overall(type, i));
- for (Bundle::PortList::const_iterator j = our_ports.begin();
- j != our_ports.end(); ++j) {
+ for (Bundle::PortList::const_iterator j = our_ports.begin(); j != our_ports.end(); ++j) {
+
boost::shared_ptr<Port> p = engine.get_port_by_name(*j);
for (Bundle::PortList::const_iterator k = other_ports.begin();
diff --git a/libs/ardour/debug.cc b/libs/ardour/debug.cc
index ad574af1ff..403bb03263 100644
--- a/libs/ardour/debug.cc
+++ b/libs/ardour/debug.cc
@@ -29,7 +29,7 @@ using namespace std;
PBD::DebugBits PBD::DEBUG::MidiSourceIO = PBD::new_debug_bit ("midisourceio");
PBD::DebugBits PBD::DEBUG::MidiPlaylistIO = PBD::new_debug_bit ("midiplaylistio");
-PBD::DebugBits PBD::DEBUG::MidiDiskstreamIO = PBD::new_debug_bit ("mididiskstreamio");
+PBD::DebugBits PBD::DEBUG::MidiDiskIO = PBD::new_debug_bit ("mididiskio");
PBD::DebugBits PBD::DEBUG::MidiRingBuffer = PBD::new_debug_bit ("midiringbuffer");
PBD::DebugBits PBD::DEBUG::SnapBBT = PBD::new_debug_bit ("snapbbt");
PBD::DebugBits PBD::DEBUG::Latency = PBD::new_debug_bit ("latency");
@@ -44,6 +44,8 @@ PBD::DebugBits PBD::DEBUG::MTC = PBD::new_debug_bit ("mtc");
PBD::DebugBits PBD::DEBUG::LTC = PBD::new_debug_bit ("ltc");
PBD::DebugBits PBD::DEBUG::TXLTC = PBD::new_debug_bit ("tx-ltc");
PBD::DebugBits PBD::DEBUG::Transport = PBD::new_debug_bit ("transport");
+PBD::DebugBits PBD::DEBUG::TFSMEvents = PBD::new_debug_bit ("tfsmevents");
+PBD::DebugBits PBD::DEBUG::TFSMState = PBD::new_debug_bit ("tfsmstate");
PBD::DebugBits PBD::DEBUG::Slave = PBD::new_debug_bit ("slave");
PBD::DebugBits PBD::DEBUG::SessionEvents = PBD::new_debug_bit ("sessionevents");
PBD::DebugBits PBD::DEBUG::MidiIO = PBD::new_debug_bit ("midiio");
diff --git a/libs/ardour/disk_reader.cc b/libs/ardour/disk_reader.cc
index 4b13b1678a..828019f28a 100644
--- a/libs/ardour/disk_reader.cc
+++ b/libs/ardour/disk_reader.cc
@@ -51,7 +51,7 @@ Sample* DiskReader::_sum_buffer = 0;
Sample* DiskReader::_mixdown_buffer = 0;
gain_t* DiskReader::_gain_buffer = 0;
samplecnt_t DiskReader::midi_readahead = 4096;
-bool DiskReader::_no_disk_output = false;
+gint DiskReader::_no_disk_output (0);
DiskReader::DiskReader (Session& s, string const & str, DiskIOProcessor::Flag f)
: DiskIOProcessor (s, str, f)
@@ -74,8 +74,8 @@ void
DiskReader::ReaderChannelInfo::resize (samplecnt_t bufsize)
{
delete rbuf;
- /* touch memory to lock it */
rbuf = new PlaybackBuffer<Sample> (bufsize);
+ /* touch memory to lock it */
memset (rbuf->buffer(), 0, sizeof (Sample) * rbuf->bufsize());
}
@@ -262,13 +262,14 @@ DiskReader::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
}
}
- const gain_t target_gain = (speed == 0.0 || ((ms & MonitoringDisk) == 0)) ? 0.0 : 1.0;
+ const bool declick_out = _session.declick_in_progress();
+ const gain_t target_gain = (declick_out || (speed == 0.0) || ((ms & MonitoringDisk) == 0)) ? 0.0 : 1.0;
if (!_session.cfg ()->get_use_transport_fades ()) {
_declick_amp.set_gain (target_gain);
}
- if ((speed == 0.0) && (ms == MonitoringDisk) && _declick_amp.gain () == target_gain) {
+ if (declick_out && (ms == MonitoringDisk) && _declick_amp.gain () == target_gain) {
/* no channels, or stopped. Don't accidentally pass any data
* from disk into our outputs (e.g. via interpolation)
*/
@@ -348,13 +349,13 @@ DiskReader::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
if (can_internal_playback_seek (start_sample - playback_sample)) {
internal_playback_seek (start_sample - playback_sample);
} else {
- cerr << owner()->name() << " playback not possible: ss = " << start_sample << " ps = " << playback_sample << endl;
+ cerr << owner()->name() << " playback at " << speed << " not possible: ss = " << start_sample << " ps = " << playback_sample << endl;
abort (); // XXX -- now what?
- goto midi;
+ /*NOTREACHED*/
}
}
- if (speed != 0.0) {
+ if (!declick_out) {
const samplecnt_t total = chaninfo->rbuf->read (disk_buf.data(), disk_samples_to_consume);
if (disk_samples_to_consume > total) {
cerr << _name << " Need " << disk_samples_to_consume << " total = " << total << endl;
@@ -384,7 +385,7 @@ DiskReader::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
/* MIDI data handling */
midi:
- if (/*!_session.declick_out_pending() && */ bufs.count().n_midi() && _midi_buf) {
+ if (!declick_in_progress() && bufs.count().n_midi() && _midi_buf) {
MidiBuffer* dst;
if (_no_disk_output) {
@@ -482,10 +483,8 @@ DiskReader::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
}
bool
-DiskReader::declick_in_progress () const {
- /* TODO use an atomic-get.
- * this may be called from the butler thread
- */
+DiskReader::declick_in_progress () const
+{
return _declick_amp.gain() != 0; // declick-out
}
@@ -544,8 +543,6 @@ DiskReader::overwrite_existing_buffers ()
samplepos_t start = overwrite_sample;
samplecnt_t to_read = size;
- cerr << owner()->name() << " over-read: " << to_read << endl;
-
if (audio_read ((*chan)->rbuf, sum_buffer.get(), mixdown_buffer.get(), gain_buffer.get(), start, to_read, n, reversed)) {
error << string_compose(_("DiskReader %1: when refilling, cannot read %2 from playlist at sample %3"), id(), size, overwrite_sample) << endmsg;
goto midi;
@@ -1167,7 +1164,7 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
Location* loc = _loop_location;
- DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose (
+ DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose (
"%1 MDS pre-read read %8 offset = %9 @ %4..%5 from %2 write to %3, LOOPED ? %6 .. %7\n", _name,
_midi_buf->get_read_ptr(), _midi_buf->get_write_ptr(), start_sample, end_sample,
(loc ? loc->start() : -1), (loc ? loc->end() : -1), nframes, Port::port_offset()));
@@ -1184,7 +1181,7 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
Evoral::Range<samplepos_t> loop_range (loc->start(), loc->end() - 1);
effective_start = loop_range.squish (start_sample);
- DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("looped, effective start adjusted to %1\n", effective_start));
+ DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("looped, effective start adjusted to %1\n", effective_start));
if (effective_start == loc->start()) {
/* We need to turn off notes that may extend
@@ -1208,23 +1205,23 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
first = loc->end() - effective_start;
second = nframes - first;
- DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read for eff %1 end %2: %3 and %4, cycle offset %5\n",
+ DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("loop read for eff %1 end %2: %3 and %4, cycle offset %5\n",
effective_start, loc->end(), first, second));
if (first) {
- DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read #1, from %1 for %2\n",
+ DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("loop read #1, from %1 for %2\n",
effective_start, first));
events_read = _midi_buf->read (*target, effective_start, first);
}
if (second) {
- DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read #2, from %1 for %2\n",
+ DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("loop read #2, from %1 for %2\n",
loc->start(), second));
events_read += _midi_buf->read (*target, loc->start(), second);
}
} else {
- DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read #3, adjusted start as %1 for %2\n",
+ DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("loop read #3, adjusted start as %1 for %2\n",
effective_start, nframes));
events_read = _midi_buf->read (*target, effective_start, effective_start + nframes);
}
@@ -1233,11 +1230,11 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
if (n_skipped > 0) {
warning << string_compose(_("MidiDiskstream %1: skipped %2 events, possible underflow"), id(), n_skipped) << endmsg;
}
- DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("playback buffer read, from %1 to %2 (%3)", start_sample, end_sample, nframes));
+ DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("playback buffer read, from %1 to %2 (%3)", start_sample, end_sample, nframes));
events_read = _midi_buf->read (*target, start_sample, end_sample, Port::port_offset ());
}
- DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose (
+ DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose (
"%1 MDS events read %2 range %3 .. %4 rspace %5 wspace %6 r@%7 w@%8\n",
_name, events_read, playback_sample, playback_sample + nframes,
_midi_buf->read_space(), _midi_buf->write_space(),
@@ -1246,7 +1243,7 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
g_atomic_int_add (&_samples_read_from_ringbuffer, nframes);
- if (ms & MonitoringInput) {
+ if (!_no_disk_output && (ms & MonitoringInput)) {
dst.merge_from (*target, nframes);
}
@@ -1285,7 +1282,7 @@ DiskReader::midi_read (samplepos_t& start, samplecnt_t dur, bool reversed)
assert(_midi_buf);
- DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MDS::midi_read @ %1 cnt %2\n", start, dur));
+ DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("MDS::midi_read @ %1 cnt %2\n", start, dur));
boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack>(_route);
MidiChannelFilter* filter = mt ? &mt->playback_filter() : 0;
@@ -1330,7 +1327,7 @@ DiskReader::midi_read (samplepos_t& start, samplecnt_t dur, bool reversed)
this_read = min (dur,this_read);
- DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MDS ::read at %1 for %2 loffset %3\n", effective_start, this_read, loop_offset));
+ DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("MDS ::read at %1 for %2 loffset %3\n", effective_start, this_read, loop_offset));
if (midi_playlist()->read (*_midi_buf, effective_start, this_read, loop_range, 0, filter) != this_read) {
error << string_compose(
@@ -1419,16 +1416,24 @@ DiskReader::refill_midi ()
}
void
-DiskReader::set_no_disk_output (bool yn)
+DiskReader::dec_no_disk_output ()
{
- /* this MUST be called as part of the process call tree, before any
- disk readers are invoked. We use it when the session needs the
- transport (and thus effective read position for DiskReaders) to keep
- advancing as part of syncing up with a transport master, but we
- don't want any actual disk output yet because we are still not
- synced.
+ /* this is called unconditionally when things happen that ought to end
+ a period of "no disk output". It's OK for that to happen when there
+ was no corresponding call to ::inc_no_disk_output(), but we must
+ stop the value from becoming negative.
*/
- _no_disk_output = yn;
+
+ do {
+ gint v = g_atomic_int_get (&_no_disk_output);
+ if (v > 0) {
+ if (g_atomic_int_compare_and_exchange (&_no_disk_output, v, v - 1)) {
+ break;
+ }
+ } else {
+ break;
+ }
+ } while (true);
}
DiskReader::DeclickAmp::DeclickAmp (samplecnt_t sample_rate)
@@ -1459,7 +1464,6 @@ DiskReader::DeclickAmp::apply_gain (AudioBuffer& buf, samplecnt_t n_samples, con
uint32_t offset = 0;
while (remain > 0) {
uint32_t n_proc = remain > max_nproc ? max_nproc : remain;
- std::cerr << "g = " << g << std::endl;
for (uint32_t i = 0; i < n_proc; ++i) {
buffer[offset + i] *= g;
}
diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc
index 5ca616001b..2665a14d94 100644
--- a/libs/ardour/enums.cc
+++ b/libs/ardour/enums.cc
@@ -448,7 +448,6 @@ setup_enum_writer ()
REGISTER_CLASS_ENUM (SessionEvent, SetTimecodeTransmission);
REGISTER_CLASS_ENUM (SessionEvent, Skip);
REGISTER_CLASS_ENUM (SessionEvent, SetTransportMaster);
- REGISTER_CLASS_ENUM (SessionEvent, StopOnce);
REGISTER_CLASS_ENUM (SessionEvent, AutoLoop);
REGISTER (_SessionEvent_Type);
diff --git a/libs/ardour/export_status.cc b/libs/ardour/export_status.cc
index 9df56840a6..d278afb312 100644
--- a/libs/ardour/export_status.cc
+++ b/libs/ardour/export_status.cc
@@ -66,11 +66,11 @@ ExportStatus::abort (bool error_occurred)
}
void
-ExportStatus::finish ()
+ExportStatus::finish (TransportRequestSource trs)
{
Glib::Threads::Mutex::Lock l (_run_lock);
set_running (false);
- Finished(); /* EMIT SIGNAL */
+ Finished (trs); /* EMIT SIGNAL */
}
} // namespace ARDOUR
diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc
index f70e015b11..cbb7e4da0e 100644
--- a/libs/ardour/globals.cc
+++ b/libs/ardour/globals.cc
@@ -574,6 +574,7 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
PannerManager::instance().discover_panners();
ARDOUR::AudioEngine::create ();
+ TransportMasterManager::create ();
/* it is unfortunate that we need to include reserved names here that
refer to control surfaces. But there's no way to ensure a complete
@@ -619,14 +620,13 @@ ARDOUR::init_post_engine (uint32_t start_cnt)
/* find plugins */
ARDOUR::PluginManager::instance().refresh (!Config->get_discover_vst_on_start());
- }
-
- if (start_cnt == 0) {
if ((node = Config->control_protocol_state()) != 0) {
ControlProtocolManager::instance().set_state (*node, 0 /* here: global-config state */);
}
+ }
+ if (start_cnt > 0) {
TransportMasterManager::instance().restart ();
}
}
diff --git a/libs/ardour/ltc_slave.cc b/libs/ardour/ltc_slave.cc
index 365839c523..f2335bba74 100644
--- a/libs/ardour/ltc_slave.cc
+++ b/libs/ardour/ltc_slave.cc
@@ -64,10 +64,6 @@ LTC_TransportMaster::LTC_TransportMaster (std::string const & name)
, a3e_timecode (Timecode::timecode_24)
, samples_per_timecode_frame (0)
{
- if ((_port = AudioEngine::instance()->register_input_port (DataType::AUDIO, string_compose ("%1 in", _name, false, TransportMasterPort))) == 0) {
- throw failed_constructor();
- }
-
DEBUG_TRACE (DEBUG::Slave, string_compose ("LTC registered %1\n", _port->name()));
memset (&prev_frame, 0, sizeof(LTCFrameExt));
@@ -85,6 +81,14 @@ LTC_TransportMaster::init ()
}
void
+LTC_TransportMaster::create_port ()
+{
+ if ((_port = AudioEngine::instance()->register_input_port (DataType::AUDIO, string_compose ("%1 in", _name, false, TransportMasterPort))) == 0) {
+ throw failed_constructor();
+ }
+}
+
+void
LTC_TransportMaster::set_session (Session *s)
{
config_connection.disconnect ();
diff --git a/libs/ardour/midi_clock_slave.cc b/libs/ardour/midi_clock_slave.cc
index 5a06187ef4..cf2c84c034 100644
--- a/libs/ardour/midi_clock_slave.cc
+++ b/libs/ardour/midi_clock_slave.cc
@@ -58,9 +58,6 @@ MIDIClock_TransportMaster::MIDIClock_TransportMaster (std::string const & name,
, _running (false)
, _bpm (0)
{
- if ((_port = create_midi_port (string_compose ("%1 in", name))) == 0) {
- throw failed_constructor();
- }
}
MIDIClock_TransportMaster::~MIDIClock_TransportMaster()
@@ -76,6 +73,14 @@ MIDIClock_TransportMaster::init ()
}
void
+MIDIClock_TransportMaster::create_port ()
+{
+ if ((_port = create_midi_port (string_compose ("%1 in", _name))) == 0) {
+ throw failed_constructor();
+ }
+}
+
+void
MIDIClock_TransportMaster::set_session (Session *session)
{
port_connections.drop_connections();
diff --git a/libs/ardour/mtc_slave.cc b/libs/ardour/mtc_slave.cc
index 3f045e26d4..048ac95455 100644
--- a/libs/ardour/mtc_slave.cc
+++ b/libs/ardour/mtc_slave.cc
@@ -68,10 +68,6 @@ MTC_TransportMaster::MTC_TransportMaster (std::string const & name)
, busy_guard2 (0)
, printed_timecode_warning (false)
{
- if ((_port = create_midi_port (string_compose ("%1 in", name))) == 0) {
- throw failed_constructor();
- }
-
DEBUG_TRACE (DEBUG::Slave, string_compose ("MTC registered %1\n", _port->name()));
init ();
@@ -104,6 +100,14 @@ MTC_TransportMaster::init ()
}
void
+MTC_TransportMaster::create_port ()
+{
+ if ((_port = create_midi_port (string_compose ("%1 in", _name))) == 0) {
+ throw failed_constructor();
+ }
+}
+
+void
MTC_TransportMaster::set_session (Session *s)
{
config_connection.disconnect ();
diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc
index f80ddf3915..417157f411 100644
--- a/libs/ardour/playlist.cc
+++ b/libs/ardour/playlist.cc
@@ -1788,6 +1788,8 @@ Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<
notify_region_end_trimmed (region);
} else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
notify_region_start_trimmed (region);
+ } else if (what_changed.contains (Properties::start)) {
+ notify_contents_changed ();
}
/* don't notify about layer changes, since we are the only object that can initiate
diff --git a/libs/ardour/port.cc b/libs/ardour/port.cc
index 03210baf80..9543233710 100644
--- a/libs/ardour/port.cc
+++ b/libs/ardour/port.cc
@@ -645,7 +645,7 @@ Port::set_state (const XMLNode& node, int)
/*static*/ void
Port::set_speed_ratio (double s) {
/* see VMResampler::set_rratio() for min/max range */
- _speed_ratio = std::min ((double) Config->get_max_transport_speed(), std::max (0.5, s));
+ _speed_ratio = std::min ((double) Config->get_max_transport_speed(), std::max (0.5, fabs (s)));
}
/*static*/ void
diff --git a/libs/ardour/rt_tasklist.cc b/libs/ardour/rt_tasklist.cc
index a0671968bf..9dc9b635b4 100644
--- a/libs/ardour/rt_tasklist.cc
+++ b/libs/ardour/rt_tasklist.cc
@@ -51,8 +51,7 @@ RTTaskList::drop_threads ()
for (uint32_t i = 0; i < nt; ++i) {
_task_run_sem.signal ();
}
- for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i)
- {
+ for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i) {
pthread_join (*i, NULL);
}
_threads.clear ();
@@ -122,7 +121,7 @@ RTTaskList::run ()
boost::function<void ()> to_run;
tm.acquire ();
- if (_tasklist.size () > 0) {
+ if (!_tasklist.empty ()) {
to_run = _tasklist.front();
_tasklist.pop_front ();
}
@@ -145,20 +144,29 @@ void
RTTaskList::process (TaskList const& tl)
{
Glib::Threads::Mutex::Lock pm (_process_mutex);
+ Glib::Threads::Mutex::Lock tm (_tasklist_mutex, Glib::Threads::NOT_LOCK);
+
+ tm.acquire ();
_tasklist = tl;
+ tm.release ();
+
process_tasklist ();
+
+ tm.acquire ();
_tasklist.clear ();
+ tm.release ();
}
void
RTTaskList::process_tasklist ()
{
- if (0 == g_atomic_int_get (&_threads_active) || _threads.size () == 0) {
+// if (0 == g_atomic_int_get (&_threads_active) || _threads.size () == 0) {
+
for (TaskList::iterator i = _tasklist.begin (); i != _tasklist.end(); ++i) {
(*i)();
}
return;
- }
+// }
uint32_t nt = std::min (_threads.size (), _tasklist.size ());
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index d61a53cd52..6935150ab6 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -49,6 +49,7 @@
#include "pbd/convert.h"
#include "pbd/error.h"
#include "pbd/file_utils.h"
+#include "pbd/i18n.h"
#include "pbd/md5.h"
#include "pbd/pthread_utils.h"
#include "pbd/search_path.h"
@@ -116,7 +117,9 @@
#include "ardour/speakers.h"
#include "ardour/tempo.h"
#include "ardour/ticker.h"
+#include "ardour/transport_fsm.h"
#include "ardour/transport_master.h"
+#include "ardour/transport_master_manager.h"
#include "ardour/track.h"
#include "ardour/types_convert.h"
#include "ardour/user_bundle.h"
@@ -129,8 +132,6 @@
#include "LuaBridge/LuaBridge.h"
-#include "pbd/i18n.h"
-
#include <glibmm/checksum.h>
namespace ARDOUR {
@@ -189,7 +190,6 @@ Session::Session (AudioEngine &eng,
, _base_sample_rate (0)
, _nominal_sample_rate (0)
, _current_sample_rate (0)
- , transport_sub_state (0)
, _record_status (Disabled)
, _transport_sample (0)
, _seek_counter (0)
@@ -251,6 +251,7 @@ Session::Session (AudioEngine &eng,
, lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
, _n_lua_scripts (0)
, _butler (new Butler (*this))
+ , _transport_fsm (TransportFSM::create (*this))
, _post_transport_work (0)
, _locations (new Locations (*this))
, _ignore_skips_updates (false)
@@ -606,9 +607,13 @@ Session::immediately_post_engine ()
_process_graph.reset (new Graph (*this));
}
- /* every time we reconnect, recompute worst case output latencies */
+ /* Restart transport FSM */
+
+ _transport_fsm->backend()->start ();
- _engine.Running.connect_same_thread (*this, boost::bind (&Session::initialize_latencies, this));
+ /* every time we reconnect, do stuff ... */
+
+ _engine.Running.connect_same_thread (*this, boost::bind (&Session::engine_running, this));
if (synced_to_engine()) {
_engine.transport_stop ();
@@ -848,7 +853,6 @@ Session::destroy ()
case SessionEvent::Skip:
case SessionEvent::PunchIn:
case SessionEvent::PunchOut:
- case SessionEvent::StopOnce:
case SessionEvent::RangeStop:
case SessionEvent::RangeLocate:
remove = false;
@@ -880,6 +884,9 @@ Session::destroy ()
delete _selection;
_selection = 0;
+ _transport_fsm->backend()->stop ();
+ _transport_fsm.reset ();
+
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
#ifndef NDEBUG
@@ -1584,6 +1591,7 @@ Session::hookup_io ()
*/
AudioEngine::instance()->reconnect_ports ();
+ TransportMasterManager::instance().reconnect_ports ();
/* Anyone who cares about input state, wake up and do something */
@@ -2015,7 +2023,7 @@ void
Session::_locations_changed (const Locations::LocationList& locations)
{
/* There was some mass-change in the Locations object.
- *
+ *
* We might be re-adding a location here but it doesn't actually matter
* for all the locations that the Session takes an interest in.
*/
diff --git a/libs/ardour/session_butler.cc b/libs/ardour/session_butler.cc
index 492346b12b..1f472b710b 100644
--- a/libs/ardour/session_butler.cc
+++ b/libs/ardour/session_butler.cc
@@ -25,6 +25,7 @@
#include "pbd/stacktrace.h"
#include "ardour/butler.h"
+#include "ardour/disk_reader.h"
#include "ardour/route.h"
#include "ardour/session.h"
#include "ardour/session_event.h"
@@ -61,6 +62,7 @@ void
Session::schedule_playback_buffering_adjustment ()
{
add_post_transport_work (PostTransportAdjustPlaybackBuffering);
+ DiskReader::inc_no_disk_output ();
_butler->schedule_transport_work ();
}
@@ -108,6 +110,7 @@ Session::overwrite_some_buffers (Track* t)
}
add_post_transport_work (PostTransportOverWrite);
+
_butler->schedule_transport_work ();
}
diff --git a/libs/ardour/session_events.cc b/libs/ardour/session_events.cc
index e4492e15ac..db3ba85e0b 100644
--- a/libs/ardour/session_events.cc
+++ b/libs/ardour/session_events.cc
@@ -225,7 +225,6 @@ SessionEventManager::merge_event (SessionEvent* ev)
switch (ev->type) {
case SessionEvent::AutoLoop:
- case SessionEvent::StopOnce:
_clear_event_type (ev->type);
break;
default:
diff --git a/libs/ardour/session_export.cc b/libs/ardour/session_export.cc
index 8d7b4da6c5..c1a7f5f388 100644
--- a/libs/ardour/session_export.cc
+++ b/libs/ardour/session_export.cc
@@ -95,7 +95,7 @@ Session::pre_export ()
_exporting = true;
export_status->set_running (true);
- export_status->Finished.connect_same_thread (*this, boost::bind (&Session::finalize_audio_export, this));
+ export_status->Finished.connect_same_thread (*this, boost::bind (&Session::finalize_audio_export, this, _1));
/* disable MMC output early */
@@ -153,7 +153,7 @@ Session::start_audio_export (samplepos_t position, bool realtime, bool region_ex
}
}
- /* we just did the core part of a locate() call above, but
+ /* we just did the core part of a locate call above, but
for the sake of any GUI, put the _transport_sample in
the right place too.
*/
@@ -256,7 +256,7 @@ Session::process_export_fw (pframes_t nframes)
set_transport_speed (1.0, 0, false);
butler_transport_work ();
g_atomic_int_set (&_butler->should_do_transport_work, 0);
- post_transport ();
+ butler_completed_transport_work ();
return;
}
@@ -299,7 +299,7 @@ int
Session::stop_audio_export ()
{
/* can't use stop_transport() here because we need
- an immediate halt and don't require all the declick
+ an synchronous halt and don't require all the declick
stuff that stop_transport() implements.
*/
@@ -311,8 +311,14 @@ Session::stop_audio_export ()
}
void
-Session::finalize_audio_export ()
+Session::finalize_audio_export (TransportRequestSource trs)
{
+ /* This is called as a handler for the Finished signal, which is
+ emitted by a UI component once the ExportStatus object associated
+ with this export indicates that it has finished. It runs in the UI
+ thread that emits the signal.
+ */
+
_exporting = false;
if (_export_rolling) {
@@ -340,6 +346,6 @@ Session::finalize_audio_export ()
if (post_export_sync) {
config.set_external_sync (true);
} else {
- locate (post_export_position, false, false, false, false, false);
+ request_locate (post_export_position, false, trs);
}
}
diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc
index b47653c3bf..7661115463 100644
--- a/libs/ardour/session_process.cc
+++ b/libs/ardour/session_process.cc
@@ -26,9 +26,9 @@
#include <algorithm>
#include <unistd.h>
-#include <boost/msm/back/state_machine.hpp>
-#include <boost/msm/front/state_machine_def.hpp>
+#include <boost/algorithm/string/erase.hpp>
+#include "pbd/i18n.h"
#include "pbd/error.h"
#include "pbd/enumwriter.h"
@@ -45,6 +45,7 @@
#include "ardour/process_thread.h"
#include "ardour/scene_changer.h"
#include "ardour/session.h"
+#include "ardour/transport_fsm.h"
#include "ardour/transport_master.h"
#include "ardour/transport_master_manager.h"
#include "ardour/ticker.h"
@@ -54,95 +55,11 @@
#include "midi++/mmc.h"
-#include "pbd/i18n.h"
-
using namespace ARDOUR;
using namespace PBD;
using namespace std;
-/* state machine */
-namespace msm = boost::msm;
-namespace mpl = boost::mpl;
-
-namespace TransportState
-{
- /* events */
- struct play {};
- struct stop {};
-
- /* front-end: define the FSM structure */
- struct TransportFSM : public msm::front::state_machine_def<TransportFSM>
- {
-
- /* FSM states */
- struct Stopped : public msm::front::state<>
- {
- template <class Event,class FSM> void
- on_entry (Event const&, FSM&)
- {
- std::cout << "entering: Stopped" << std::endl;
- }
- template <class Event,class FSM> void
- on_exit (Event const&, FSM&)
- {
- std::cout << "leaving: Stopped" << std::endl;
- }
- };
-
- struct Playing : public msm::front::state<>
- {
- template <class Event,class FSM> void
- on_entry (Event const&, FSM&)
- {
- std::cout << "entering: Playing" << std::endl;
- }
-
- template <class Event,class FSM> void
- on_exit (Event const&, FSM&)
- {
- std::cout << "leaving: Playing" << std::endl;
- }
- };
-
- /* the initial state */
- typedef Stopped initial_state;
-
- /* transition actions */
- void start_playback (play const&)
- {
- std::cout << "player::start_playback\n";
- }
-
- void stop_playback (stop const&)
- {
- std::cout << "player::stop_playback\n";
- }
-
- typedef TransportFSM _t; // makes transition table cleaner
-
- struct transition_table : mpl::vector<
- // Start Event Next Action Guard
- // +---------+-------------+---------+---------------------+----------------------+
- a_row < Stopped , play , Playing , &_t::start_playback >,
- _row < Stopped , stop , Stopped >,
- // +---------+-------------+---------+---------------------+----------------------+
- a_row < Playing , stop , Stopped , &_t::stop_playback >
- // +---------+-------------+---------+---------------------+----------------------+
- > {};
- };
-
- typedef msm::back::state_machine<TransportFSM> transport_fsm;
-
- void test()
- {
- transport_fsm t;
- t.start ();
- t.process_event (play());
- t.process_event (stop());
- t.stop();
- }
-
-};
+#define TFSM_EVENT(ev) { DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("TFSM(%1)\n", typeid(ev).name())); _transport_fsm->enqueue (ev); }
/** Called by the audio engine when there is work to be done with JACK.
* @param nframes Number of samples to process.
@@ -161,8 +78,12 @@ Session::process (pframes_t nframes)
}
if (non_realtime_work_pending()) {
+ DEBUG_TRACE (DEBUG::Butler, string_compose ("non-realtime work pending: %1\n", enum_2_string (post_transport_work())));
if (!_butler->transport_work_requested ()) {
- post_transport ();
+ DEBUG_TRACE (DEBUG::Butler, string_compose ("done, waiting? %1\n", _transport_fsm->waiting_for_butler()));
+ butler_completed_transport_work ();
+ } else {
+ DEBUG_TRACE (DEBUG::Butler, "not done yet\n");
}
}
@@ -177,11 +98,16 @@ Session::process (pframes_t nframes)
* callig it hold a _processor_lock reader-lock
*/
boost::shared_ptr<RouteList> r = routes.reader ();
+ bool one_or_more_routes_declicking = false;
for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
if ((*i)->apply_processor_changes_rt()) {
_rt_emit_pending = true;
}
+ if ((*i)->declick_in_progress()) {
+ one_or_more_routes_declicking = true;
+ }
}
+
if (_rt_emit_pending) {
if (!_rt_thread_active) {
emit_route_signals ();
@@ -193,6 +119,17 @@ Session::process (pframes_t nframes)
}
}
+ /* We are checking two things here:
+ *
+ * 1) whether or not all tracks have finished a declick out.
+ * 2) is the transport FSM waiting to be told this
+ */
+
+ if (!one_or_more_routes_declicking && declick_in_progress()) {
+ /* end of the declick has been reached by all routes */
+ TFSM_EVENT (TransportFSM::declick_done());
+ }
+
_engine.main_thread()->drop_buffers ();
/* deliver MIDI clock. Note that we need to use the transport sample
@@ -241,6 +178,8 @@ Session::no_roll (pframes_t nframes)
(*i)->automation_run (_transport_sample, nframes);
}
+ _global_locate_pending = locate_pending ();
+
if (_process_graph) {
DEBUG_TRACE(DEBUG::ProcessThreads,"calling graph/no-roll\n");
_process_graph->routes_no_roll( nframes, _transport_sample, end_sample, non_realtime_work_pending());
@@ -281,7 +220,7 @@ Session::process_routes (pframes_t nframes, bool& need_butler)
(*i)->automation_run (start_sample, nframes);
}
- _global_locate_pending = locate_pending ();
+ _global_locate_pending = locate_pending();
if (_process_graph) {
DEBUG_TRACE(DEBUG::ProcessThreads,"calling graph/process-routes\n");
@@ -302,7 +241,7 @@ Session::process_routes (pframes_t nframes, bool& need_butler)
bool b = false;
if ((ret = (*i)->roll (nframes, start_sample, end_sample, b)) < 0) {
- stop_transport ();
+ TFSM_EVENT (TransportFSM::stop_transport (false, false));
return -1;
}
@@ -404,7 +343,7 @@ Session::process_with_events (pframes_t nframes)
assert (_count_in_samples == 0 || _remaining_latency_preroll == 0 || _count_in_samples == _remaining_latency_preroll);
- DEBUG_TRACE (DEBUG::Transport, string_compose ("Running count in/latency preroll of %1 & %2\n", _count_in_samples, _remaining_latency_preroll));
+ // DEBUG_TRACE (DEBUG::Transport, string_compose ("Running count in/latency preroll of %1 & %2\n", _count_in_samples, _remaining_latency_preroll));
while (_count_in_samples > 0 || _remaining_latency_preroll > 0) {
samplecnt_t ns;
@@ -615,10 +554,8 @@ Session::process_with_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));
} 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));
} else {
DEBUG_TRACE (DEBUG::Transport, "no transport motion\n");
}
@@ -699,7 +636,6 @@ Session::process_without_events (pframes_t nframes)
return;
} else {
samples_moved = (samplecnt_t) nframes * _transport_speed;
- DEBUG_TRACE (DEBUG::Transport, string_compose ("no-events, plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_speed));
}
if (!_exporting && !timecode_transmission_suspended()) {
@@ -730,10 +666,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");
}
@@ -915,37 +851,27 @@ Session::process_event (SessionEvent* ev)
/* roll after locate, do not flush, set "with loop"
true only if we are seamless looping
*/
- start_locate (ev->target_sample, true, false, Config->get_seamless_loop());
+ TFSM_EVENT (TransportFSM::locate (ev->target_sample, true, false, Config->get_seamless_loop(), false));
}
remove = false;
del = false;
break;
case SessionEvent::Locate:
- if (ev->yes_or_no) { /* force locate */
- /* args: do not roll after locate, do flush, not with loop */
- locate (ev->target_sample, false, true, false);
- } else {
- /* args: do not roll after locate, do flush, not with loop */
- start_locate (ev->target_sample, false, true, false);
- }
+ /* args: do not roll after locate, do flush, not with loop, force */
+ TFSM_EVENT (TransportFSM::locate (ev->target_sample, false, true, false, ev->yes_or_no));
_send_timecode_update = true;
break;
case SessionEvent::LocateRoll:
- if (ev->yes_or_no) {
- /* args: roll after locate, do flush, not with loop */
- locate (ev->target_sample, true, true, false);
- } else {
- /* args: roll after locate, do flush, not with loop */
- start_locate (ev->target_sample, true, true, false);
- }
+ /* args: roll after locate, do flush, not with loop, force */
+ TFSM_EVENT (TransportFSM::locate (ev->target_sample, true, true, false, ev->yes_or_no));
_send_timecode_update = true;
break;
case SessionEvent::Skip:
if (Config->get_skip_playback()) {
- start_locate (ev->target_sample, true, true, false);
+ TFSM_EVENT (TransportFSM::locate (ev->target_sample, true, true, false, false));
_send_timecode_update = true;
}
remove = false;
@@ -985,26 +911,15 @@ Session::process_event (SessionEvent* ev)
del = false;
break;
- case SessionEvent::StopOnce:
- if (!non_realtime_work_pending()) {
- _clear_event_type (SessionEvent::StopOnce);
- stop_transport (ev->yes_or_no);
- }
- remove = false;
- del = false;
- break;
-
case SessionEvent::RangeStop:
- if (!non_realtime_work_pending()) {
- stop_transport (ev->yes_or_no);
- }
+ TFSM_EVENT (TransportFSM::stop_transport (ev->yes_or_no, false));
remove = false;
del = false;
break;
case SessionEvent::RangeLocate:
/* args: roll after locate, do flush, not with loop */
- start_locate (ev->target_sample, true, true, false);
+ TFSM_EVENT (TransportFSM::locate (ev->target_sample, true, true, false, false));
remove = false;
del = false;
break;
@@ -1179,117 +1094,63 @@ Session::follow_transport_master (pframes_t nframes)
slave_speed = tmm.get_current_speed_in_process_context();
slave_transport_sample = tmm.get_current_position_in_process_context ();
-
- track_transport_master (slave_speed, slave_transport_sample);
-
- /* transport sample may have been moved during ::track_transport_master() */
-
delta = _transport_sample - slave_transport_sample;
DEBUG_TRACE (DEBUG::Slave, string_compose ("session at %1, master at %2, delta: %3 res: %4\n", _transport_sample, slave_transport_sample, delta, tmm.current()->resolution()));
- if (transport_master_tracking_state == Running) {
-
- if (!actively_recording() && abs (delta) > tmm.current()->resolution()) {
- DEBUG_TRACE (DEBUG::Slave, string_compose ("current slave delta %1 greater than slave resolution %2\n", delta, tmm.current()->resolution()));
- if (micro_locate (-delta) != 0) {
- DEBUG_TRACE (DEBUG::Slave, "micro-locate didn't work, set no disk output true\n");
-
- /* run routes as normal, but no disk output */
- DiskReader::set_no_disk_output (true);
- }
- return true;
- }
+ /* This is a heuristic rather than a strictly provable rule. The idea
+ * is that if we're "far away" from the master, we should locate to its
+ * current position, and then varispeed to sync with it.
+ *
+ * On the other hand, if we're close to it, just varispeed.
+ */
- if (transport_master_tracking_state == Running) {
- /* speed is set, we're locked, and good to go */
- DiskReader::set_no_disk_output (false);
- return true;
+ if (!actively_recording() && abs (delta) > (5 * current_block_size)) {
+ DiskReader::inc_no_disk_output ();
+ if (!_transport_fsm->locating()) {
+ DEBUG_TRACE (DEBUG::Slave, string_compose ("request locate to master position %1\n", slave_transport_sample));
+ TFSM_EVENT (TransportFSM::locate (slave_transport_sample, true, true, false, false));
}
+ return true;
}
- noroll:
- /* don't move at all */
- DEBUG_TRACE (DEBUG::Slave, "no roll\n")
- no_roll (nframes);
- return false;
-}
-
-void
-Session::track_transport_master (float slave_speed, samplepos_t slave_transport_sample)
-{
- boost::shared_ptr<TransportMaster> master (TransportMasterManager::instance().current());
-
- assert (master);
-
- DEBUG_TRACE (DEBUG::Slave, string_compose ("session has master tracking state as %1\n", transport_master_tracking_state));
-
- if (slave_speed != 0.0f) {
-
- /* slave is running */
-
- switch (transport_master_tracking_state) {
- case Stopped:
- master_wait_end = slave_transport_sample + worst_latency_preroll() + master->seekahead_distance ();
- DEBUG_TRACE (DEBUG::Slave, string_compose ("slave stopped, but running, requires seekahead to %1, now WAITING\n", master_wait_end));
- /* we can call locate() here because we are in process context */
- if (micro_locate (master_wait_end - _transport_sample) != 0) {
- locate (master_wait_end, false, false);
- }
- transport_master_tracking_state = Waiting;
-
- case Waiting:
- default:
- break;
+ if (slave_speed != 0.0) {
+ if (_transport_speed == 0.0f) {
+ DEBUG_TRACE (DEBUG::Slave, string_compose ("slave starts transport: %1 sample %2 tf %3\n", slave_speed, slave_transport_sample, _transport_sample));
+ TFSM_EVENT (TransportFSM::start_transport ());
}
-
- if (transport_master_tracking_state == Waiting) {
-
- DEBUG_TRACE (DEBUG::Slave, string_compose ("master currently at %1, waiting to pass %2\n", slave_transport_sample, master_wait_end));
-
- if (slave_transport_sample >= master_wait_end) {
-
- DEBUG_TRACE (DEBUG::Slave, string_compose ("slave start at %1 vs %2\n", slave_transport_sample, _transport_sample));
-
- transport_master_tracking_state = Running;
-
- /* now perform a "micro-seek" within the disk buffers to realign ourselves
- precisely with the master.
- */
-
- if (micro_locate (slave_transport_sample - _transport_sample) != 0) {
- cerr << "cannot micro-seek\n";
- /* XXX what? */
- }
- }
- }
-
- if (transport_master_tracking_state == Running && _transport_speed == 0.0f) {
- DEBUG_TRACE (DEBUG::Slave, "slave starts transport\n");
- start_transport ();
- }
-
- } else { // slave_speed is 0
-
- /* slave has stopped */
-
+ } else {
if (_transport_speed != 0.0f) {
DEBUG_TRACE (DEBUG::Slave, string_compose ("slave stops transport: %1 sample %2 tf %3\n", slave_speed, slave_transport_sample, _transport_sample));
- stop_transport ();
+ TFSM_EVENT (TransportFSM::stop_transport (false, false));
}
+ }
- if (slave_transport_sample != _transport_sample) {
- DEBUG_TRACE (DEBUG::Slave, string_compose ("slave stopped, move to %1\n", slave_transport_sample));
- force_locate (slave_transport_sample, false);
- }
+ /* This is the second part of the "we're not synced yet" code. If we're
+ * close, but not within the resolution of the master, silence disk
+ * output but continue to varispeed to get in sync.
+ */
- reset_slave_state();
+ if (!actively_recording() && abs (delta) > tmm.current()->resolution()) {
+ /* just varispeed to chase the master, and be silent till we're synced */
+ DiskReader::inc_no_disk_output ();
+ return true;
}
+
+ /* speed is set, we're locked, and good to go */
+ DiskReader::dec_no_disk_output ();
+ return true;
+
+ noroll:
+ /* don't move at all */
+ DEBUG_TRACE (DEBUG::Slave, "no roll\n")
+ no_roll (nframes);
+ return false;
}
void
Session::reset_slave_state ()
{
transport_master_tracking_state = Stopped;
- DiskReader::set_no_disk_output (false);
+ DiskReader::dec_no_disk_output ();
}
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index e38fd57db1..1b9eeea59f 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -4123,7 +4123,6 @@ Session::config_changed (std::string p, bool ours)
first_file_data_format_reset = false;
} else if (p == "external-sync") {
- std::cerr << "param change, rss to " << TransportMasterManager::instance().current() << std::endl;
request_sync_source (TransportMasterManager::instance().current());
} else if (p == "denormal-model") {
setup_fpu ();
diff --git a/libs/ardour/session_time.cc b/libs/ardour/session_time.cc
index 550a04cc04..674ea7b67b 100644
--- a/libs/ardour/session_time.cc
+++ b/libs/ardour/session_time.cc
@@ -35,6 +35,7 @@
#include "ardour/session.h"
#include "ardour/tempo.h"
+#include "ardour/transport_fsm.h"
#include "pbd/i18n.h"
@@ -42,6 +43,8 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
+#define TFSM_EVENT(ev) { DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("TFSM(%1)\n", typeid(ev).name())); _transport_fsm->enqueue (ev); }
+
/* BBT TIME*/
void
@@ -214,7 +217,7 @@ Session::backend_sync_callback (TransportState state, samplepos_t pos)
case TransportRolling:
// cerr << "SYNC: rolling slave = " << slave << endl;
if (slave) {
- start_transport ();
+ TFSM_EVENT (TransportFSM::start_transport());
}
break;
diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc
index 0c424704bf..3ab5c64f6e 100644
--- a/libs/ardour/session_transport.cc
+++ b/libs/ardour/session_transport.cc
@@ -32,12 +32,15 @@
#include <cerrno>
#include <unistd.h>
-#include "pbd/undo.h"
+#include <boost/algorithm/string/erase.hpp>
+
#include "pbd/error.h"
#include "pbd/enumwriter.h"
-#include "pbd/pthread_utils.h"
+#include "pbd/i18n.h"
#include "pbd/memento_command.h"
+#include "pbd/pthread_utils.h"
#include "pbd/stacktrace.h"
+#include "pbd/undo.h"
#include "midi++/mmc.h"
#include "midi++/port.h"
@@ -54,6 +57,7 @@
#include "ardour/profile.h"
#include "ardour/scene_changer.h"
#include "ardour/session.h"
+#include "ardour/transport_fsm.h"
#include "ardour/transport_master.h"
#include "ardour/transport_master_manager.h"
#include "ardour/tempo.h"
@@ -61,8 +65,6 @@
#include "ardour/vca.h"
#include "ardour/vca_manager.h"
-#include "pbd/i18n.h"
-
using namespace std;
using namespace ARDOUR;
using namespace PBD;
@@ -80,8 +82,10 @@ using namespace PBD;
#endif
+#define TFSM_EVENT(ev) { DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("TFSM(%1)\n", typeid(ev).name())); _transport_fsm->enqueue (ev); }
+
/* *****************************************************************************
- * REALTIME ACTIONS (to be called on state transtion
+ * REALTIME ACTIONS (to be called on state transitions)
* ****************************************************************************/
void
@@ -89,14 +93,13 @@ Session::realtime_stop (bool abort, bool clear_state)
{
ENSURE_PROCESS_THREAD;
- DEBUG_TRACE (DEBUG::Transport, string_compose ("realtime stop @ %1\n", _transport_sample));
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("realtime stop @ %1 speed = %2\n", _transport_sample, _transport_speed));
PostTransportWork todo = PostTransportWork (0);
- /* assume that when we start, we'll be moving forwards */
-
- if (_transport_speed < 0.0f) {
+ if (_last_transport_speed < 0.0f) {
todo = (PostTransportWork (todo | PostTransportStop | PostTransportReverse));
_default_transport_speed = 1.0;
+ DiskReader::inc_no_disk_output (); // for the buffer reversal
} else {
todo = PostTransportWork (todo | PostTransportStop);
}
@@ -127,7 +130,6 @@ Session::realtime_stop (bool abort, bool clear_state)
add_post_transport_work (todo);
}
- _clear_event_type (SessionEvent::StopOnce);
_clear_event_type (SessionEvent::RangeStop);
_clear_event_type (SessionEvent::RangeLocate);
@@ -156,22 +158,13 @@ Session::realtime_stop (bool abort, bool clear_state)
waiting_for_sync_offset = true;
}
- transport_sub_state = 0;
-}
-
-void
-Session::realtime_locate ()
-{
- ENSURE_PROCESS_THREAD;
-
- boost::shared_ptr<RouteList> r = routes.reader ();
- for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- (*i)->realtime_locate ();
+ if (todo) {
+ TFSM_EVENT (TransportFSM::butler_required());
}
}
void
-Session::start_locate (samplepos_t target_sample, bool with_roll, bool with_flush, bool for_loop_enabled, bool force)
+Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, bool for_loop_enabled, bool force, bool with_mmc)
{
ENSURE_PROCESS_THREAD;
@@ -195,7 +188,7 @@ Session::start_locate (samplepos_t target_sample, bool with_roll, bool with_flus
will use the incorrect _transport_sample and report an old
and incorrect time to Jack transport
*/
- locate (target_sample, with_roll, with_flush, for_loop_enabled, force);
+ do_locate (target_sample, with_roll, with_flush, for_loop_enabled, force, with_mmc);
}
/* tell JACK to change transport position, and we will
@@ -211,13 +204,13 @@ Session::start_locate (samplepos_t target_sample, bool with_roll, bool with_flus
}
} else {
- locate (target_sample, with_roll, with_flush, for_loop_enabled, force);
+ do_locate (target_sample, with_roll, with_flush, for_loop_enabled, force, with_mmc);
}
}
/** @param with_mmc true to send a MMC locate command when the locate is done */
void
-Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, bool for_loop_enabled, bool force, bool with_mmc)
+Session::do_locate (samplepos_t target_sample, bool with_roll, bool with_flush, bool for_loop_enabled, bool force, bool with_mmc)
{
ENSURE_PROCESS_THREAD;
@@ -246,12 +239,11 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, boo
set_transport_speed (1.0, 0, false);
}
loop_changing = false;
+ TFSM_EVENT (TransportFSM::locate_done());
Located (); /* EMIT SIGNAL */
return;
}
- cerr << "... now doing the actual locate to " << target_sample << " from " << _transport_sample << endl;
-
// Update Timecode time
_transport_sample = target_sample;
// Bump seek counter so that any in-process locate in the butler
@@ -266,19 +258,30 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, boo
* we are rolling AND
* no autoplay in effect AND
* we're not going to keep rolling after the locate AND
- * !(playing a loop with JACK sync)
+ * !(playing a loop with JACK sync) AND
+ * we're not synced to an external transport master
*
*/
bool transport_was_stopped = !transport_rolling();
- if (!transport_was_stopped && (!auto_play_legal || !config.get_auto_play()) && !with_roll && !(synced_to_engine() && play_loop) &&
+ if (!transport_was_stopped &&
+ (!auto_play_legal || !config.get_auto_play()) &&
+ !with_roll &&
+ !(synced_to_engine() && play_loop) &&
(!Profile->get_trx() || !(config.get_external_sync() && !synced_to_engine()))) {
+
realtime_stop (false, true); // XXX paul - check if the 2nd arg is really correct
transport_was_stopped = true;
+
} else {
- /* otherwise tell the world that we located */
- realtime_locate ();
+
+ /* Tell all routes to do the RT part of locate */
+
+ boost::shared_ptr<RouteList> r = routes.reader ();
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->realtime_locate ();
+ }
}
if (force || !for_loop_enabled || loop_changing) {
@@ -379,7 +382,9 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, boo
}
if (need_butler) {
- _butler->schedule_transport_work ();
+ TFSM_EVENT (TransportFSM::butler_required());
+ } else {
+ TFSM_EVENT (TransportFSM::locate_done());
}
loop_changing = false;
@@ -396,6 +401,17 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, boo
}
}
+void
+Session::post_locate ()
+{
+ if (transport_master_is_external() && !synced_to_engine()) {
+ const samplepos_t current_master_position = TransportMasterManager::instance().get_current_position_in_process_context();
+ if (abs (current_master_position - _transport_sample) > TransportMasterManager::instance().current()->resolution()) {
+ _last_roll_location = _last_roll_or_reversal_location = _transport_sample;
+ }
+ }
+}
+
/** Set the transport speed.
* Called from the process thread.
* @param speed New speed
@@ -468,7 +484,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
_requested_return_sample = destination_sample;
}
- stop_transport (abort);
+ TFSM_EVENT (TransportFSM::stop_transport (abort, false));
}
} else if (transport_stopped() && speed == 1.0) {
@@ -505,7 +521,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
_engine.transport_start ();
_count_in_once = false;
} else {
- start_transport ();
+ TFSM_EVENT (TransportFSM::start_transport());
}
} else {
@@ -547,6 +563,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
if ((_transport_speed && speed * _transport_speed < 0.0) || (_last_transport_speed * speed < 0.0) || (_last_transport_speed == 0.0 && speed < 0.0)) {
todo = PostTransportWork (todo | PostTransportReverse);
+ DiskReader::inc_no_disk_output (); // for the buffer reversal
_last_roll_or_reversal_location = _transport_sample;
}
@@ -559,7 +576,7 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool
if (todo) {
add_post_transport_work (todo);
- _butler->schedule_transport_work ();
+ TFSM_EVENT (TransportFSM::butler_required());
}
DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC3 with speed = %1\n", _transport_speed));
@@ -595,14 +612,10 @@ Session::stop_transport (bool abort, bool clear_state)
ENSURE_PROCESS_THREAD;
_count_in_once = false;
- if (_transport_speed == 0.0f) {
- return;
- }
- DEBUG_TRACE (DEBUG::Transport, "time to actually stop\n");
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("time to actually stop with TS @ %1\n", _transport_sample));
realtime_stop (abort, clear_state);
- _butler->schedule_transport_work ();
}
/** Called from the process thread */
@@ -705,15 +718,26 @@ Session::start_transport ()
TransportStateChange (); /* EMIT SIGNAL */
}
+bool
+Session::should_roll_after_locate () const
+{
+ /* a locate must previously have been requested and completed */
+
+ return ((!config.get_external_sync() && (auto_play_legal && config.get_auto_play())) && !_exporting) || (post_transport_work() & PostTransportRoll);
+
+}
+
/** Do any transport work in the audio thread that needs to be done after the
- * transport thread is finished. Audio thread, realtime safe.
+ * butler thread is finished. Audio thread, realtime safe.
*/
void
-Session::post_transport ()
+Session::butler_completed_transport_work ()
{
ENSURE_PROCESS_THREAD;
PostTransportWork ptw = post_transport_work ();
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler done, RT cleanup for %1\n", enum_2_string (ptw)));
+
if (ptw & PostTransportAudition) {
if (auditioner && auditioner->auditioning()) {
process_function = &Session::process_audition;
@@ -722,19 +746,14 @@ Session::post_transport ()
}
}
- if (ptw & PostTransportStop) {
-
- transport_sub_state = 0;
- }
-
if (ptw & PostTransportLocate) {
+ post_locate ();
+ TFSM_EVENT (TransportFSM::locate_done());
+ }
- if (((!config.get_external_sync() && (auto_play_legal && config.get_auto_play())) && !_exporting) || (ptw & PostTransportRoll)) {
- _count_in_once = false;
- start_transport ();
- } else {
- transport_sub_state = 0;
- }
+ if (ptw & PostTransportAdjustPlaybackBuffering) {
+ /* we blocked output while this happened */
+ DiskReader::dec_no_disk_output ();
}
set_next_event ();
@@ -742,6 +761,20 @@ Session::post_transport ()
know were handled ?
*/
set_post_transport_work (PostTransportWork (0));
+
+ if (_transport_fsm->waiting_for_butler()) {
+ TFSM_EVENT (TransportFSM::butler_done());
+ }
+
+ DiskReader::dec_no_disk_output ();
+}
+
+void
+Session::schedule_butler_for_transport_work ()
+{
+ assert (_transport_fsm->waiting_for_butler ());
+ DEBUG_TRACE (DEBUG::Butler, "summon butler for transport work\n");
+ _butler->schedule_transport_work ();
}
bool
@@ -752,7 +785,7 @@ Session::maybe_stop (samplepos_t limit)
if (synced_to_engine () && config.get_jack_time_master ()) {
_engine.transport_stop ();
} else if (!synced_to_engine ()) {
- stop_transport ();
+ TFSM_EVENT (TransportFSM::stop_transport ());
}
return true;
}
@@ -861,11 +894,11 @@ Session::set_play_loop (bool yn, double speed)
rolling, do not locate to loop start.
*/
if (!transport_rolling() && (speed != 0.0)) {
- start_locate (loc->start(), true, true, false, true);
+ TFSM_EVENT (TransportFSM::locate (loc->start(), true, true, false, true));
}
} else {
if (speed != 0.0) {
- start_locate (loc->start(), true, true, false, true);
+ TFSM_EVENT (TransportFSM::locate (loc->start(), true, true, false, true));
}
}
}
@@ -1114,14 +1147,14 @@ Session::request_cancel_play_range ()
bool
Session::solo_selection_active ()
{
- if ( _soloSelection.empty() ) {
+ if (_soloSelection.empty()) {
return false;
}
return true;
}
void
-Session::solo_selection ( StripableList &list, bool new_state )
+Session::solo_selection (StripableList &list, bool new_state)
{
boost::shared_ptr<ControlList> solo_list (new ControlList);
boost::shared_ptr<ControlList> unsolo_list (new ControlList);
@@ -1176,7 +1209,7 @@ Session::butler_transport_work ()
PostTransportWork ptw = post_transport_work();
uint64_t before;
- DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = %1 at %2\n", enum_2_string (ptw), (before = g_get_monotonic_time())));
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = [%1] (0x%3%4%5) at %2\n", enum_2_string (ptw), (before = g_get_monotonic_time()), std::hex, ptw, std::dec));
if (ptw & PostTransportLocate) {
@@ -1311,18 +1344,6 @@ Session::non_realtime_overwrite (int on_entry, bool& finished)
}
}
-bool
-Session::declick_in_progress () const
-{
- boost::shared_ptr<RouteList> rl = routes.reader();
- for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- if ((*i)->declick_in_progress ()) {
- return true;
- }
- }
- return false;
-}
-
void
Session::non_realtime_locate ()
{
@@ -1370,6 +1391,7 @@ Session::non_realtime_locate ()
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
(*i)->non_realtime_locate (tf);
if (sc != g_atomic_int_get (&_seek_counter)) {
+ std::cerr << "\n\nLOCATE INTERRUPTED BY LOCATE!!!\n\n";
goto restart;
}
}
@@ -1757,7 +1779,7 @@ Session::unset_play_loop ()
if (Config->get_seamless_loop()) {
/* likely need to flush track buffers: this will locate us to wherever we are */
add_post_transport_work (PostTransportLocate);
- _butler->schedule_transport_work ();
+ TFSM_EVENT (TransportFSM::butler_required());
}
TransportStateChange (); /* EMIT SIGNAL */
}
@@ -1901,27 +1923,37 @@ Session::request_roll_at_and_return (samplepos_t start, samplepos_t return_to)
void
Session::engine_halted ()
{
- bool ignored;
-
/* there will be no more calls to process(), so
we'd better clean up for ourselves, right now.
- but first, make sure the butler is out of
- the picture.
+ We can't queue SessionEvents because they only get
+ handled from within a process callback.
*/
- if (_butler) {
- _butler->stop ();
- }
+ /* this just stops the FSM engine ... it doesn't change the state of
+ * the FSM directly or anything else ... but the FSM will be
+ * reinitialized when we call its ::start() method from
+ * ::engine_running() (if we ever get there)
+ */
- realtime_stop (false, true);
- non_realtime_stop (false, 0, ignored);
- transport_sub_state = 0;
+ _transport_fsm->backend()->stop ();
- DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC6 with speed = %1\n", _transport_speed));
- TransportStateChange (); /* EMIT SIGNAL */
+ /* Synchronously do the realtime part of a transport stop.
+ *
+ * Calling this will cause the butler to asynchronously run
+ * ::non_realtime_stop() where the rest of the "stop" work will be
+ * done.
+ */
+
+ realtime_stop (false, true);
}
+void
+Session::engine_running ()
+{
+ initialize_latencies ();
+ _transport_fsm->backend()->start ();
+}
void
Session::xrun_recovery ()
@@ -2054,7 +2086,7 @@ Session::sync_source_changed (SyncSource type, samplepos_t pos, pframes_t cycle_
longer valid with a new slave.
*/
- DiskReader::set_no_disk_output (false);
+ DiskReader::dec_no_disk_output ();
#if 0
we should not be treating specific transport masters as special cases because there maybe > 1 of a particular type
@@ -2101,3 +2133,27 @@ Session::sync_source_changed (SyncSource type, samplepos_t pos, pframes_t cycle_
set_dirty();
}
+
+bool
+Session::transport_stopped() const
+{
+ return _transport_fsm->stopped();
+}
+
+bool
+Session::transport_rolling() const
+{
+ return _transport_speed != 0.0 && _count_in_samples == 0 && _remaining_latency_preroll == 0;
+}
+
+bool
+Session::locate_pending () const
+{
+ return _transport_fsm->locating();
+}
+
+bool
+Session::declick_in_progress () const
+{
+ return _transport_fsm->declick_in_progress();
+}
diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc
index e80026742a..38af2d86b0 100644
--- a/libs/ardour/track.cc
+++ b/libs/ardour/track.cc
@@ -264,7 +264,7 @@ Track::freeze_state() const
bool
Track::declick_in_progress () const
{
- return _disk_reader->declick_in_progress ();
+ return active() && _disk_reader->declick_in_progress ();
}
bool
diff --git a/libs/ardour/transport_fsm.cc b/libs/ardour/transport_fsm.cc
new file mode 100644
index 0000000000..ea2be0eb3f
--- /dev/null
+++ b/libs/ardour/transport_fsm.cc
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "ardour/debug.h"
+#include "ardour/session.h"
+#include "ardour/transport_fsm.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+
+/* transition actions */
+
+void
+TransportFSM::start_playback (TransportFSM::start_transport const& p)
+{
+ DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_playback\n");
+ api->start_transport();
+}
+
+void
+TransportFSM::start_declick (TransportFSM::stop_transport const &s)
+{
+ DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_declick\n");
+ _last_stop = s;
+}
+
+void
+TransportFSM::stop_playback (TransportFSM::declick_done const& /*ignored*/)
+{
+ DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::stop_playback\n");
+ api->stop_transport (_last_stop.abort, _last_stop.clear_state);
+}
+
+void
+TransportFSM::save_locate_and_start_declick (TransportFSM::locate const & l)
+{
+ DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::save_locate_and_stop\n");
+ _last_locate = l;
+ start_declick (stop_transport (false, false));
+}
+
+void
+TransportFSM::start_locate (TransportFSM::locate const& l)
+{
+ DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_locate\n");
+ api->locate (l.target, l.with_roll, l.with_flush, l.with_loop, l.force);
+}
+
+void
+TransportFSM::start_saved_locate (TransportFSM::declick_done const&)
+{
+ DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::start_save\n");
+ api->locate (_last_locate.target, _last_locate.with_roll, _last_locate.with_flush, _last_locate.with_loop, _last_locate.force);
+}
+
+void
+TransportFSM::interrupt_locate (TransportFSM::locate const& l)
+{
+ DEBUG_TRACE (DEBUG::TFSMEvents, "tfsm::interrupt\n");
+ /* maintain original "with-roll" choice of initial locate, even though
+ * we are interrupting the locate to start a new one.
+ */
+ api->locate (l.target, _last_locate.with_roll, l.with_flush, l.with_loop, l.force);
+}
+
+void
+TransportFSM::schedule_butler_for_transport_work (TransportFSM::butler_required const&)
+{
+ api->schedule_butler_for_transport_work ();
+}
+
+bool
+TransportFSM::should_roll_after_locate (TransportFSM::locate_done const &)
+{
+ bool ret = api->should_roll_after_locate ();
+ DEBUG_TRACE (DEBUG::TFSMEvents, string_compose ("tfsm::should_roll_after_locate() ? %1\n", ret));
+ return ret;
+}
+
+void
+TransportFSM::roll_after_locate (TransportFSM::locate_done const &)
+{
+ DEBUG_TRACE (DEBUG::TFSMEvents, "rolling after locate\n");
+ api->start_transport ();
+}
+
diff --git a/libs/ardour/transport_master.cc b/libs/ardour/transport_master.cc
index 64c8062281..4d3aa82053 100644
--- a/libs/ardour/transport_master.cc
+++ b/libs/ardour/transport_master.cc
@@ -74,6 +74,7 @@ TransportMaster::TransportMaster (SyncSource t, std::string const & name)
, _sclock_synced (Properties::sclock_synced, false)
, _collect (Properties::collect, true)
, _connected (Properties::connected, false)
+ , port_node (X_(""))
{
register_properties ();
@@ -260,7 +261,27 @@ TransportMaster::set_state (XMLNode const & node, int /* version */)
XMLNode* pnode = node.child (X_("Port"));
if (pnode) {
- XMLNodeList const & children = pnode->children();
+ port_node = *pnode;
+
+ if (AudioEngine::instance()->running()) {
+ connect_port_using_state ();
+ }
+ }
+
+ PropertyChanged (what_changed);
+
+ return 0;
+}
+
+void
+TransportMaster::connect_port_using_state ()
+{
+ if (!_port) {
+ create_port ();
+ }
+
+ if (_port) {
+ XMLNodeList const & children = port_node.children();
for (XMLNodeList::const_iterator ci = children.begin(); ci != children.end(); ++ci) {
XMLProperty const *prop;
@@ -273,10 +294,6 @@ TransportMaster::set_state (XMLNode const & node, int /* version */)
}
}
}
-
- PropertyChanged (what_changed);
-
- return 0;
}
XMLNode&
@@ -360,21 +377,27 @@ TransportMaster::factory (SyncSource type, std::string const& name, bool removea
DEBUG_TRACE (DEBUG::Slave, string_compose ("factory-construct %1 name %2 removeable %3\n", enum_2_string (type), name, removeable));
- switch (type) {
- case MTC:
- tm.reset (new MTC_TransportMaster (name));
- break;
- case LTC:
- tm.reset (new LTC_TransportMaster (name));
- break;
- case MIDIClock:
- tm.reset (new MIDIClock_TransportMaster (name));
- break;
- case Engine:
- tm.reset (new Engine_TransportMaster (*AudioEngine::instance()));
- break;
- default:
- break;
+ try {
+ switch (type) {
+ case MTC:
+ tm.reset (new MTC_TransportMaster (name));
+ break;
+ case LTC:
+ tm.reset (new LTC_TransportMaster (name));
+ break;
+ case MIDIClock:
+ tm.reset (new MIDIClock_TransportMaster (name));
+ break;
+ case Engine:
+ tm.reset (new Engine_TransportMaster (*AudioEngine::instance()));
+ break;
+ default:
+ break;
+ }
+ } catch (...) {
+ error << string_compose (_("Construction of transport master object of type %1 failed"), enum_2_string (type)) << endmsg;
+ std::cerr << string_compose (_("Construction of transport master object of type %1 failed"), enum_2_string (type)) << std::endl;
+ return boost::shared_ptr<TransportMaster>();
}
if (tm) {
@@ -444,7 +467,7 @@ TransportMasterViaMIDI::create_midi_port (std::string const & port_name)
{
boost::shared_ptr<Port> p;
- if ((p = AudioEngine::instance()->register_input_port (DataType::MIDI, port_name)) == 0) {
+ if ((p = AudioEngine::instance()->register_input_port (DataType::MIDI, port_name, false, TransportMasterPort)) == 0) {
return boost::shared_ptr<Port> ();
}
@@ -490,4 +513,3 @@ TimecodeTransportMaster::set_fr2997 (bool yn)
PropertyChanged (Properties::fr2997);
}
}
-
diff --git a/libs/ardour/transport_master_manager.cc b/libs/ardour/transport_master_manager.cc
index 505dbe3576..93db2113ee 100644
--- a/libs/ardour/transport_master_manager.cc
+++ b/libs/ardour/transport_master_manager.cc
@@ -20,16 +20,12 @@
#include "ardour/debug.h"
#include "ardour/disk_reader.h"
#include "ardour/session.h"
+#include "ardour/rc_configuration.h"
#include "ardour/transport_master_manager.h"
#include "pbd/boost_debug.cc"
#include "pbd/i18n.h"
-
-#if __cplusplus > 199711L
-#define local_signbit(x) std::signbit (x)
-#else
-#define local_signbit(x) ((((__int64*)(&z))*) & 0x8000000000000000)
-#endif
+#include "pbd/stateful.h"
using namespace ARDOUR;
using namespace PBD;
@@ -51,6 +47,27 @@ TransportMasterManager::~TransportMasterManager ()
clear ();
}
+TransportMasterManager&
+TransportMasterManager::create ()
+{
+ assert (!_instance);
+
+ cerr << "TMM::create(), Config = " << Config << " size will be " << sizeof (TransportMasterManager) << endl;
+
+ _instance = new TransportMasterManager;
+
+ XMLNode* tmm_node = Config->extra_xml (X_("TransportMasters"));
+ if (tmm_node) {
+ cerr << " setting state via XML\n";
+ _instance->set_state (*tmm_node, Stateful::current_state_version);
+ } else {
+ cerr << " setting default config\n";
+ _instance->set_default_configuration ();
+ }
+
+ return *_instance;
+}
+
int
TransportMasterManager::set_default_configuration ()
{
@@ -106,7 +123,7 @@ TransportMasterManager::parameter_changed (std::string const & what)
if (what == "external-sync") {
if (!_session->config.get_external_sync()) {
/* disabled */
- DiskReader::set_no_disk_output (false);
+ DiskReader::dec_no_disk_output ();
}
}
}
@@ -115,7 +132,8 @@ TransportMasterManager&
TransportMasterManager::instance()
{
if (!_instance) {
- _instance = new TransportMasterManager();
+ fatal << string_compose (_("programming error:%1"), X_("TransportMasterManager::instance() called without an instance!")) << endmsg;
+ /* NOTREACHED */
}
return *_instance;
}
@@ -196,7 +214,7 @@ TransportMasterManager::pre_process_transport_masters (pframes_t nframes, sample
if (master_dll_initstate == 0) {
init_transport_master_dll (_master_speed, _master_position);
- // _master_invalid_this_cycle = true;
+ _master_invalid_this_cycle = true;
DEBUG_TRACE (DEBUG::Slave, "no roll3 - still initializing master DLL\n");
master_dll_initstate = _master_speed > 0.0 ? 1 : -1;
@@ -220,12 +238,12 @@ TransportMasterManager::pre_process_transport_masters (pframes_t nframes, sample
if (!_session->actively_recording()) {
DEBUG_TRACE (DEBUG::Slave, string_compose ("slave delta %1 greater than slave resolution %2 => no disk output\n", delta, _current_master->resolution()));
/* run routes as normal, but no disk output */
- DiskReader::set_no_disk_output (true);
+ DiskReader::inc_no_disk_output ();
} else {
- DiskReader::set_no_disk_output (false);
+ DiskReader::dec_no_disk_output ();
}
} else {
- DiskReader::set_no_disk_output (false);
+ DiskReader::dec_no_disk_output ();
}
/* inject DLL with new data */
@@ -330,6 +348,11 @@ TransportMasterManager::add (SyncSource type, std::string const & name, bool rem
}
tm = TransportMaster::factory (type, name, removeable);
+
+ if (!tm) {
+ return -1;
+ }
+
boost_debug_shared_ptr_mark_interesting (tm.get(), "tm");
ret = add_locked (tm);
}
@@ -396,6 +419,10 @@ TransportMasterManager::set_current_locked (boost::shared_ptr<TransportMaster> c
}
}
+ if (!c->usable()) {
+ return -1;
+ }
+
_current_master = c;
_master_speed = 0;
_master_position = 0;
@@ -490,6 +517,7 @@ TransportMasterManager::clear ()
int
TransportMasterManager::set_state (XMLNode const & node, int version)
{
+ PBD::stacktrace (std::cerr, 20);
assert (node.name() == state_node_name);
XMLNodeList const & children = node.children();
@@ -500,11 +528,22 @@ TransportMasterManager::set_state (XMLNode const & node, int version)
_current_master.reset ();
boost_debug_list_ptrs ();
- _transport_masters.clear ();
+ /* TramsportMasters live for the entire life of the
+ * program. TransportMasterManager::set_state() should only be
+ * called at the start of the program, and there should be no
+ * transport masters at that time.
+ */
+
+ assert (_transport_masters.empty());
for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
boost::shared_ptr<TransportMaster> tm = TransportMaster::factory (**c);
+
+ if (!tm) {
+ continue;
+ }
+
boost_debug_shared_ptr_mark_interesting (tm.get(), "tm");
if (add_locked (tm)) {
@@ -518,8 +557,17 @@ TransportMasterManager::set_state (XMLNode const & node, int version)
}
std::string current_master;
+
if (node.get_property (X_("current"), current_master)) {
+
+ /* may fal if current_master is not usable */
+
set_current (current_master);
+
+ if (!current()) {
+ set_current (MTC); // always available
+ }
+
} else {
set_current (MTC);
}
@@ -578,10 +626,14 @@ TransportMasterManager::restart ()
XMLNode* node;
if ((node = Config->transport_master_state()) != 0) {
- if (TransportMasterManager::instance().set_state (*node, Stateful::loading_state_version)) {
- error << _("Cannot restore transport master manager") << endmsg;
- /* XXX now what? */
+
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ for (TransportMasters::const_iterator tm = _transport_masters.begin(); tm != _transport_masters.end(); ++tm) {
+ (*tm)->connect_port_using_state ();
+ (*tm)->reset (false);
}
+
} else {
if (TransportMasterManager::instance().set_default_configuration ()) {
error << _("Cannot initialize transport master manager") << endmsg;
@@ -589,3 +641,16 @@ TransportMasterManager::restart ()
}
}
}
+
+void
+TransportMasterManager::reconnect_ports ()
+{
+ DEBUG_TRACE (DEBUG::Slave, "reconnecting all transport master ports\n");
+ {
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ for (TransportMasters::const_iterator tm = _transport_masters.begin(); tm != _transport_masters.end(); ++tm) {
+ (*tm)->connect_port_using_state ();
+ }
+ }
+}
diff --git a/libs/ardour/wscript b/libs/ardour/wscript
index 9e3283a173..dbdce22361 100644
--- a/libs/ardour/wscript
+++ b/libs/ardour/wscript
@@ -248,6 +248,7 @@ libardour_sources = [
'track.cc',
'transient_detector.cc',
'transform.cc',
+ 'transport_fsm.cc',
'transport_master.cc',
'transport_master_manager.cc',
'transpose.cc',
diff --git a/libs/pbd/transmitter.cc b/libs/pbd/transmitter.cc
index f1ae75deb9..719e30a122 100644
--- a/libs/pbd/transmitter.cc
+++ b/libs/pbd/transmitter.cc
@@ -57,8 +57,6 @@ void
Transmitter::deliver ()
{
- string foo;
-
/* NOTE: this is just a default action for a Transmitter or a
derived class. Any class can override this to produce some
other action when deliver() is called.
@@ -68,8 +66,7 @@ Transmitter::deliver ()
/* send the SigC++ signal */
- foo = str();
- (*send) (channel, foo.c_str());
+ (*send) (channel, str().c_str());
/* XXX when or how can we delete this ? */
// delete foo;