new approach to handling Transport Masters when engine is restarted

Trust that ::reset() works for all transport masters, and call it when engine is stopped. This way
the transport masters are ready to be called again as soon as the engine restarts.
This commit is contained in:
Paul Davis 2019-01-24 22:05:20 -07:00
parent c01ab83e1f
commit 1be3301342
14 changed files with 139 additions and 50 deletions

View file

@ -33,7 +33,7 @@
#include "pbd/i18n.h" #include "pbd/i18n.h"
#include "pbd/properties.h" #include "pbd/properties.h"
#include "pbd/signals.h" #include "pbd/signals.h"
#include "pbd/stateful.h" #include "pbd/statefuldestructible.h"
#include "temporal/time.h" #include "temporal/time.h"
@ -214,6 +214,8 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful {
*/ */
virtual bool speed_and_position (double& speed, samplepos_t& position, samplepos_t & lp, samplepos_t & when, samplepos_t now); virtual bool speed_and_position (double& speed, samplepos_t& position, samplepos_t & lp, samplepos_t & when, samplepos_t now);
virtual void reset (bool with_position) = 0;
/** /**
* reports to ARDOUR whether the TransportMaster is currently synced to its external * reports to ARDOUR whether the TransportMaster is currently synced to its external
* time source. * time source.
@ -346,6 +348,8 @@ class LIBARDOUR_API TransportMaster : public PBD::Stateful {
std::string display_name (bool sh/*ort*/ = true) const; std::string display_name (bool sh/*ort*/ = true) const;
virtual void unregister_port ();
protected: protected:
SyncSource _type; SyncSource _type;
PBD::Property<std::string> _name; PBD::Property<std::string> _name;
@ -423,6 +427,9 @@ class LIBARDOUR_API MTC_TransportMaster : public TimecodeTransportMaster, public
void pre_process (pframes_t nframes, samplepos_t now, boost::optional<samplepos_t>); void pre_process (pframes_t nframes, samplepos_t now, boost::optional<samplepos_t>);
void unregister_port ();
void reset (bool with_pos);
bool locked() const; bool locked() const;
bool ok() const; bool ok() const;
void handle_locate (const MIDI::byte*); void handle_locate (const MIDI::byte*);
@ -467,7 +474,6 @@ class LIBARDOUR_API MTC_TransportMaster : public TimecodeTransportMaster, public
Timecode::Time timecode; Timecode::Time timecode;
bool printed_timecode_warning; bool printed_timecode_warning;
void reset (bool with_pos);
void queue_reset (bool with_pos); void queue_reset (bool with_pos);
void maybe_reset (); void maybe_reset ();
@ -490,6 +496,7 @@ public:
void pre_process (pframes_t nframes, samplepos_t now, boost::optional<samplepos_t>); void pre_process (pframes_t nframes, samplepos_t now, boost::optional<samplepos_t>);
void reset (bool with_pos);
bool locked() const; bool locked() const;
bool ok() const; bool ok() const;
@ -510,7 +517,6 @@ public:
bool detect_discontinuity(LTCFrameExt *, int, bool); bool detect_discontinuity(LTCFrameExt *, int, bool);
bool detect_ltc_fps(int, bool); bool detect_ltc_fps(int, bool);
bool equal_ltc_sample_time(LTCFrame *a, LTCFrame *b); bool equal_ltc_sample_time(LTCFrame *a, LTCFrame *b);
void reset (bool with_ts = true);
void resync_xrun(); void resync_xrun();
void resync_latency(); void resync_latency();
void parse_timecode_offset(); void parse_timecode_offset();
@ -550,10 +556,13 @@ class LIBARDOUR_API MIDIClock_TransportMaster : public TransportMaster, public T
void set_session (Session*); void set_session (Session*);
void unregister_port ();
void pre_process (pframes_t nframes, samplepos_t now, boost::optional<samplepos_t>); void pre_process (pframes_t nframes, samplepos_t now, boost::optional<samplepos_t>);
void rebind (MidiPort&); void rebind (MidiPort&);
void reset (bool with_pos);
bool locked() const; bool locked() const;
bool ok() const; bool ok() const;
bool starting() const; bool starting() const;
@ -594,7 +603,6 @@ class LIBARDOUR_API MIDIClock_TransportMaster : public TransportMaster, public T
bool _running; bool _running;
double _bpm; double _bpm;
void reset ();
void start (MIDI::Parser& parser, samplepos_t timestamp); void start (MIDI::Parser& parser, samplepos_t timestamp);
void contineu (MIDI::Parser& parser, samplepos_t timestamp); void contineu (MIDI::Parser& parser, samplepos_t timestamp);
void stop (MIDI::Parser& parser, samplepos_t timestamp); void stop (MIDI::Parser& parser, samplepos_t timestamp);
@ -616,6 +624,7 @@ class LIBARDOUR_API Engine_TransportMaster : public TransportMaster
bool speed_and_position (double& speed, samplepos_t& pos, samplepos_t &, samplepos_t &, samplepos_t); bool speed_and_position (double& speed, samplepos_t& pos, samplepos_t &, samplepos_t &, samplepos_t);
bool starting() const { return _starting; } bool starting() const { return _starting; }
void reset (bool with_position);
bool locked() const; bool locked() const;
bool ok() const; bool ok() const;
samplecnt_t update_interval () const; samplecnt_t update_interval () const;

