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