Update libardour GPL boilerplate and (C) from git log
[ardour.git] / libs / ardour / transport_master.cc
1 /*
2  * Copyright (C) 2018-2019 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2018 Robin Gareus <robin@gareus.org>
4  *
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.
9  *
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.
14  *
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.
18  */
19
20 #include <vector>
21
22 #include "pbd/boost_debug.h"
23 #include "pbd/debug.h"
24 #include "pbd/i18n.h"
25
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"
34
35
36 namespace ARDOUR {
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;
43         }
44 }
45
46 using namespace ARDOUR;
47 using namespace PBD;
48
49 void
50 TransportMaster::make_property_quarks ()
51 {
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));
62 }
63
64 const std::string TransportMaster::state_node_name = X_("TransportMaster");
65
66 TransportMaster::TransportMaster (SyncSource t, std::string const & name)
67         : _type (t)
68         , _name (Properties::name, name)
69         , _session (0)
70         , _current_delta (0)
71         , _pending_collect (true)
72         , _removeable (false)
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)
77 {
78         register_properties ();
79
80         ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect_same_thread (port_connection, boost::bind (&TransportMaster::connection_handler, this, _1, _2, _3, _4, _5));
81         ARDOUR::AudioEngine::instance()->Running.connect_same_thread (backend_connection, boost::bind (&TransportMaster::check_backend, this));
82 }
83
84 TransportMaster::~TransportMaster()
85 {
86         DEBUG_TRACE (DEBUG::Destruction, string_compose ("destroying transport master \"%1\" along with port %2\n", name(), (_port ? _port->name() : std::string ("no port"))));
87
88         unregister_port ();
89 }
90
91 bool
92 TransportMaster::speed_and_position (double& speed, samplepos_t& pos, samplepos_t& lp, samplepos_t& when, samplepos_t now)
93 {
94         if (!_collect) {
95                 return false;
96         }
97
98         if (!locked()) {
99                 DEBUG_TRACE (DEBUG::Slave, string_compose ("%1: not locked, no speed and position!\n", name()));
100                 return false;
101         }
102
103         SafeTime last;
104         current.safe_read (last);
105
106         if (last.timestamp == 0) {
107                 return false;
108         }
109
110         if (last.timestamp && now > last.timestamp && now - last.timestamp > (2.0 * update_interval())) {
111                 /* no timecode for two cycles - conclude that it's stopped */
112
113                 if (!Config->get_transport_masters_just_roll_when_sync_lost()) {
114                         speed = 0;
115                         pos = last.position;
116                         lp = last.position;
117                         when = last.timestamp;
118                         _current_delta = 0;
119                         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));
120                         return false;
121                 }
122         }
123
124         lp = last.position;
125         when = last.timestamp;
126         speed = last.speed;
127
128         /* provide a .1% deadzone to lock the speed */
129         if (fabs (speed - 1.0) <= 0.001) {
130                 speed = 1.0;
131         }
132
133         pos = last.position + (now - last.timestamp) * speed;
134
135         DEBUG_TRACE (DEBUG::Slave, string_compose ("%1 sync spd: %2 pos: %3 | last-pos: %4 @  %7| elapsed: %5 | speed: %6\n",
136                                                    name(), speed, pos, last.position, (now - last.timestamp), speed, when));
137
138         return true;
139 }
140
141 void
142 TransportMaster::register_properties ()
143 {
144         _xml_node_name = state_node_name;
145
146         add_property (_name);
147         add_property (_collect);
148         add_property (_sclock_synced);
149         add_property (_request_mask);
150
151         /* we omit _connected since it is derived from port state, and merely
152          * used for signalling
153          */
154 }
155
156 void
157 TransportMaster::set_name (std::string const & str)
158 {
159         if (_name != str) {
160                 _name = str;
161                 PropertyChanged (Properties::name);
162         }
163 }
164
165 bool
166 TransportMaster::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
167 {
168         if (!_port) {
169                 return false;
170         }
171
172         const std::string fqn = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->name());
173
174         if (fqn == name1 || fqn == name2) {
175
176                 /* it's about us */
177
178                 /* XXX technically .. if the user makes an N->1 connection to
179                  * this transport master's port, this simple minded logic is
180                  * not sufficient. But the user shouldn't do that ...
181                  */
182
183                 if (yn) {
184                         _connected = true;
185                 } else {
186                         _connected = false;
187                 }
188
189                 PropertyChanged (Properties::connected);
190
191                 return true;
192         }
193
194         return false;
195 }
196
197 bool
198 TransportMaster::check_collect()
199 {
200         if (!_connected) {
201                 return false;
202         }
203
204         /* XXX should probably use boost::atomic something or other here */
205
206         if (_pending_collect != _collect) {
207                 if (_pending_collect) {
208                         init ();
209                 } else {
210                         if (TransportMasterManager::instance().current().get() == this) {
211                                 if (_session) {
212                                         _session->config.set_external_sync (false);
213                                 }
214                         }
215                 }
216                 _collect = _pending_collect;
217                 PropertyChanged (Properties::collect);
218         }
219
220         return _collect;
221 }
222
223 void
224 TransportMaster::set_collect (bool yn)
225 {
226         /* theoretical race condition */
227
228         if (_connected) {
229                 _pending_collect = yn;
230         } else {
231                 if (_collect != yn) {
232                         _pending_collect = _collect = yn;
233                         PropertyChanged (Properties::collect);
234                 }
235         }
236 }
237
238 void
239 TransportMaster::set_sample_clock_synced (bool yn)
240 {
241         if (yn != _sclock_synced) {
242                 _sclock_synced = yn;
243                 PropertyChanged (Properties::sclock_synced);
244         }
245 }
246
247 void
248 TransportMaster::set_session (Session* s)
249 {
250         _session = s;
251 }
252
253 int
254 TransportMaster::set_state (XMLNode const & node, int /* version */)
255 {
256         PropertyChange what_changed;
257
258         what_changed = set_values (node);
259
260         XMLNode* pnode = node.child (X_("Port"));
261
262         if (pnode) {
263                 XMLNodeList const & children = pnode->children();
264                 for (XMLNodeList::const_iterator ci = children.begin(); ci != children.end(); ++ci) {
265
266                         XMLProperty const *prop;
267
268                         if ((*ci)->name() == X_("Connection")) {
269                                 if ((prop = (*ci)->property (X_("other"))) == 0) {
270                                         continue;
271                                 }
272                                 _port->connect (prop->value());
273                         }
274                 }
275         }
276
277         PropertyChanged (what_changed);
278
279         return 0;
280 }
281
282 XMLNode&
283 TransportMaster::get_state ()
284 {
285         XMLNode* node = new XMLNode (state_node_name);
286         node->set_property (X_("type"), _type);
287         node->set_property (X_("removeable"), _removeable);
288
289         add_properties (*node);
290
291         if (_port) {
292                 std::vector<std::string> connections;
293
294                 XMLNode* pnode = new XMLNode (X_("Port"));
295
296                 if (_port->get_connections (connections)) {
297
298                         std::vector<std::string>::const_iterator ci;
299                         std::sort (connections.begin(), connections.end());
300
301                         for (ci = connections.begin(); ci != connections.end(); ++ci) {
302
303                                 /* if its a connection to our own port,
304                                    return only the port name, not the
305                                    whole thing. this allows connections
306                                    to be re-established even when our
307                                    client name is different.
308                                 */
309
310                                 XMLNode* cnode = new XMLNode (X_("Connection"));
311
312                                 cnode->set_property (X_("other"), AudioEngine::instance()->make_port_name_relative (*ci));
313                                 pnode->add_child_nocopy (*cnode);
314                         }
315                 }
316
317                 node->add_child_nocopy (*pnode);
318         }
319
320         return *node;
321 }
322
323 boost::shared_ptr<TransportMaster>
324 TransportMaster::factory (XMLNode const & node)
325 {
326         if (node.name() != TransportMaster::state_node_name) {
327                 return boost::shared_ptr<TransportMaster>();
328         }
329
330         SyncSource type;
331         std::string name;
332         bool removeable;
333
334         if (!node.get_property (X_("type"), type)) {
335                 return boost::shared_ptr<TransportMaster>();
336         }
337
338         if (!node.get_property (X_("name"), name)) {
339                 return boost::shared_ptr<TransportMaster>();
340         }
341
342         if (!node.get_property (X_("removeable"), removeable)) {
343                 /* development versions of 6.0 didn't have this property for a
344                    while. Any TM listed in XML at that time was non-removeable
345                 */
346                 removeable = false;
347         }
348
349         DEBUG_TRACE (DEBUG::Slave, string_compose ("xml-construct %1 name %2 removeable %3\n", enum_2_string (type), name, removeable));
350
351         return factory (type, name, removeable);
352 }
353
354 boost::shared_ptr<TransportMaster>
355 TransportMaster::factory (SyncSource type, std::string const& name, bool removeable)
356 {
357         /* XXX need to count existing sources of a given type */
358
359         boost::shared_ptr<TransportMaster> tm;
360
361         DEBUG_TRACE (DEBUG::Slave, string_compose ("factory-construct %1 name %2 removeable %3\n", enum_2_string (type), name, removeable));
362
363         switch (type) {
364         case MTC:
365                 tm.reset (new MTC_TransportMaster (name));
366                 break;
367         case LTC:
368                 tm.reset (new LTC_TransportMaster (name));
369                 break;
370         case MIDIClock:
371                 tm.reset (new MIDIClock_TransportMaster (name));
372                 break;
373         case Engine:
374                 tm.reset (new Engine_TransportMaster (*AudioEngine::instance()));
375                 break;
376         default:
377                 break;
378         }
379
380         if (tm) {
381                 tm->set_removeable (removeable);
382         }
383
384         return tm;
385 }
386
387 /** @param sh Return a short version of the string */
388 std::string
389 TransportMaster::display_name (bool sh) const
390 {
391
392         switch (_type) {
393         case Engine:
394                 /* no other backends offer sync for now ... deal with this if we
395                  * ever have to.
396                  */
397                 return S_("SyncSource|JACK");
398
399         case MTC:
400                 if (sh) {
401                         if (name().length() <= 4) {
402                                 return name();
403                         }
404                         return S_("SyncSource|MTC");
405                 } else {
406                         return name();
407                 }
408
409         case MIDIClock:
410                 if (sh) {
411                         if (name().length() <= 4) {
412                                 return name();
413                         }
414                         return S_("SyncSource|M-Clk");
415                 } else {
416                         return name();
417                 }
418
419         case LTC:
420                 if (sh) {
421                         if (name().length() <= 4) {
422                                 return name();
423                         }
424                         return S_("SyncSource|LTC");
425                 } else {
426                         return name();
427                 }
428         }
429         /* GRRRR .... stupid, stupid gcc - you can't get here from there, all enum values are handled */
430         return S_("SyncSource|JACK");
431 }
432
433 void
434 TransportMaster::unregister_port ()
435 {
436         if (_port) {
437                 AudioEngine::instance()->unregister_port (_port);
438                 _port.reset ();
439         }
440 }
441
442 boost::shared_ptr<Port>
443 TransportMasterViaMIDI::create_midi_port (std::string const & port_name)
444 {
445         boost::shared_ptr<Port> p;
446
447         if ((p = AudioEngine::instance()->register_input_port (DataType::MIDI, port_name)) == 0) {
448                 return boost::shared_ptr<Port> ();
449         }
450
451         _midi_port = boost::dynamic_pointer_cast<MidiPort> (p);
452
453         return p;
454 }
455
456 bool
457 TransportMaster::allow_request (TransportRequestSource src, TransportRequestType type) const
458 {
459         return _request_mask & type;
460 }
461
462 void
463 TransportMaster::set_request_mask (TransportRequestType t)
464 {
465         if (_request_mask != t) {
466                 _request_mask = t;
467                 PropertyChanged (Properties::allowed_transport_requests);
468         }
469 }
470
471 TimecodeTransportMaster::TimecodeTransportMaster (std::string const & name, SyncSource type)
472         : TransportMaster (type, name)
473         , _fr2997 (Properties::fr2997, false)
474 {
475         register_properties ();
476 }
477
478 void
479 TimecodeTransportMaster::register_properties ()
480 {
481         TransportMaster::register_properties ();
482         add_property (_fr2997);
483 }
484
485 void
486 TimecodeTransportMaster::set_fr2997 (bool yn)
487 {
488         if (yn != _fr2997) {
489                 _fr2997 = yn;
490                 PropertyChanged (Properties::fr2997);
491         }
492 }
493