1 /*
2 * Copyright (C) 2006-2012 David Robillard <d@drobilla.net>
3 * Copyright (C) 2008-2019 Paul Davis <paul@linuxaudiosystems.com>
4 * Copyright (C) 2009-2010 Carl Hetherington <carl@carlh.net>
5 * Copyright (C) 2012-2017 Robin Gareus <robin@gareus.org>
6 * Copyright (C) 2013-2018 John Emmas <john@creativepost.co.uk>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #include <iostream>
23 #include <errno.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27 #include "pbd/error.h"
28 #include "pbd/pthread_utils.h"
29
30 #include "ardour/audioengine.h"
31 #include "ardour/debug.h"
32 #include "ardour/midi_buffer.h"
33 #include "ardour/midi_port.h"
34 #include "ardour/session.h"
35 #include "ardour/transport_master.h"
36 #include "ardour/transport_master_manager.h"
37
38 #include "pbd/i18n.h"
39
40 using namespace std;
41 using namespace ARDOUR;
42 using namespace MIDI;
43 using namespace PBD;
44 using namespace Timecode;
45
46 /* length (in timecode frames) of the "window" that we consider legal given receipt of
47 a given timecode position. Ardour will try to chase within this window, and will
48 stop+locate+wait+chase if timecode arrives outside of it. The window extends entirely
49 in the current direction of motion, so if any timecode arrives that is before the most
50 recently received position (and without the direction of timecode reversing too), we
51 will stop+locate+wait+chase.
52 */
53 const int MTC_TransportMaster::sample_tolerance = 2;
54
MTC_TransportMaster(std::string const & name)55 MTC_TransportMaster::MTC_TransportMaster (std::string const & name)
56 : TimecodeTransportMaster (name, MTC)
57 , can_notify_on_unknown_rate (true)
58 , mtc_frame (0)
59 , mtc_frame_dll (0)
60 , last_inbound_frame (0)
61 , window_begin (0)
62 , window_end (0)
63 , first_mtc_timestamp (0)
64 , reset_pending (0)
65 , reset_position (false)
66 , transport_direction (1)
67 , busy_guard1 (0)
68 , busy_guard2 (0)
69 {
70 init ();
71 }
72
~MTC_TransportMaster()73 MTC_TransportMaster::~MTC_TransportMaster()
74 {
75 port_connections.drop_connections();
76 }
77
78 void
init()79 MTC_TransportMaster::init ()
80 {
81 reset (true);
82 resync_latency (false);
83 }
84
85 void
connection_handler(boost::weak_ptr<ARDOUR::Port> w0,std::string n0,boost::weak_ptr<ARDOUR::Port> w1,std::string n1,bool con)86 MTC_TransportMaster::connection_handler (boost::weak_ptr<ARDOUR::Port> w0, std::string n0, boost::weak_ptr<ARDOUR::Port> w1, std::string n1, bool con)
87 {
88 TransportMaster::connection_handler(w0, n0, w1, n1, con);
89
90 boost::shared_ptr<Port> p = w1.lock ();
91 if (p == _port) {
92 resync_latency (false);
93 }
94 }
95
96 void
create_port()97 MTC_TransportMaster::create_port ()
98 {
99 if ((_port = create_midi_port (string_compose ("%1 in", _name))) == 0) {
100 throw failed_constructor();
101 }
102 }
103
104 void
set_session(Session * s)105 MTC_TransportMaster::set_session (Session* s)
106 {
107 TransportMaster::set_session (s);
108 TransportMasterViaMIDI::set_session (s);
109
110 port_connections.drop_connections();
111
112 if (_session) {
113
114 last_mtc_fps_byte = _session->get_mtc_timecode_bits ();
115 quarter_frame_duration = (double) (_session->samples_per_timecode_frame() / 4.0);
116 mtc_timecode = _session->config.get_timecode_format();
117
118 parse_timecode_offset ();
119 reset (true);
120
121 parser.mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_TransportMaster::update_mtc_time, this, _1, _2, _3));
122 parser.mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_TransportMaster::update_mtc_qtr, this, _1, _2, _3));
123 parser.mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_TransportMaster::update_mtc_status, this, _1));
124 }
125 }
126
127 void
pre_process(MIDI::pframes_t nframes,samplepos_t now,boost::optional<samplepos_t> session_pos)128 MTC_TransportMaster::pre_process (MIDI::pframes_t nframes, samplepos_t now, boost::optional<samplepos_t> session_pos)
129 {
130 /* Read and parse incoming MIDI */
131
132 maybe_reset ();
133
134 if (!_midi_port) {
135 _current_delta = 0;
136 DEBUG_TRACE (DEBUG::MTC, "No MTC port registered");
137 return;
138 }
139
140 _midi_port->read_and_parse_entire_midi_buffer_with_no_speed_adjustment (nframes, parser, now);
141
142 if (session_pos) {
143 const samplepos_t current_pos = current.position + ((now - current.timestamp) * current.speed);
144 _current_delta = current_pos - *session_pos;
145 } else {
146 _current_delta = 0;
147 }
148 }
149
150 void
parse_timecode_offset()151 MTC_TransportMaster::parse_timecode_offset() {
152 Timecode::Time offset_tc;
153 Timecode::parse_timecode_format (_session->config.get_slave_timecode_offset(), offset_tc);
154 offset_tc.rate = _session->timecode_frames_per_second();
155 offset_tc.drop = _session->timecode_drop_frames();
156 _session->timecode_to_sample(offset_tc, timecode_offset, false, false);
157 timecode_negative_offset = offset_tc.negative;
158 }
159
160 void
parameter_changed(std::string const & p)161 MTC_TransportMaster::parameter_changed (std::string const & p)
162 {
163 if (p == "slave-timecode-offset"
164 || p == "timecode-format"
165 ) {
166 parse_timecode_offset();
167 }
168 }
169
170 ARDOUR::samplecnt_t
update_interval() const171 MTC_TransportMaster::update_interval() const
172 {
173 if (timecode.rate) {
174 return AudioEngine::instance()->sample_rate() / timecode.rate;
175 }
176
177 return AudioEngine::instance()->sample_rate(); /* useless but what other answer is there? */
178 }
179
180 ARDOUR::samplecnt_t
resolution() const181 MTC_TransportMaster::resolution () const
182 {
183 return (samplecnt_t) quarter_frame_duration * 4.0;
184 }
185
186 ARDOUR::samplecnt_t
seekahead_distance() const187 MTC_TransportMaster::seekahead_distance () const
188 {
189 return quarter_frame_duration * 8 * transport_direction;
190 }
191
192 bool
outside_window(samplepos_t pos) const193 MTC_TransportMaster::outside_window (samplepos_t pos) const
194 {
195 return ((pos < window_begin) || (pos > window_end));
196 }
197
198
199 bool
locked() const200 MTC_TransportMaster::locked () const
201 {
202 DEBUG_TRACE (DEBUG::MTC, string_compose ("locked ? %1 last %2\n", parser.mtc_locked(), last_inbound_frame));
203 return parser.mtc_locked() && last_inbound_frame !=0;
204 }
205
206 bool
ok() const207 MTC_TransportMaster::ok() const
208 {
209 return true;
210 }
211
212 void
queue_reset(bool reset_pos)213 MTC_TransportMaster::queue_reset (bool reset_pos)
214 {
215 Glib::Threads::Mutex::Lock lm (reset_lock);
216 reset_pending++;
217 if (reset_pos) {
218 reset_position = true;
219 }
220 }
221
222 void
maybe_reset()223 MTC_TransportMaster::maybe_reset ()
224 {
225 Glib::Threads::Mutex::Lock lm (reset_lock);
226
227 if (reset_pending) {
228 reset (reset_position);
229 reset_pending = 0;
230 reset_position = false;
231 }
232 }
233
234 void
reset(bool with_position)235 MTC_TransportMaster::reset (bool with_position)
236 {
237 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC_TransportMaster reset %1\n", with_position?"with position":"without position"));
238
239 if (with_position) {
240 current.update (current.position, 0, 0);
241 } else {
242 current.reset ();
243 }
244 first_mtc_timestamp = 0;
245 window_begin = 0;
246 window_end = 0;
247 transport_direction = 1;
248 _current_delta = 0;
249 timecode_format_valid = false;
250 }
251
252 void
handle_locate(const MIDI::byte * mmc_tc)253 MTC_TransportMaster::handle_locate (const MIDI::byte* mmc_tc)
254 {
255 MIDI::byte mtc[5];
256 DEBUG_TRACE (DEBUG::MTC, "MTC_TransportMaster::handle_locate\n");
257
258 mtc[4] = last_mtc_fps_byte;
259 mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
260 mtc[2] = mmc_tc[1];
261 mtc[1] = mmc_tc[2];
262 mtc[0] = mmc_tc[3];
263
264 update_mtc_time (mtc, true, 0);
265 }
266
267 void
init_mtc_dll(samplepos_t tme,double qtr)268 MTC_TransportMaster::init_mtc_dll(samplepos_t tme, double qtr)
269 {
270 const double omega = 2.0 * M_PI * qtr / 2.0 / double(_session->sample_rate());
271 b = 1.4142135623730950488 * omega;
272 c = omega * omega;
273
274 e2 = qtr;
275 t0 = double(tme);
276 t1 = t0 + e2;
277 DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2));
278 }
279
280 /* called from MIDI parser */
281 void
update_mtc_qtr(Parser & p,int which_qtr,samplepos_t now)282 MTC_TransportMaster::update_mtc_qtr (Parser& p, int which_qtr, samplepos_t now)
283 {
284 busy_guard1++;
285 const double qtr_d = quarter_frame_duration;
286
287 mtc_frame_dll += qtr_d * (double) transport_direction;
288 mtc_frame = rint(mtc_frame_dll);
289
290 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr sample %1 at %2 -> mtc_frame: %3\n", which_qtr, now, mtc_frame));
291
292 double mtc_speed = 0;
293 if (first_mtc_timestamp != 0) {
294 /* update MTC DLL and calculate speed */
295 const double e = mtc_frame_dll - (double)transport_direction * ((double)now - (double)current.timestamp + t0);
296 t0 = t1;
297 t1 += b * e + e2;
298 e2 += c * e;
299
300 mtc_speed = (t1 - t0) / qtr_d;
301 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr sample DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", t0, t1, e, mtc_speed, e2 - qtr_d));
302
303 current.update (mtc_frame, now, mtc_speed);
304
305 last_inbound_frame = now;
306 }
307
308 maybe_reset ();
309
310 busy_guard2++;
311 }
312
313 /* called from MIDI parser _after_ update_mtc_qtr()
314 * when a full TC has been received
315 * OR on locate */
316 void
update_mtc_time(const MIDI::byte * msg,bool was_full,samplepos_t now)317 MTC_TransportMaster::update_mtc_time (const MIDI::byte *msg, bool was_full, samplepos_t now)
318 {
319 busy_guard1++;
320
321 /* "now" can be zero if this is called from a context where we do not have or do not want
322 to use a timestamp indicating when this MTC time was received. example: when we received
323 a locate command via MMC.
324 */
325 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", pthread_name()));
326 TimecodeFormat tc_format;
327 bool have_tc = true;
328
329 timecode.hours = msg[3];
330 timecode.minutes = msg[2];
331 timecode.seconds = msg[1];
332 timecode.frames = msg[0];
333
334 last_mtc_fps_byte = msg[4];
335
336 DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
337
338 if (now) {
339 maybe_reset ();
340 }
341
342 switch (msg[4]) {
343 case MTC_24_FPS:
344 timecode.rate = 24;
345 timecode.drop = false;
346 tc_format = timecode_24;
347 can_notify_on_unknown_rate = true;
348 break;
349 case MTC_25_FPS:
350 timecode.rate = 25;
351 timecode.drop = false;
352 tc_format = timecode_25;
353 can_notify_on_unknown_rate = true;
354 break;
355 case MTC_30_FPS_DROP:
356 if (fr2997()) {
357 tc_format = Timecode::timecode_2997000drop;
358 timecode.rate = (29970.0/1000.0);
359 } else {
360 tc_format = timecode_2997drop;
361 timecode.rate = (30000.0/1001.0);
362 }
363 timecode.drop = true;
364 can_notify_on_unknown_rate = true;
365 break;
366 case MTC_30_FPS:
367 timecode.rate = 30;
368 timecode.drop = false;
369 can_notify_on_unknown_rate = true;
370 tc_format = timecode_30;
371 break;
372 default:
373 /* throttle error messages about unknown MTC rates */
374 if (can_notify_on_unknown_rate) {
375 error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
376 (int) msg[4])
377 << endmsg;
378 can_notify_on_unknown_rate = false;
379 }
380 timecode.rate = _session->timecode_frames_per_second();
381 timecode.drop = _session->timecode_drop_frames();
382 have_tc = false;
383 }
384
385 if (have_tc) {
386 mtc_timecode = tc_format;
387 timecode_format_valid = true; /* SET FLAG */
388 }
389
390 /* do a careful conversion of the timecode value to a position
391 so that we take drop/nondrop and all that nonsense into
392 consideration.
393 */
394
395 quarter_frame_duration = (double(_session->sample_rate()) / (double) timecode.rate / 4.0);
396
397 Timecode::timecode_to_sample (timecode, mtc_frame, true, false,
398 double(_session->sample_rate()),
399 _session->config.get_subframes_per_frame(),
400 timecode_negative_offset, timecode_offset
401 );
402
403 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4)\n", now, timecode, mtc_frame, was_full));
404
405 if (was_full || outside_window (mtc_frame)) {
406 DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC %1 or outside window %2 MTC %3\n", was_full, outside_window (mtc_frame), mtc_frame));
407 boost::shared_ptr<TransportMaster> c = TransportMasterManager::instance().current();
408 if (c && c.get() == this && _session->config.get_external_sync()) {
409 _session->set_requested_return_sample (-1);
410 _session->request_locate (mtc_frame, MustStop, TRS_MTC);
411 }
412 update_mtc_status (MIDI::MTC_Stopped);
413 reset (false);
414 reset_window (mtc_frame);
415 } else {
416
417 /* we've had the first set of 8 qtr sample messages, determine position
418 and allow continuing qtr sample messages to provide position
419 and speed information.
420 */
421
422 /* We received the last quarter frame 7 quarter frames (1.75 mtc
423 samples) after the instance when the contents of the mtc quarter
424 samples were decided. Add time to compensate for the elapsed 1.75
425 samples.
426 */
427 double qtr = quarter_frame_duration;
428 long int mtc_off = (long) rint(7.0 * qtr);
429
430 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame: %1 | MTC-FpT: %2 A3-FpT:%3\n",
431 mtc_frame, (4.0*qtr), _session->samples_per_timecode_frame()));
432
433 switch (parser.mtc_running()) {
434 case MTC_Backward:
435 mtc_frame -= mtc_off;
436 qtr *= -1.0;
437 break;
438 case MTC_Forward:
439 mtc_frame += mtc_off;
440 break;
441 default:
442 break;
443 }
444
445 DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame (w/offset) = %1\n", mtc_frame));
446
447 if (now) {
448 if (first_mtc_timestamp == 0 || current.timestamp == 0) {
449 first_mtc_timestamp = now;
450 init_mtc_dll(mtc_frame, qtr);
451 mtc_frame_dll = mtc_frame + midi_port_latency.max;
452 }
453 current.update (mtc_frame + midi_port_latency.max, now, current.speed);
454 reset_window (mtc_frame);
455 }
456 }
457
458 busy_guard2++;
459 }
460
461 void
update_mtc_status(MIDI::MTC_Status status)462 MTC_TransportMaster::update_mtc_status (MIDI::MTC_Status status)
463 {
464 /* XXX !!! thread safety ... called from MIDI I/O context
465 * on locate (via ::update_mtc_time())
466 */
467 DEBUG_TRACE (DEBUG::MTC, string_compose("MTC_TransportMaster::update_mtc_status - TID:%1 MTC:%2\n", pthread_name(), mtc_frame));
468 return; // why was this fn needed anyway ? it just messes up things -> use reset.
469 busy_guard1++;
470
471 switch (status) {
472 case MTC_Stopped:
473 current.update (mtc_frame, 0, 0);
474 break;
475
476 case MTC_Forward:
477 current.update (mtc_frame, 0, 0);
478 break;
479
480 case MTC_Backward:
481 current.update (mtc_frame, 0, 0);
482 break;
483 }
484 busy_guard2++;
485 }
486
487 void
reset_window(samplepos_t root)488 MTC_TransportMaster::reset_window (samplepos_t root)
489 {
490 /* if we're waiting for the master to catch us after seeking ahead, keep the window
491 of acceptable MTC samples wide open. otherwise, shrink it down to just 2 video frames
492 ahead of the window root (taking direction into account).
493 */
494
495 samplecnt_t const d = (quarter_frame_duration * 4 * sample_tolerance);
496
497 switch (parser.mtc_running()) {
498 case MTC_Forward:
499 window_begin = root;
500 transport_direction = 1;
501 window_end = root + d;
502 break;
503
504 case MTC_Backward:
505 transport_direction = -1;
506 if (root > d) {
507 window_begin = root - d;
508 window_end = root;
509 } else {
510 window_begin = 0;
511 }
512 window_end = root;
513 break;
514
515 default:
516 /* do nothing */
517 break;
518 }
519
520 DEBUG_TRACE (DEBUG::MTC, string_compose ("reset MTC window @ %3, now %1 .. %2\n", window_begin, window_end, root));
521 }
522
523 Timecode::TimecodeFormat
apparent_timecode_format() const524 MTC_TransportMaster::apparent_timecode_format () const
525 {
526 return mtc_timecode;
527 }
528
529 std::string
position_string() const530 MTC_TransportMaster::position_string() const
531 {
532 SafeTime last;
533 current.safe_read (last);
534 if (last.timestamp == 0 || reset_pending) {
535 return " --:--:--:--";
536 }
537 return Timecode::timecode_format_sampletime(
538 last.position,
539 double(_session->sample_rate()),
540 Timecode::timecode_to_frames_per_second(mtc_timecode),
541 Timecode::timecode_has_drop_frames(mtc_timecode));
542 }
543
544 std::string
delta_string() const545 MTC_TransportMaster::delta_string () const
546 {
547 SafeTime last;
548 current.safe_read (last);
549
550 if (last.timestamp == 0 || reset_pending) {
551 return X_("\u2012\u2012\u2012\u2012");
552 } else {
553 return format_delta_time (_current_delta);
554 }
555 }
556
557 void
unregister_port()558 MTC_TransportMaster::unregister_port ()
559 {
560 _midi_port.reset ();
561 TransportMaster::unregister_port ();
562 }
563