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 if (did_reset_tc_format) {
74 session.config.set_timecode_format (saved_tc_format);
78 delete [] speed_accumulator;
82 MTC_Slave::give_slave_full_control_over_transport_speed() const
84 return true; // for PiC control */
85 // return false; // for Session-level computed varispeed
89 MTC_Slave::rebind (MIDI::Port& p)
91 port_connections.drop_connections ();
95 port->parser()->mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_time, this, _1, _2, _3));
96 port->parser()->mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_qtr, this, _1, _2, _3));
97 port->parser()->mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_status, this, _1));
101 MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now)
103 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr frame %1 at %2\n", which_qtr, now));
105 last_inbound_frame = now;
109 MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now)
111 /* "now" can be zero if this is called from a context where we do not have or do not want
112 to use a timestamp indicating when this MTC time was received. example: when we received
113 a locate command via MMC.
120 Timecode::Time timecode;
121 TimecodeFormat tc_format;
122 bool reset_tc = true;
123 framepos_t window_root = -1;
125 DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
127 timecode.hours = msg[3];
128 timecode.minutes = msg[2];
129 timecode.seconds = msg[1];
130 timecode.frames = msg[0];
132 last_mtc_fps_byte = msg[4];
137 timecode.drop = false;
138 tc_format = timecode_24;
139 can_notify_on_unknown_rate = true;
143 timecode.drop = false;
144 tc_format = timecode_25;
145 can_notify_on_unknown_rate = true;
147 case MTC_30_FPS_DROP:
149 timecode.drop = true;
150 tc_format = timecode_30drop;
151 can_notify_on_unknown_rate = true;
155 timecode.drop = false;
156 can_notify_on_unknown_rate = true;
157 tc_format = timecode_30;
160 /* throttle error messages about unknown MTC rates */
161 if (can_notify_on_unknown_rate) {
162 error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
165 can_notify_on_unknown_rate = false;
167 timecode.rate = session.timecode_frames_per_second();
168 timecode.drop = session.timecode_drop_frames();
173 if (!did_reset_tc_format) {
174 saved_tc_format = session.config.get_timecode_format();
175 did_reset_tc_format = true;
177 session.config.set_timecode_format (tc_format);
180 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC time timestamp = %1 TC %2 = frame %3 (from full message ? %4)\n",
181 now, timecode, mtc_frame, was_full));
183 if (was_full || outside_window (mtc_frame)) {
185 session.timecode_to_sample (timecode, mtc_frame, true, false);
186 session.request_locate (mtc_frame, false);
187 session.request_transport_speed (0);
188 update_mtc_status (MIDI::MTC_Stopped);
190 reset_window (mtc_frame);
194 /* we've had the first set of 8 qtr frame messages, determine position
195 and allow continuing qtr frame messages to provide position
196 and speed information.
199 /* do a careful conversion of the timecode value to a position
200 so that we take drop/nondrop and all that nonsense into
204 session.timecode_to_sample (timecode, mtc_frame, true, false);
206 /* We received the last quarter frame 7 quarter frames (1.75 mtc
207 frames) after the instance when the contents of the mtc quarter
208 frames were decided. Add time to compensate for the elapsed 1.75
209 frames. Also compensate for audio latency.
212 mtc_frame += (long) (1.75 * session.frames_per_timecode_frame()) + session.worst_playback_latency();
217 if (last_mtc_timestamp == 0) {
219 last_mtc_timestamp = now;
220 last_mtc_frame = mtc_frame;
224 if (give_slave_full_control_over_transport_speed()) {
227 * its not the average, but we will assign it to current.speed below
230 static framepos_t last_seen_timestamp = 0;
231 static framepos_t last_seen_position = 0;
233 if ((now - last_seen_timestamp) < 300) {
234 mtc_frame = (mtc_frame + last_seen_position)/2;
237 last_seen_timestamp = now;
238 last_seen_position = mtc_frame;
247 framepos_t time_delta = (now - last_mtc_timestamp);
249 if (time_delta != 0) {
250 double apparent_speed = (mtc_frame - last_mtc_frame) / (double) (time_delta);
252 process_apparent_speed (apparent_speed);
253 DEBUG_TRACE (DEBUG::Slave, string_compose ("apparent speed was %1 average is now %2\n", apparent_speed, average_speed));
255 DEBUG_TRACE (DEBUG::Slave, string_compose ("no apparent calc, average is %1\n", average_speed));
258 /* every second, recalibrate the starting point for the speed measurement */
259 if (mtc_frame - last_mtc_frame > session.frame_rate()) {
260 last_mtc_timestamp = now;
261 last_mtc_frame = mtc_frame;
267 current.position = mtc_frame;
268 current.timestamp = now;
269 current.speed = average_speed;
271 window_root = mtc_frame;
276 last_inbound_frame = now;
279 if (window_root >= 0) {
280 reset_window (window_root);
285 MTC_Slave::process_apparent_speed (double this_speed)
287 DEBUG_TRACE (DEBUG::MTC, string_compose ("speed cnt %1 sz %2 have %3\n", speed_accumulator_cnt, speed_accumulator_size, have_first_speed_accumulator));
289 /* clamp to an expected range */
291 if (this_speed > 4.0 || this_speed < -4.0) {
292 this_speed = average_speed;
295 if (speed_accumulator_cnt >= speed_accumulator_size) {
296 have_first_speed_accumulator = true;
297 speed_accumulator_cnt = 0;
300 speed_accumulator[speed_accumulator_cnt++] = this_speed;
302 if (have_first_speed_accumulator) {
304 for (size_t i = 0; i < speed_accumulator_size; ++i) {
305 average_speed += speed_accumulator[i];
307 average_speed /= speed_accumulator_size;
312 MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
316 mtc[4] = last_mtc_fps_byte;
317 mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
322 update_mtc_time (mtc, true, 0);
326 MTC_Slave::update_mtc_status (MIDI::MTC_Status status)
328 /* XXX !!! thread safety ... called from MIDI I/O context
329 and process() context (via ::speed_and_position())
335 current.position = mtc_frame;
336 current.timestamp = 0;
344 current.position = mtc_frame;
345 current.timestamp = 0;
352 current.position = mtc_frame;
353 current.timestamp = 0;
362 MTC_Slave::read_current (SafeTime *st) const
368 error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
375 } while (st->guard1 != st->guard2);
379 MTC_Slave::locked () const
381 return port->parser()->mtc_locked();
385 MTC_Slave::ok() const
391 MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
393 framepos_t now = session.engine().frame_time();
396 bool in_control = false;
398 read_current (&last);
400 if (last.timestamp == 0) {
403 DEBUG_TRACE (DEBUG::MTC, string_compose ("first call to MTC_Slave::speed_and_position, pos = %1\n", last.position));
407 /* no timecode for 1/4 second ? conclude that its stopped */
409 if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > session.frame_rate() / 4) {
412 session.request_locate (pos, false);
413 session.request_transport_speed (0);
415 DEBUG_TRACE (DEBUG::MTC, "MTC not seen for 1/4 second - reset pending\n");
419 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position %1 %2\n", last.speed, last.position));
421 if (give_slave_full_control_over_transport_speed()) {
422 in_control = (session.slave_state() == Session::Running);
423 framepos_t pic_want_locate = 0;
424 //framepos_t slave_pos = session.audible_frame();
425 framepos_t slave_pos = session.transport_frame();
426 static double average_speed = 0;
428 framepos_t ref_now = session.engine().frame_time_at_cycle_start();
429 average_speed = pic->get_ratio (last.timestamp, last.position, ref_now, slave_pos, in_control, session.engine().frames_per_cycle());
431 pic_want_locate = pic->want_locate();
433 if (in_control && pic_want_locate) {
434 last.speed = average_speed + (double) (pic_want_locate - session.transport_frame()) / (double)session.get_block_size();
435 std::cout << "locate req " << pic_want_locate << " speed: " << average_speed << "\n";
437 last.speed = average_speed;
441 if (last.speed == 0.0f) {
447 /* scale elapsed time by the current MTC speed */
449 if (last.timestamp && (now > last.timestamp)) {
450 elapsed = (framecnt_t) floor (last.speed * (now - last.timestamp));
451 DEBUG_TRACE (DEBUG::MTC, string_compose ("last timecode received @ %1, now = %2, elapsed frames = %3 w/speed= %4\n",
452 last.timestamp, now, elapsed, last.speed));
454 elapsed = 0; /* XXX is this right? */
458 /* now add the most recent timecode value plus the estimated elapsed interval */
461 pos = session.transport_frame();
463 pos = last.position + elapsed;
468 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position FINAL %1 %2\n", last.speed, pos));
471 DEBUG_TRACE (DEBUG::MTC, string_compose ("last = %1 elapsed = %2 pos = %3 speed = %4\n", last.position, elapsed, pos, speed));
477 MTC_Slave::resolution () const
479 return (framecnt_t) session.frames_per_timecode_frame();
483 MTC_Slave::queue_reset (bool reset_pos)
485 Glib::Mutex::Lock lm (reset_lock);
488 reset_position = true;
493 MTC_Slave::maybe_reset ()
495 Glib::Mutex::Lock lm (reset_lock);
498 reset (reset_position);
500 reset_position = false;
505 MTC_Slave::reset (bool with_position)
508 last_inbound_frame = 0;
510 current.position = 0;
511 current.timestamp = 0;
515 last_inbound_frame = 0;
517 current.timestamp = 0;
525 last_mtc_timestamp = 0;
528 have_first_speed_accumulator = false;
529 speed_accumulator_cnt = 0;
535 MTC_Slave::reset_window (framepos_t root)
538 /* if we're waiting for the master to catch us after seeking ahead, keep the window
539 of acceptable MTC frames wide open. otherwise, shrink it down to just 2 video frames
540 ahead of the window root (taking direction into account).
543 switch (port->parser()->mtc_running()) {
546 if (session.slave_state() == Session::Running) {
547 window_end = root + (session.frames_per_timecode_frame() * frame_tolerance);
549 window_end = root + seekahead_distance ();
554 if (session.slave_state() == Session::Running) {
555 framecnt_t const d = session.frames_per_timecode_frame() * frame_tolerance;
557 window_begin = root - d;
563 framecnt_t const d = seekahead_distance ();
565 window_begin = root - d;
578 DEBUG_TRACE (DEBUG::MTC, string_compose ("legal MTC window now %1 .. %2\n", window_begin, window_end));
582 MTC_Slave::seekahead_distance () const
585 return session.frame_rate();
589 MTC_Slave::outside_window (framepos_t pos) const
591 return ((pos < window_begin) || (pos > window_end));