2 * Copyright (C) 2018-2019 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2018 Robin Gareus <robin@gareus.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "pbd/boost_debug.h"
23 #include "pbd/debug.h"
26 #include "ardour/audioengine.h"
27 #include "ardour/debug.h"
28 #include "ardour/midi_port.h"
29 #include "ardour/session.h"
30 #include "ardour/transport_master.h"
31 #include "ardour/transport_master_manager.h"
32 #include "ardour/types_convert.h"
33 #include "ardour/utils.h"
37 namespace Properties {
38 PBD::PropertyDescriptor<bool> fr2997;
39 PBD::PropertyDescriptor<bool> sclock_synced;
40 PBD::PropertyDescriptor<bool> collect;
41 PBD::PropertyDescriptor<bool> connected;
42 PBD::PropertyDescriptor<TransportRequestType> allowed_transport_requests;
46 using namespace ARDOUR;
50 TransportMaster::make_property_quarks ()
52 Properties::fr2997.property_id = g_quark_from_static_string (X_("fr2997"));
53 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fr2997 = %1\n", Properties::fr2997.property_id));
54 Properties::sclock_synced.property_id = g_quark_from_static_string (X_("sclock_synced"));
55 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sclock_synced = %1\n", Properties::sclock_synced.property_id));
56 Properties::collect.property_id = g_quark_from_static_string (X_("collect"));
57 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for collect = %1\n", Properties::collect.property_id));
58 Properties::connected.property_id = g_quark_from_static_string (X_("connected"));
59 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for connected = %1\n", Properties::connected.property_id));
60 Properties::allowed_transport_requests.property_id = g_quark_from_static_string (X_("allowed_transport_requests"));
61 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for allowed_transport_requests = %1\n", Properties::allowed_transport_requests.property_id));
64 const std::string TransportMaster::state_node_name = X_("TransportMaster");
66 TransportMaster::TransportMaster (SyncSource t, std::string const & name)
68 , _name (Properties::name, name)
71 , _pending_collect (true)
73 , _request_mask (Properties::allowed_transport_requests, TransportRequestType (0))
74 , _sclock_synced (Properties::sclock_synced, false)
75 , _collect (Properties::collect, true)
76 , _connected (Properties::connected, false)
79 register_properties ();
81 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect_same_thread (port_connection, boost::bind (&TransportMaster::connection_handler, this, _1, _2, _3, _4, _5));
82 ARDOUR::AudioEngine::instance()->Running.connect_same_thread (backend_connection, boost::bind (&TransportMaster::check_backend, this));
85 TransportMaster::~TransportMaster()
87 DEBUG_TRACE (DEBUG::Destruction, string_compose ("destroying transport master \"%1\" along with port %2\n", name(), (_port ? _port->name() : std::string ("no port"))));
93 TransportMaster::speed_and_position (double& speed, samplepos_t& pos, samplepos_t& lp, samplepos_t& when, samplepos_t now)
100 DEBUG_TRACE (DEBUG::Slave, string_compose ("%1: not locked, no speed and position!\n", name()));
105 current.safe_read (last);
107 if (last.timestamp == 0) {
111 if (last.timestamp && now > last.timestamp && now - last.timestamp > (2.0 * update_interval())) {
112 /* no timecode for two cycles - conclude that it's stopped */
114 if (!Config->get_transport_masters_just_roll_when_sync_lost()) {
118 when = last.timestamp;
120 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));
126 when = last.timestamp;
129 /* provide a .1% deadzone to lock the speed */
130 if (fabs (speed - 1.0) <= 0.001) {
134 pos = last.position + (now - last.timestamp) * speed;
136 DEBUG_TRACE (DEBUG::Slave, string_compose ("%1 sync spd: %2 pos: %3 | last-pos: %4 @ %7| elapsed: %5 | speed: %6\n",
137 name(), speed, pos, last.position, (now - last.timestamp), speed, when));
143 TransportMaster::register_properties ()
145 _xml_node_name = state_node_name;
147 add_property (_name);
148 add_property (_collect);
149 add_property (_sclock_synced);
150 add_property (_request_mask);
152 /* we omit _connected since it is derived from port state, and merely
153 * used for signalling
158 TransportMaster::set_name (std::string const & str)
162 PropertyChanged (Properties::name);
167 TransportMaster::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
173 const std::string fqn = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->name());
175 if (fqn == name1 || fqn == name2) {
179 /* XXX technically .. if the user makes an N->1 connection to
180 * this transport master's port, this simple minded logic is
181 * not sufficient. But the user shouldn't do that ...
190 PropertyChanged (Properties::connected);
199 TransportMaster::check_collect()
205 /* XXX should probably use boost::atomic something or other here */
207 if (_pending_collect != _collect) {
208 if (_pending_collect) {
211 if (TransportMasterManager::instance().current().get() == this) {
213 _session->config.set_external_sync (false);
217 _collect = _pending_collect;
218 PropertyChanged (Properties::collect);
225 TransportMaster::set_collect (bool yn)
227 /* theoretical race condition */
230 _pending_collect = yn;
232 if (_collect != yn) {
233 _pending_collect = _collect = yn;
234 PropertyChanged (Properties::collect);
240 TransportMaster::set_sample_clock_synced (bool yn)
242 if (yn != _sclock_synced) {
244 PropertyChanged (Properties::sclock_synced);
249 TransportMaster::set_session (Session* s)
255 TransportMaster::set_state (XMLNode const & node, int /* version */)
257 PropertyChange what_changed;
259 what_changed = set_values (node);
261 XMLNode* pnode = node.child (X_("Port"));
266 if (AudioEngine::instance()->running()) {
267 connect_port_using_state ();
271 PropertyChanged (what_changed);
277 TransportMaster::connect_port_using_state ()
284 XMLNodeList const & children = port_node.children();
285 for (XMLNodeList::const_iterator ci = children.begin(); ci != children.end(); ++ci) {
287 XMLProperty const *prop;
289 if ((*ci)->name() == X_("Connection")) {
290 if ((prop = (*ci)->property (X_("other"))) == 0) {
293 _port->connect (prop->value());
300 TransportMaster::get_state ()
302 XMLNode* node = new XMLNode (state_node_name);
303 node->set_property (X_("type"), _type);
304 node->set_property (X_("removeable"), _removeable);
306 add_properties (*node);
309 std::vector<std::string> connections;
311 XMLNode* pnode = new XMLNode (X_("Port"));
313 if (_port->get_connections (connections)) {
315 std::vector<std::string>::const_iterator ci;
316 std::sort (connections.begin(), connections.end());
318 for (ci = connections.begin(); ci != connections.end(); ++ci) {
320 /* if its a connection to our own port,
321 return only the port name, not the
322 whole thing. this allows connections
323 to be re-established even when our
324 client name is different.
327 XMLNode* cnode = new XMLNode (X_("Connection"));
329 cnode->set_property (X_("other"), AudioEngine::instance()->make_port_name_relative (*ci));
330 pnode->add_child_nocopy (*cnode);
334 node->add_child_nocopy (*pnode);
340 boost::shared_ptr<TransportMaster>
341 TransportMaster::factory (XMLNode const & node)
343 if (node.name() != TransportMaster::state_node_name) {
344 return boost::shared_ptr<TransportMaster>();
351 if (!node.get_property (X_("type"), type)) {
352 return boost::shared_ptr<TransportMaster>();
355 if (!node.get_property (X_("name"), name)) {
356 return boost::shared_ptr<TransportMaster>();
359 if (!node.get_property (X_("removeable"), removeable)) {
360 /* development versions of 6.0 didn't have this property for a
361 while. Any TM listed in XML at that time was non-removeable
366 DEBUG_TRACE (DEBUG::Slave, string_compose ("xml-construct %1 name %2 removeable %3\n", enum_2_string (type), name, removeable));
368 return factory (type, name, removeable);
371 boost::shared_ptr<TransportMaster>
372 TransportMaster::factory (SyncSource type, std::string const& name, bool removeable)
374 /* XXX need to count existing sources of a given type */
376 boost::shared_ptr<TransportMaster> tm;
378 DEBUG_TRACE (DEBUG::Slave, string_compose ("factory-construct %1 name %2 removeable %3\n", enum_2_string (type), name, removeable));
383 tm.reset (new MTC_TransportMaster (name));
386 tm.reset (new LTC_TransportMaster (name));
389 tm.reset (new MIDIClock_TransportMaster (name));
392 tm.reset (new Engine_TransportMaster (*AudioEngine::instance()));
398 error << string_compose (_("Construction of transport master object of type %1 failed"), enum_2_string (type)) << endmsg;
399 std::cerr << string_compose (_("Construction of transport master object of type %1 failed"), enum_2_string (type)) << std::endl;
400 return boost::shared_ptr<TransportMaster>();
404 tm->set_removeable (removeable);
410 /** @param sh Return a short version of the string */
412 TransportMaster::display_name (bool sh) const
417 /* no other backends offer sync for now ... deal with this if we
420 return S_("SyncSource|JACK");
424 if (name().length() <= 4) {
427 return S_("SyncSource|MTC");
434 if (name().length() <= 4) {
437 return S_("SyncSource|M-Clk");
444 if (name().length() <= 4) {
447 return S_("SyncSource|LTC");
452 /* GRRRR .... stupid, stupid gcc - you can't get here from there, all enum values are handled */
453 return S_("SyncSource|JACK");
457 TransportMaster::unregister_port ()
460 AudioEngine::instance()->unregister_port (_port);
465 boost::shared_ptr<Port>
466 TransportMasterViaMIDI::create_midi_port (std::string const & port_name)
468 boost::shared_ptr<Port> p;
470 if ((p = AudioEngine::instance()->register_input_port (DataType::MIDI, port_name, false, TransportMasterPort)) == 0) {
471 return boost::shared_ptr<Port> ();
474 _midi_port = boost::dynamic_pointer_cast<MidiPort> (p);
480 TransportMaster::allow_request (TransportRequestSource src, TransportRequestType type) const
482 return _request_mask & type;
486 TransportMaster::set_request_mask (TransportRequestType t)
488 if (_request_mask != t) {
490 PropertyChanged (Properties::allowed_transport_requests);
494 TimecodeTransportMaster::TimecodeTransportMaster (std::string const & name, SyncSource type)
495 : TransportMaster (type, name)
496 , _fr2997 (Properties::fr2997, false)
498 register_properties ();
502 TimecodeTransportMaster::register_properties ()
504 TransportMaster::register_properties ();
505 add_property (_fr2997);
509 TimecodeTransportMaster::set_fr2997 (bool yn)
513 PropertyChanged (Properties::fr2997);