View file

@ -36,6 +36,8 @@ class LIBARDOUR_API TransportMasterManager : public boost::noncopyable
~TransportMasterManager (); ~TransportMasterManager ();
int set_default_configuration (); int set_default_configuration ();
void restart ();
void engine_stopped ();
static TransportMasterManager& instance(); static TransportMasterManager& instance();
/* this method is not thread-safe and is intended to be used only /* this method is not thread-safe and is intended to be used only

View file

@ -1002,6 +1002,8 @@ AudioEngine::stop (bool for_latency)
} }
if (stop_engine) { if (stop_engine) {
TransportMasterManager& tmm (TransportMasterManager::instance());
tmm.engine_stopped ();
Stopped (); /* EMIT SIGNAL */ Stopped (); /* EMIT SIGNAL */
} }

View file

@ -57,6 +57,12 @@ Engine_TransportMaster::check_backend()
} }
} }
void
Engine_TransportMaster::reset (bool)
{
_starting = false;
}
bool bool
Engine_TransportMaster::locked() const Engine_TransportMaster::locked() const
{ {

View file

@ -561,7 +561,7 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
ARDOUR::AudioEngine::create (); ARDOUR::AudioEngine::create ();
/* This will run only once in whatever thread calls AudioEngine::start() */ /* This will run in whatever thread calls AudioEngine::start() */
ARDOUR::AudioEngine::instance()->Running.connect_same_thread (engine_startup_connection, ARDOUR::init_post_engine); ARDOUR::AudioEngine::instance()->Running.connect_same_thread (engine_startup_connection, ARDOUR::init_post_engine);
/* it is unfortunate that we need to include reserved names here that /* it is unfortunate that we need to include reserved names here that
@ -599,39 +599,30 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
} }
void void
ARDOUR::init_post_engine (uint32_t /* ignored */) ARDOUR::init_post_engine (uint32_t start_cnt)
{ {
XMLNode* node; XMLNode* node;
std::cerr << "Engine stated, libardour inits, cnt = " << start_cnt << std::endl;
if (start_cnt == 0) {
/* find plugins */
ARDOUR::PluginManager::instance().refresh (!Config->get_discover_vst_on_start());
}
if ((node = Config->control_protocol_state()) != 0) { if ((node = Config->control_protocol_state()) != 0) {
ControlProtocolManager::instance().set_state (*node, 0 /* here: global-config state */); ControlProtocolManager::instance().set_state (*node, 0 /* here: global-config state */);
} }
if ((node = Config->transport_master_state()) != 0) { if (start_cnt == 0) {
if (TransportMasterManager::instance().set_state (*node, Stateful::loading_state_version)) { TransportMasterManager::instance().restart ();
error << _("Cannot restore transport master manager") << endmsg;
/* XXX now what? */
}
} else {
if (TransportMasterManager::instance().set_default_configuration ()) {
error << _("Cannot initialize transport master manager") << endmsg;
/* XXX now what? */
}
} }
/* find plugins */
ARDOUR::PluginManager::instance().refresh (!Config->get_discover_vst_on_start());
/* Don't do this again - we are only meant to execute on the first
* engine start, not any subsequence starts
*/
engine_startup_connection.disconnect ();
} }
void void
ARDOUR::cleanup () ARDOUR::cleanup ()
{ {
if (!libardour_initialized) { if (!libardour_initialized) {
return; return;

View file

@ -104,7 +104,7 @@ LTC_TransportMaster::set_session (Session *s)
decoder = ltc_decoder_create((int) samples_per_ltc_frame, 128 /*queue size*/); decoder = ltc_decoder_create((int) samples_per_ltc_frame, 128 /*queue size*/);
parse_timecode_offset(); parse_timecode_offset();
reset(); reset (true);
_session->config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&LTC_TransportMaster::parameter_changed, this, _1)); _session->config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&LTC_TransportMaster::parameter_changed, this, _1));
} }
@ -189,10 +189,10 @@ LTC_TransportMaster::resync_latency()
} }
void void
LTC_TransportMaster::reset (bool with_ts) LTC_TransportMaster::reset (bool with_position)
{ {
DEBUG_TRACE (DEBUG::LTC, "LTC reset()\n"); DEBUG_TRACE (DEBUG::LTC, "LTC reset()\n");
if (with_ts) { if (with_position) {
current.update (current.position, 0, current.speed); current.update (current.position, 0, current.speed);
_current_delta = 0; _current_delta = 0;
} }
@ -381,7 +381,7 @@ LTC_TransportMaster::process_ltc(samplepos_t const now)
} }
if (!ltc_is_stationary && detect_ltc_fps (stime.frame, (sample.ltc.dfbit)? true : false)) { if (!ltc_is_stationary && detect_ltc_fps (stime.frame, (sample.ltc.dfbit)? true : false)) {
reset(); reset(true);
fps_detected=true; fps_detected=true;
} }
@ -528,7 +528,7 @@ LTC_TransportMaster::pre_process (ARDOUR::pframes_t nframes, samplepos_t now, bo
} else if (skip != 0) { } else if (skip != 0) {
/* this should never happen. it may if monotonic_cnt, now overflow on 64bit */ /* this should never happen. it may if monotonic_cnt, now overflow on 64bit */
DEBUG_TRACE (DEBUG::LTC, string_compose("engine skipped %1 samples\n", skip)); DEBUG_TRACE (DEBUG::LTC, string_compose("engine skipped %1 samples\n", skip));
reset(); reset(true);
} }
/* Now feed the incoming LTC signal into the decoder */ /* Now feed the incoming LTC signal into the decoder */
@ -553,7 +553,7 @@ LTC_TransportMaster::pre_process (ARDOUR::pframes_t nframes, samplepos_t now, bo
if (abs (now - current.timestamp) > FLYWHEEL_TIMEOUT) { if (abs (now - current.timestamp) > FLYWHEEL_TIMEOUT) {
DEBUG_TRACE (DEBUG::LTC, "flywheel timeout\n"); DEBUG_TRACE (DEBUG::LTC, "flywheel timeout\n");
reset(); reset(true);
/* don't change position from last known */ /* don't change position from last known */
return; return;

View file

@ -88,7 +88,7 @@ MIDIClock_TransportMaster::set_session (Session *session)
parser.stop.connect_same_thread (port_connections, boost::bind (&MIDIClock_TransportMaster::stop, this, _1, _2)); parser.stop.connect_same_thread (port_connections, boost::bind (&MIDIClock_TransportMaster::stop, this, _1, _2));
parser.position.connect_same_thread (port_connections, boost::bind (&MIDIClock_TransportMaster::position, this, _1, _2, _3, _4)); parser.position.connect_same_thread (port_connections, boost::bind (&MIDIClock_TransportMaster::position, this, _1, _2, _3, _4));
reset (); reset (true);
} }
} }
@ -284,18 +284,22 @@ MIDIClock_TransportMaster::start (Parser& /*parser*/, samplepos_t timestamp)
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("MIDIClock_TransportMaster got start message at time %1 engine time %2 transport_sample %3\n", timestamp, ENGINE->sample_time(), _session->transport_sample())); DEBUG_TRACE (DEBUG::MidiClock, string_compose ("MIDIClock_TransportMaster got start message at time %1 engine time %2 transport_sample %3\n", timestamp, ENGINE->sample_time(), _session->transport_sample()));
if (!_running) { if (!_running) {
reset(); reset(true);
_running = true; _running = true;
current.update (_session->transport_sample(), timestamp, 0); current.update (_session->transport_sample(), timestamp, 0);
} }
} }
void void
MIDIClock_TransportMaster::reset () MIDIClock_TransportMaster::reset (bool with_position)
{ {
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("MidiClock Master reset(): calculated filter for period size %2\n", ENGINE->samples_per_cycle())); DEBUG_TRACE (DEBUG::MidiClock, string_compose ("MidiClock Master reset(): calculated filter for period size %2\n", ENGINE->samples_per_cycle()));
current.update (_session->transport_sample(), 0, 0); if (with_position) {
current.update (_session->transport_sample(), 0, 0);
} else {
current.update (0, 0, 0);
}
_running = false; _running = false;
_current_delta = 0; _current_delta = 0;
@ -409,3 +413,11 @@ MIDIClock_TransportMaster::delta_string() const
} }
return std::string(delta); return std::string(delta);
} }
void
MIDIClock_TransportMaster::unregister_port ()
{
_midi_port.reset ();
TransportMaster::unregister_port ();
}

