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>
24 #include "pbd/error.h"
25 #include "pbd/enumwriter.h"
26 #include "pbd/failed_constructor.h"
27 #include "pbd/pthread_utils.h"
29 #include "midi++/port.h"
30 #include "ardour/debug.h"
31 #include "ardour/slave.h"
32 #include "ardour/session.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/pi_controller.h"
39 using namespace ARDOUR;
43 /* length (in timecode frames) of the "window" that we consider legal given receipt of
44 a given timecode position. Ardour will try to chase within this window, and will
45 stop+locate+wait+chase if timecode arrives outside of it. The window extends entirely
46 in the current direction of motion, so if any timecode arrives that is before the most
47 recently received position (and without the direction of timecode reversing too), we
48 will stop+locate+wait+chase.
51 const int MTC_Slave::frame_tolerance = 2;
53 MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
55 , port_connections (0)
57 can_notify_on_unknown_rate = true;
58 did_reset_tc_format = false;
62 last_mtc_fps_byte = session.get_mtc_timecode_bits ();
65 speed_accumulator_size = 16;
66 speed_accumulator = new double[speed_accumulator_size];
72 MTC_Slave::~MTC_Slave()
74 delete port_connections;
76 if (did_reset_tc_format) {
77 session.config.set_timecode_format (saved_tc_format);
81 delete [] speed_accumulator;
85 MTC_Slave::give_slave_full_control_over_transport_speed() const
87 // return true; // for PiC control */
88 return false; // for Session-level computed varispeed
92 MTC_Slave::rebind (MIDI::Port& p)
94 delete port_connections;
95 port_connections = new ScopedConnectionList;
99 port_connections->add_connection (port->input()->mtc_time.connect ( boost::bind (&MTC_Slave::update_mtc_time, this, _1, _2, _3)));
100 port_connections->add_connection (port->input()->mtc_qtr.connect (boost::bind (&MTC_Slave::update_mtc_qtr, this, _1, _2, _3)));
101 port_connections->add_connection (port->input()->mtc_status.connect (boost::bind (&MTC_Slave::update_mtc_status, this, _1)));
105 MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, nframes_t now)
109 DEBUG_TRACE (DEBUG::MTC, string_compose ("qtr frame %1 at %2\n", which_qtr, now));
110 last_inbound_frame = now;
114 MTC_Slave::update_mtc_time (const byte *msg, bool was_full, nframes_t now)
116 /* "now" can be zero if this is called from a context where we do not have or do not want
117 to use a timestamp indicating when this MTC time was received. example: when we received
118 a locate command via MMC.
125 Timecode::Time timecode;
126 TimecodeFormat tc_format;
127 bool reset_tc = true;
128 nframes64_t window_root = -1;
130 DEBUG_TRACE (DEBUG::MTC, string_compose ("full mtc time known at %1, full ? %2\n", now, was_full));
132 timecode.hours = msg[3];
133 timecode.minutes = msg[2];
134 timecode.seconds = msg[1];
135 timecode.frames = msg[0];
137 last_mtc_fps_byte = msg[4];
142 timecode.drop = false;
143 tc_format = timecode_24;
144 can_notify_on_unknown_rate = true;
148 timecode.drop = false;
149 tc_format = timecode_25;
150 can_notify_on_unknown_rate = true;
152 case MTC_30_FPS_DROP:
154 timecode.drop = true;
155 tc_format = timecode_30drop;
156 can_notify_on_unknown_rate = true;
160 timecode.drop = false;
161 can_notify_on_unknown_rate = true;
162 tc_format = timecode_30;
165 /* throttle error messages about unknown MTC rates */
166 if (can_notify_on_unknown_rate) {
167 error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
170 can_notify_on_unknown_rate = false;
172 timecode.rate = session.timecode_frames_per_second();
173 timecode.drop = session.timecode_drop_frames();
178 if (!did_reset_tc_format) {
179 saved_tc_format = session.config.get_timecode_format();
180 did_reset_tc_format = true;
182 session.config.set_timecode_format (tc_format);
185 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC time timestamp = %1 TC %2 = frame %3 (from full message ? %4)\n",
186 now, timecode, mtc_frame, was_full));
188 if (was_full || outside_window (mtc_frame)) {
190 session.timecode_to_sample (timecode, mtc_frame, true, false);
191 session.request_locate (mtc_frame, false);
192 session.request_transport_speed (0);
193 update_mtc_status (MIDI::MTC_Stopped);
194 reset_window (mtc_frame);
199 /* we've had the first set of 8 qtr frame messages, determine position
200 and allow continuing qtr frame messages to provide position
201 and speed information.
204 /* do a careful conversion of the timecode value to a position
205 so that we take drop/nondrop and all that nonsense into
209 session.timecode_to_sample (timecode, mtc_frame, true, false);
211 /* We received the last quarter frame 7 quarter frames (1.75 mtc
212 frames) after the instance when the contents of the mtc quarter
213 frames were decided. Add time to compensate for the elapsed 1.75
214 frames. Also compensate for audio latency.
217 mtc_frame += (long) (1.75 * session.frames_per_timecode_frame()) + session.worst_output_latency();
222 if (last_mtc_timestamp == 0) {
224 last_mtc_timestamp = now;
225 last_mtc_frame = mtc_frame;
229 if (give_slave_full_control_over_transport_speed()) {
232 * its not the average, but we will assign it to current.speed below
235 static nframes64_t last_seen_timestamp = 0;
236 static nframes64_t last_seen_position = 0;
238 if ((now - last_seen_timestamp) < 300) {
239 mtc_frame = (mtc_frame + last_seen_position)/2;
242 last_seen_timestamp = now;
243 last_seen_position = mtc_frame;
252 nframes64_t time_delta = (now - last_mtc_timestamp);
254 if (time_delta != 0) {
255 double apparent_speed = (mtc_frame - last_mtc_frame) / (double) (time_delta);
257 process_apparent_speed (apparent_speed);
258 DEBUG_TRACE (DEBUG::Slave, string_compose ("apparent speed was %1 average is now %2\n", apparent_speed, average_speed));
260 DEBUG_TRACE (DEBUG::Slave, string_compose ("no apparent calc, average is %1\n", average_speed));
263 /* every second, recalibrate the starting point for the speed measurement */
264 if (mtc_frame - last_mtc_frame > session.frame_rate()) {
265 last_mtc_timestamp = now;
266 last_mtc_frame = mtc_frame;
272 current.position = mtc_frame;
273 current.timestamp = now;
274 current.speed = average_speed;
276 window_root = mtc_frame;
281 last_inbound_frame = now;
284 if (window_root >= 0) {
285 reset_window (window_root);
290 MTC_Slave::process_apparent_speed (double this_speed)
292 DEBUG_TRACE (DEBUG::MTC, string_compose ("speed cnt %1 sz %2 have %3\n", speed_accumulator_cnt, speed_accumulator_size, have_first_speed_accumulator));
294 /* clamp to an expected range */
296 if (this_speed > 4.0 || this_speed < -4.0) {
297 this_speed = average_speed;
300 if (speed_accumulator_cnt >= speed_accumulator_size) {
301 have_first_speed_accumulator = true;
302 speed_accumulator_cnt = 0;
305 speed_accumulator[speed_accumulator_cnt++] = this_speed;
307 if (have_first_speed_accumulator) {
309 for (size_t i = 0; i < speed_accumulator_size; ++i) {
310 average_speed += speed_accumulator[i];
312 average_speed /= speed_accumulator_size;
317 MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
321 mtc[4] = last_mtc_fps_byte;
322 mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
327 update_mtc_time (mtc, true, 0);
331 MTC_Slave::update_mtc_status (MIDI::MTC_Status status)
333 /* XXX !!! thread safety ... called from MIDI I/O context
334 and process() context (via ::speed_and_position())
338 DEBUG_TRACE (DEBUG::MTC, string_compose ("new MTC status %1\n", enum_2_string (status)));
342 current.position = mtc_frame;
343 current.timestamp = 0;
351 current.position = mtc_frame;
352 current.timestamp = 0;
359 current.position = mtc_frame;
360 current.timestamp = 0;
369 MTC_Slave::read_current (SafeTime *st) const
375 error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
382 } while (st->guard1 != st->guard2);
386 MTC_Slave::locked () const
388 return port->input()->mtc_locked();
392 MTC_Slave::ok() const
398 MTC_Slave::speed_and_position (double& speed, nframes64_t& pos)
400 nframes64_t now = session.engine().frame_time();
404 read_current (&last);
406 if (last.timestamp == 0) {
409 DEBUG_TRACE (DEBUG::MTC, string_compose ("first call to MTC_Slave::speed_and_position, pos = %1\n", last.position));
413 /* no timecode for 1/4 second ? conclude that its stopped */
415 if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > session.frame_rate() / 4) {
418 session.request_locate (pos, false);
419 session.request_transport_speed (0);
421 DEBUG_TRACE (DEBUG::MTC, "MTC not seen for 1/4 second - reset pending\n");
425 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position %1 %2\n", last.speed, last.position));
427 if (give_slave_full_control_over_transport_speed()) {
428 bool in_control = (session.slave_state() == Session::Running);
429 nframes64_t pic_want_locate = 0;
430 //nframes64_t slave_pos = session.audible_frame();
431 nframes64_t slave_pos = session.transport_frame();
432 static double average_speed = 0;
434 average_speed = pic->get_ratio (last.timestamp, last.position, slave_pos, in_control );
435 pic_want_locate = pic->want_locate();
437 if (in_control && pic_want_locate) {
438 last.speed = average_speed + (double) (pic_want_locate - session.transport_frame()) / (double)session.get_block_size();
439 std::cout << "locate req " << pic_want_locate << " speed: " << average_speed << "\n";
441 last.speed = average_speed;
445 if (last.speed == 0.0f) {
451 /* scale elapsed time by the current MTC speed */
453 if (last.timestamp && (now > last.timestamp)) {
454 elapsed = (nframes_t) floor (last.speed * (now - last.timestamp));
455 DEBUG_TRACE (DEBUG::MTC, string_compose ("last timecode received @ %1, now = %2, elapsed frames = %3 w/speed= %4\n",
456 last.timestamp, now, elapsed, last.speed));
458 elapsed = 0; /* XXX is this right? */
462 /* now add the most recent timecode value plus the estimated elapsed interval */
464 pos = last.position + elapsed;
467 DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::speed_and_position FINAL %1 %2\n", last.speed, pos));
473 MTC_Slave::resolution() const
475 return (nframes_t) session.frames_per_timecode_frame();
479 MTC_Slave::queue_reset ()
481 Glib::Mutex::Lock lm (reset_lock);
486 MTC_Slave::maybe_reset ()
495 reset_lock.unlock ();
501 port->input()->reset_mtc_state ();
503 last_inbound_frame = 0;
505 current.position = 0;
506 current.timestamp = 0;
513 last_mtc_timestamp = 0;
516 have_first_speed_accumulator = false;
517 speed_accumulator_cnt = 0;
523 MTC_Slave::reset_window (nframes64_t root)
526 /* if we're waiting for the master to catch us after seeking ahead, keep the window
527 of acceptable MTC frames wide open. otherwise, shrink it down to just 2 video frames
528 ahead of the window root (taking direction into account).
531 switch (port->input()->mtc_running()) {
534 if (session.slave_state() == Session::Running) {
535 window_end = root + (session.frames_per_timecode_frame() * frame_tolerance);
537 window_end = root + seekahead_distance ();
539 DEBUG_TRACE (DEBUG::MTC, string_compose ("legal MTC window now %1 .. %2\n", window_begin, window_end));
543 if (session.slave_state() == Session::Running) {
544 nframes_t d = session.frames_per_timecode_frame() * frame_tolerance;
546 window_begin = root - d;
552 nframes_t d = seekahead_distance ();
554 window_begin = root - d;
560 DEBUG_TRACE (DEBUG::MTC, string_compose ("legal MTC window now %1 .. %2\n", window_begin, window_end));
570 MTC_Slave::seekahead_distance () const
573 return session.frame_rate();
577 MTC_Slave::outside_window (nframes64_t pos) const
579 return ((pos < window_begin) || (pos > window_end));