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