View file

@ -587,3 +587,10 @@ MTC_TransportMaster::delta_string () const
} }
return std::string(delta); return std::string(delta);
} }
void
MTC_TransportMaster::unregister_port ()
{
_midi_port.reset ();
TransportMaster::unregister_port ();
}

View file

@ -184,7 +184,12 @@ Session::ltc_tx_send_time_code_for_cycle (samplepos_t start_sample, samplepos_t
return; return;
} }
if (!TransportMasterManager::instance().current()) {
return;
}
SyncSource sync_src = TransportMasterManager::instance().current()->type(); SyncSource sync_src = TransportMasterManager::instance().current()->type();
if (engine().freewheeling() || !Config->get_send_ltc() if (engine().freewheeling() || !Config->get_send_ltc()
/* TODO /* TODO
* decide which time-sources we can generated LTC from. * decide which time-sources we can generated LTC from.
@ -194,7 +199,7 @@ Session::ltc_tx_send_time_code_for_cycle (samplepos_t start_sample, samplepos_t
|| (config.get_external_sync() && sync_src == LTC) || (config.get_external_sync() && sync_src == LTC)
|| (config.get_external_sync() && sync_src == MTC) || (config.get_external_sync() && sync_src == MTC)
*/ */
||(config.get_external_sync() && sync_src == MIDIClock) ||(config.get_external_sync() && sync_src == MIDIClock)
) { ) {
return; return;
} }

