/*
- Copyright (C) 2002 Paul Davis
-
- 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-
+ * Copyright (C) 2018-2019 Paul Davis <paul@linuxaudiosystems.com>
+ * Copyright (C) 2018 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 <vector>
+#include "pbd/boost_debug.h"
#include "pbd/debug.h"
+#include "pbd/i18n.h"
#include "ardour/audioengine.h"
+#include "ardour/debug.h"
#include "ardour/midi_port.h"
#include "ardour/session.h"
#include "ardour/transport_master.h"
#include "ardour/types_convert.h"
#include "ardour/utils.h"
-#include "pbd/i18n.h"
namespace ARDOUR {
namespace Properties {
, _session (0)
, _current_delta (0)
, _pending_collect (true)
+ , _removeable (false)
, _request_mask (Properties::allowed_transport_requests, TransportRequestType (0))
- , _locked (Properties::locked, false)
, _sclock_synced (Properties::sclock_synced, false)
, _collect (Properties::collect, true)
, _connected (Properties::connected, false)
+ , port_node (X_(""))
{
register_properties ();
TransportMaster::~TransportMaster()
{
- delete _session;
+ 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
+TransportMaster::speed_and_position (double& speed, samplepos_t& pos, samplepos_t& lp, samplepos_t& when, samplepos_t now)
+{
+ if (!_collect) {
+ return false;
+ }
+
+ if (!locked()) {
+ DEBUG_TRACE (DEBUG::Slave, string_compose ("%1: not locked, no speed and position!\n", name()));
+ return false;
+ }
+
+ SafeTime last;
+ current.safe_read (last);
+
+ if (last.timestamp == 0) {
+ return false;
+ }
+
+ if (last.timestamp && now > last.timestamp && now - last.timestamp > (2.0 * update_interval())) {
+ /* no timecode for two cycles - conclude that it's stopped */
+
+ if (!Config->get_transport_masters_just_roll_when_sync_lost()) {
+ speed = 0;
+ pos = last.position;
+ lp = last.position;
+ when = last.timestamp;
+ _current_delta = 0;
+ DEBUG_TRACE (DEBUG::Slave, string_compose ("%1 not seen since %2 vs %3 (%4) with seekahead = %5 reset pending, pos = %6\n", name(), last.timestamp, now, (now - last.timestamp), update_interval(), pos));
+ return false;
+ }
+ }
+
+ lp = last.position;
+ when = last.timestamp;
+ speed = last.speed;
+
+ /* provide a .1% deadzone to lock the speed */
+ if (fabs (speed - 1.0) <= 0.001) {
+ speed = 1.0;
+ }
+
+ pos = last.position + (now - last.timestamp) * speed;
+
+ DEBUG_TRACE (DEBUG::Slave, string_compose ("%1 sync spd: %2 pos: %3 | last-pos: %4 @ %7| elapsed: %5 | speed: %6\n",
+ name(), speed, pos, last.position, (now - last.timestamp), speed, when));
+
+ return true;
}
void
_xml_node_name = state_node_name;
add_property (_name);
- add_property (_locked);
add_property (_collect);
add_property (_sclock_synced);
add_property (_request_mask);
{
if (_name != str) {
_name = str;
- PropertyChange (Properties::name);
+ PropertyChanged (Properties::name);
}
}
void
TransportMaster::set_collect (bool yn)
{
- _pending_collect = yn;
+ /* theoretical race condition */
+
+ if (_connected) {
+ _pending_collect = yn;
+ } else {
+ if (_collect != yn) {
+ _pending_collect = _collect = yn;
+ PropertyChanged (Properties::collect);
+ }
+ }
}
void
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;
}
}
}
-
- PropertyChanged (what_changed);
-
- return 0;
}
XMLNode&
{
XMLNode* node = new XMLNode (state_node_name);
node->set_property (X_("type"), _type);
+ node->set_property (X_("removeable"), _removeable);
add_properties (*node);
SyncSource type;
std::string name;
+ bool removeable;
if (!node.get_property (X_("type"), type)) {
return boost::shared_ptr<TransportMaster>();
return boost::shared_ptr<TransportMaster>();
}
- return factory (type, name);
+ if (!node.get_property (X_("removeable"), removeable)) {
+ /* development versions of 6.0 didn't have this property for a
+ while. Any TM listed in XML at that time was non-removeable
+ */
+ removeable = false;
+ }
+
+ DEBUG_TRACE (DEBUG::Slave, string_compose ("xml-construct %1 name %2 removeable %3\n", enum_2_string (type), name, removeable));
+
+ return factory (type, name, removeable);
}
boost::shared_ptr<TransportMaster>
-TransportMaster::factory (SyncSource type, std::string const& name)
+TransportMaster::factory (SyncSource type, std::string const& name, bool removeable)
{
/* XXX need to count existing sources of a given type */
- switch (type) {
+ boost::shared_ptr<TransportMaster> tm;
+
+ DEBUG_TRACE (DEBUG::Slave, string_compose ("factory-construct %1 name %2 removeable %3\n", enum_2_string (type), name, removeable));
+
+ 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) {
+ tm->set_removeable (removeable);
+ }
+
+ return tm;
+}
+
+/** @param sh Return a short version of the string */
+std::string
+TransportMaster::display_name (bool sh) const
+{
+
+ switch (_type) {
+ case Engine:
+ /* no other backends offer sync for now ... deal with this if we
+ * ever have to.
+ */
+ return S_("SyncSource|JACK");
+
case MTC:
- return boost::shared_ptr<TransportMaster> (new MTC_TransportMaster (sync_source_to_string (type)));
- case LTC:
- return boost::shared_ptr<TransportMaster> (new LTC_TransportMaster (sync_source_to_string (type)));
+ if (sh) {
+ if (name().length() <= 4) {
+ return name();
+ }
+ return S_("SyncSource|MTC");
+ } else {
+ return name();
+ }
+
case MIDIClock:
- return boost::shared_ptr<TransportMaster> (new MIDIClock_TransportMaster (sync_source_to_string (type)));
- case Engine:
- return boost::shared_ptr<TransportMaster> (new Engine_TransportMaster (*AudioEngine::instance()));
- default:
- break;
+ if (sh) {
+ if (name().length() <= 4) {
+ return name();
+ }
+ return S_("SyncSource|M-Clk");
+ } else {
+ return name();
+ }
+
+ case LTC:
+ if (sh) {
+ if (name().length() <= 4) {
+ return name();
+ }
+ return S_("SyncSource|LTC");
+ } else {
+ return name();
+ }
}
+ /* GRRRR .... stupid, stupid gcc - you can't get here from there, all enum values are handled */
+ return S_("SyncSource|JACK");
+}
- return boost::shared_ptr<TransportMaster>();
+void
+TransportMaster::unregister_port ()
+{
+ if (_port) {
+ AudioEngine::instance()->unregister_port (_port);
+ _port.reset ();
+ }
}
boost::shared_ptr<Port>
{
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> ();
}