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