View file

@ -861,6 +861,7 @@ Session::process_event (SessionEvent* ev)
break; break;
case SessionEvent::SetTransportMaster: case SessionEvent::SetTransportMaster:
cerr << "Process TMM current request\n";
TransportMasterManager::instance().set_current (ev->transport_master); TransportMasterManager::instance().set_current (ev->transport_master);
break; break;

View file

@ -4098,6 +4098,7 @@ Session::config_changed (std::string p, bool ours)
first_file_data_format_reset = false; first_file_data_format_reset = false;
} else if (p == "external-sync") { } else if (p == "external-sync") {
std::cerr << "param change, rss to " << TransportMasterManager::instance().current() << std::endl;
request_sync_source (TransportMasterManager::instance().current()); request_sync_source (TransportMasterManager::instance().current());
} else if (p == "denormal-model") { } else if (p == "denormal-model") {
setup_fpu (); setup_fpu ();

View file

@ -1975,7 +1975,7 @@ Session::transport_master() const
bool bool
Session::transport_master_is_external () const Session::transport_master_is_external () const
{ {
return config.get_external_sync(); return TransportMasterManager::instance().current() && config.get_external_sync();
} }
void void

View file

@ -19,7 +19,9 @@
#include <vector> #include <vector>
#include "pbd/boost_debug.h"
#include "pbd/debug.h" #include "pbd/debug.h"
#include "pbd/i18n.h"
#include "ardour/audioengine.h" #include "ardour/audioengine.h"
#include "ardour/debug.h" #include "ardour/debug.h"
@ -30,7 +32,6 @@
#include "ardour/types_convert.h" #include "ardour/types_convert.h"
#include "ardour/utils.h" #include "ardour/utils.h"
#include "pbd/i18n.h"
namespace ARDOUR { namespace ARDOUR {
namespace Properties { namespace Properties {
@ -83,6 +84,9 @@ TransportMaster::TransportMaster (SyncSource t, std::string const & name)
TransportMaster::~TransportMaster() TransportMaster::~TransportMaster()
{ {
DEBUG_TRACE (DEBUG::Destruction, string_compose ("destroying transport master \"%1\" along with port %2\n", name(), (_port ? _port->name() : std::string ("no port"))));
unregister_port ();
} }
bool bool
@ -431,6 +435,15 @@ TransportMaster::display_name (bool sh) const
return S_("SyncSource|JACK"); return S_("SyncSource|JACK");
} }
void
TransportMaster::unregister_port ()
{
if (_port) {
AudioEngine::instance()->unregister_port (_port);
_port.reset ();
}
}
boost::shared_ptr<Port> boost::shared_ptr<Port>
TransportMasterViaMIDI::create_midi_port (std::string const & port_name) TransportMasterViaMIDI::create_midi_port (std::string const & port_name)
{ {

View file

@ -22,6 +22,7 @@
#include "ardour/session.h" #include "ardour/session.h"
#include "ardour/transport_master_manager.h" #include "ardour/transport_master_manager.h"
#include "pbd/boost_debug.cc"
#include "pbd/i18n.h" #include "pbd/i18n.h"
#if __cplusplus > 199711L #if __cplusplus > 199711L
@ -54,19 +55,24 @@ int
TransportMasterManager::set_default_configuration () TransportMasterManager::set_default_configuration ()
{ {
try { try {
clear ();
/* setup default transport masters. Most people will never need any /* setup default transport masters. Most people will never need any
others others
*/ */
add (Engine, X_("JACK Transport"), false); add (Engine, X_("JACK Transport"), false);
add (MTC, X_("MTC"), false); add (MTC, X_("MTC"), false);
add (LTC, X_("LTC"), false); add (LTC, X_("LTC"), false);
add (MIDIClock, X_("MIDI Clock"), false); add (MIDIClock, X_("MIDI Clock"), false);
} catch (...) { } catch (...) {
return -1; return -1;
} }
_current_master = _transport_masters.back(); _current_master = _transport_masters.back();
cerr << "default current master (back) is " << _current_master->name() << endl;
return 0; return 0;
} }
@ -324,6 +330,7 @@ TransportMasterManager::add (SyncSource type, std::string const & name, bool rem
} }
tm = TransportMaster::factory (type, name, removeable); tm = TransportMaster::factory (type, name, removeable);
boost_debug_shared_ptr_mark_interesting (tm.get(), "tm");
ret = add_locked (tm); ret = add_locked (tm);
} }
@ -382,9 +389,11 @@ TransportMasterManager::remove (std::string const & name)
int int
TransportMasterManager::set_current_locked (boost::shared_ptr<TransportMaster> c) TransportMasterManager::set_current_locked (boost::shared_ptr<TransportMaster> c)
{ {
if (find (_transport_masters.begin(), _transport_masters.end(), c) == _transport_masters.end()) { if (c) {
warning << string_compose (X_("programming error: attempt to use unknown transport master named \"%1\"\n"), c->name()); if (find (_transport_masters.begin(), _transport_masters.end(), c) == _transport_masters.end()) {
return -1; warning << string_compose (X_("programming error: attempt to use unknown transport master \"%1\"\n"), c->name());
return -1;
}
} }
_current_master = c; _current_master = c;
@ -393,7 +402,7 @@ TransportMasterManager::set_current_locked (boost::shared_ptr<TransportMaster> c
master_dll_initstate = 0; master_dll_initstate = 0;
DEBUG_TRACE (DEBUG::Slave, string_compose ("current transport master set to %1\n", c->name())); DEBUG_TRACE (DEBUG::Slave, string_compose ("current transport master set to %1\n", (c ? c->name() : string ("none"))));
return 0; return 0;
} }
@ -471,6 +480,7 @@ TransportMasterManager::clear ()
{ {
{ {
Glib::Threads::RWLock::WriterLock lm (lock); Glib::Threads::RWLock::WriterLock lm (lock);
_current_master.reset ();
_transport_masters.clear (); _transport_masters.clear ();
} }
@ -484,18 +494,18 @@ TransportMasterManager::set_state (XMLNode const & node, int version)
XMLNodeList const & children = node.children(); XMLNodeList const & children = node.children();
if (!children.empty()) {
_transport_masters.clear ();
}
{ {
Glib::Threads::RWLock::WriterLock lm (lock); Glib::Threads::RWLock::WriterLock lm (lock);
_current_master.reset ();
boost_debug_list_ptrs ();
_transport_masters.clear ();
for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) { for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
boost::shared_ptr<TransportMaster> tm = TransportMaster::factory (**c); boost::shared_ptr<TransportMaster> tm = TransportMaster::factory (**c);
boost_debug_shared_ptr_mark_interesting (tm.get(), "tm");
if (add_locked (tm)) { if (add_locked (tm)) {
continue; continue;
@ -548,3 +558,33 @@ TransportMasterManager::master_by_type (SyncSource src) const
return boost::shared_ptr<TransportMaster> (); return boost::shared_ptr<TransportMaster> ();
} }
void
TransportMasterManager::engine_stopped ()
{
{
Glib::Threads::RWLock::ReaderLock lm (lock);
for (TransportMasters::const_iterator tm = _transport_masters.begin(); tm != _transport_masters.end(); ++tm) {
(*tm)->reset (false);
}
}
}
void
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? */
}
} else {
if (TransportMasterManager::instance().set_default_configuration ()) {
error << _("Cannot initialize transport master manager") << endmsg;
/* XXX now what? */
}
}
}