2 Copyright (C) 2002-4 Paul Davis
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.
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.
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.
22 #include <sys/types.h>
25 #include "pbd/error.h"
27 #include "midi++/port.h"
28 #include "ardour/debug.h"
29 #include "ardour/slave.h"
30 #include "ardour/session.h"
31 #include "ardour/audioengine.h"
32 #include "ardour/pi_controller.h"
37 using namespace ARDOUR;
41 /* length (in timecode frames) of the "window" that we consider legal given receipt of
42 a given timecode position. Ardour will try to chase within this window, and will
43 stop+locate+wait+chase if timecode arrives outside of it. The window extends entirely
44 in the current direction of motion, so if any timecode arrives that is before the most
45 recently received position (and without the direction of timecode reversing too), we
46 will stop+locate+wait+chase.
49 const int MTC_Slave::frame_tolerance = 2;
51 MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
54 can_notify_on_unknown_rate = true;
55 did_reset_tc_format = false;
57 reset_position = false;
61 last_mtc_fps_byte = session.get_mtc_timecode_bits ();
64 speed_accumulator_size = 16;
65 speed_accumulator = new double[speed_accumulator_size];
71 MTC_Slave::~MTC_Slave()
73 port_connections.drop_connections ();
75 if (did_reset_tc_format) {
76 session.config.set_timecode_format (saved_tc_format);
80 delete [] speed_accumulator;
84 MTC_Slave::give_slave_full_control_over_transport_speed() const
86 return true; // for PiC control */
87 // return false; // for Session-level computed varispeed
91 MTC_Slave::rebind (MIDI::Port& p)
93 port_connections.drop_connections ();
97 port->parser()->mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_time, this, _1, _2, _3));
98 port->parser()->mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_qtr, this, _1, _2, _3));
99 port->parser()->mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_status, this, _1));
103 MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now)
105 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr frame %1 at %2\n", which_qtr, now));
107 last_inbound_frame = now;
111 MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now)
113 /* "now" can be zero if this is called from a context where we do not have or do not want
114 to use a timestamp indicating when this MTC time was received. example: when we received
115 a locate command via MMC.
122 Timecode::Time timecode;
123 TimecodeFormat tc_format;
124 bool reset_tc = true;
125 framepos_t window_root = -1;
127 DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
129 timecode.hours = msg[3];
130 timecode.minutes = msg[2];
131 timecode.seconds = msg[1];
132 timecode.frames = msg[0];
134 last_mtc_fps_byte = msg[4];
139 timecode.drop = false;
140 tc_format = timecode_24;
141 can_notify_on_unknown_rate = true;
145 timecode.drop = false;
146 tc_format = timecode_25;
147 can_notify_on_unknown_rate = true;
149 case MTC_30_FPS_DROP:
151 timecode.drop = true;
152 tc_format = timecode_30drop;
153 can_notify_on_unknown_rate = true;
157 timecode.drop = false;
158 can_notify_on_unknown_rate = true;
159 tc_format = timecode_30;
162 /* throttle error messages about unknown MTC rates */
163 if (can_notify_on_unknown_rate) {
164 error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
167 can_notify_on_unknown_rate = false;
169 timecode.rate = session.timecode_frames_per_second();
170 timecode.drop = session.timecode_drop_frames();
175 if (!did_reset_tc_format) {
176 saved_tc_format = session.config.get_timecode_format();
177 did_reset_tc_format = true;
179 session.config.set_timecode_format (tc_format);
182 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC time timestamp = %1 TC %2 = frame %3 (from full message ? %4)\n",
183 now, timecode, mtc_frame, was_full));
185 if (was_full || outside_window (mtc_frame)) {
187 session.timecode_to_sample (timecode, mtc_frame, true, false);
188 session.request_locate (mtc_frame, false);
189 session.request_transport_speed (0);
190 update_mtc_status (MIDI::MTC_Stopped);
192 reset_window (mtc_frame);
196 /* we've had the first set of 8 qtr frame messages, determine position
197 and allow continuing qtr frame messages to provide position
198 and speed information.
201 /* do a careful conversion of the timecode value to a position
202 so that we take drop/nondrop and all that nonsense into
206 session.timecode_to_sample (timecode, mtc_frame, true, false);
208 /* We received the last quarter frame 7 quarter frames (1.75 mtc
209 frames) after the instance when the contents of the mtc quarter
210 frames were decided. Add time to compensate for the elapsed 1.75
211 frames. Also compensate for audio latency.
214 mtc_frame += (long) (1.75 * session.frames_per_timecode_frame()) + session.worst_playback_latency();
219 if (last_mtc_timestamp == 0) {
221 last_mtc_timestamp = now;
222 last_mtc_frame = mtc_frame;
226 if (give_slave_full_control_over_transport_speed()) {
229 * its not the average, but we will assign it to current.speed below
232 static framepos_t last_seen_timestamp = 0;
233 static framepos_t last_seen_position = 0;
235 if ((now - last_seen_timestamp) < 300) {
236 mtc_frame = (mtc_frame + last_seen_position)/2;
239 last_seen_timestamp = now;
240 last_seen_position = mtc_frame;
249 framepos_t time_delta = (now - last_mtc_timestamp);
251 if (time_delta != 0) {
252 double apparent_speed = (mtc_frame - last_mtc_frame) / (double) (time_delta);
254 process_apparent_speed (apparent_speed);
255 DEBUG_TRACE (DEBUG::Slave, string_compose ("apparent speed was %1 average is now %2\n", apparent_speed, average_speed));
257 DEBUG_TRACE (DEBUG::Slave, string_compose ("no apparent calc, average is %1\n", average_speed));
260 /* every second, recalibrate the starting point for the speed measurement */
261 if (mtc_frame - last_mtc_frame > session.frame_rate()) {
262 last_mtc_timestamp = now;
263 last_mtc_frame = mtc_frame;
269 current.position = mtc_frame;
270 current.timestamp = now;
271 current.speed = average_speed;
273 window_root = mtc_frame;
278 last_inbound_frame = now;
281 if (window_root >= 0) {
282 reset_window (window_root);
287 MTC_Slave::process_apparent_speed (double this_speed)
289 DEBUG_TRACE (DEBUG::MTC, string_compose ("speed cnt %1 sz %2 have %3\n", speed_accumulator_cnt, speed_accumulator_size, have_first_speed_accumulator));
291 /* clamp to an expected range */
293 if (this_speed > 4.0 || this_speed < -4.0) {
294 this_speed = average_speed;
297 if (speed_accumulator_cnt >= speed_accumulator_size) {
298 have_first_speed_accumulator = true;
299 speed_accumulator_cnt = 0;
302 speed_accumulator[speed_accumulator_cnt++] = this_speed;
304 if (have_first_speed_accumulator) {
306 for (size_t i = 0; i < speed_accumulator_size; ++i) {
307 average_speed += speed_accumulator[i];
309 average_speed /= speed_accumulator_size;
314 MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
318 mtc[4] = last_mtc_fps_byte;
319 mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
324 update_mtc_time (mtc, true, 0);
328 MTC_Slave::update_mtc_status (MIDI::MTC_Status status)
330 /* XXX !!! thread safety ... called from MIDI I/O context
331 and process() context (via ::speed_and_position())
337 current.position = mtc_frame;
338 current.timestamp = 0;
346 current.position = mtc_frame;
347 current.timestamp = 0;
354 current.position = mtc_frame;
355 current.timestamp = 0;
364 MTC_Slave::read_current (SafeTime *st) const
370 error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
377 } while (st->guard1 != st->guard2);
381 MTC_Slave::locked () const
383 return port->parser()->mtc_locked();
387 MTC_Slave::ok() const
393 MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
395 framepos_t now = session.engine().frame_time();
398 bool in_control = false;
400 read_current (&last);
402 if (last.timestamp == 0) {
405 DEBUG_TRACE (DEBUG::MTC, string_compose ("first call to MTC_Slave::speed_and_position, pos = %1\n", last.position));
409 /* no timecode for 1/4 second ? conclude that its stopped */
411 if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > session.frame_rate() / 4) {
414 session.request_locate (pos, false);
415 session.request_transport_speed (0);
417 DEBUG_TRACE (DEBUG::MTC, "MTC not seen for 1/4 second - reset pending\n");
421 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position %1 %2\n", last.speed, last.position));
423 if (give_slave_full_control_over_transport_speed()) {
424 in_control = (session.slave_state() == Session::Running);
425 framepos_t pic_want_locate = 0;
426 //framepos_t slave_pos = session.audible_frame();
427 framepos_t slave_pos = session.transport_frame();
428 static double average_speed = 0;
430 framepos_t ref_now = session.engine().frame_time_at_cycle_start();
431 average_speed = pic->get_ratio (last.timestamp, last.position, ref_now, slave_pos, in_control, session.engine().frames_per_cycle());
433 pic_want_locate = pic->want_locate();
435 if (in_control && pic_want_locate) {
436 last.speed = average_speed + (double) (pic_want_locate - session.transport_frame()) / (double)session.get_block_size();
437 std::cout << "locate req " << pic_want_locate << " speed: " << average_speed << "\n";
439 last.speed = average_speed;
443 if (last.speed == 0.0f) {
449 /* scale elapsed time by the current MTC speed */
451 if (last.timestamp && (now > last.timestamp)) {
452 elapsed = (framecnt_t) floor (last.speed * (now - last.timestamp));
453 DEBUG_TRACE (DEBUG::MTC, string_compose ("last timecode received @ %1, now = %2, elapsed frames = %3 w/speed= %4\n",
454 last.timestamp, now, elapsed, last.speed));
456 elapsed = 0; /* XXX is this right? */
460 /* now add the most recent timecode value plus the estimated elapsed interval */
463 pos = session.transport_frame();
465 pos = last.position + elapsed;
470 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position FINAL %1 %2\n", last.speed, pos));
473 DEBUG_TRACE (DEBUG::MTC, string_compose ("last = %1 elapsed = %2 pos = %3 speed = %4\n", last.position, elapsed, pos, speed));
479 MTC_Slave::resolution () const
481 return (framecnt_t) session.frames_per_timecode_frame();
485 MTC_Slave::queue_reset (bool reset_pos)
487 Glib::Threads::Mutex::Lock lm (reset_lock);
490 reset_position = true;
495 MTC_Slave::maybe_reset ()
497 Glib::Threads::Mutex::Lock lm (reset_lock);
500 reset (reset_position);
502 reset_position = false;
507 MTC_Slave::reset (bool with_position)
510 last_inbound_frame = 0;
512 current.position = 0;
513 current.timestamp = 0;
517 last_inbound_frame = 0;
519 current.timestamp = 0;
527 last_mtc_timestamp = 0;
530 have_first_speed_accumulator = false;
531 speed_accumulator_cnt = 0;
537 MTC_Slave::reset_window (framepos_t root)
540 /* if we're waiting for the master to catch us after seeking ahead, keep the window
541 of acceptable MTC frames wide open. otherwise, shrink it down to just 2 video frames
542 ahead of the window root (taking direction into account).
545 switch (port->parser()->mtc_running()) {
548 if (session.slave_state() == Session::Running) {
549 window_end = root + (session.frames_per_timecode_frame() * frame_tolerance);
551 window_end = root + seekahead_distance ();
556 if (session.slave_state() == Session::Running) {
557 framecnt_t const d = session.frames_per_timecode_frame() * frame_tolerance;
559 window_begin = root - d;
565 framecnt_t const d = seekahead_distance ();
567 window_begin = root - d;
580 DEBUG_TRACE (DEBUG::MTC, string_compose ("legal MTC window now %1 .. %2\n", window_begin, window_end));
584 MTC_Slave::seekahead_distance () const
587 return session.frame_rate();
591 MTC_Slave::outside_window (framepos_t pos) const
593 return ((pos < window_begin) || (pos > window_end));