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/failed_constructor.h"
26 #include "pbd/pthread_utils.h"
28 #include "midi++/port.h"
29 #include "ardour/slave.h"
30 #include "ardour/session.h"
31 #include "ardour/audioengine.h"
32 #include "ardour/cycles.h"
37 using namespace ARDOUR;
42 MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
45 can_notify_on_unknown_rate = true;
47 last_mtc_fps_byte = session.get_mtc_timecode_bits ();
53 MTC_Slave::~MTC_Slave()
58 MTC_Slave::rebind (MIDI::Port& p)
60 for (vector<sigc::connection>::iterator i = connections.begin(); i != connections.end(); ++i) {
66 connections.push_back (port->input()->mtc_time.connect (mem_fun (*this, &MTC_Slave::update_mtc_time)));
67 connections.push_back (port->input()->mtc_qtr.connect (mem_fun (*this, &MTC_Slave::update_mtc_qtr)));
68 connections.push_back (port->input()->mtc_status.connect (mem_fun (*this, &MTC_Slave::update_mtc_status)));
72 MTC_Slave::update_mtc_qtr (Parser& /*p*/)
74 cycles_t cnow = get_cycles ();
75 nframes64_t now = session.engine().frame_time();
77 static cycles_t last_qtr = 0;
79 qtr = (long) (session.frames_per_timecode_frame() / 4);
84 current.position = mtc_frame;
85 current.timestamp = now;
88 last_inbound_frame = now;
92 MTC_Slave::update_mtc_time (const byte *msg, bool was_full)
94 nframes64_t now = session.engine().frame_time();
95 Timecode::Time timecode;
97 timecode.hours = msg[3];
98 timecode.minutes = msg[2];
99 timecode.seconds = msg[1];
100 timecode.frames = msg[0];
102 last_mtc_fps_byte = msg[4];
107 timecode.drop = false;
108 can_notify_on_unknown_rate = true;
112 timecode.drop = false;
113 can_notify_on_unknown_rate = true;
115 case MTC_30_FPS_DROP:
117 timecode.drop = true;
118 can_notify_on_unknown_rate = true;
122 timecode.drop = false;
123 can_notify_on_unknown_rate = true;
126 /* throttle error messages about unknown MTC rates */
127 if (can_notify_on_unknown_rate) {
128 error << string_compose (_("Unknown rate/drop value %1 in incoming MTC stream, session values used instead"),
131 can_notify_on_unknown_rate = false;
133 timecode.rate = session.timecode_frames_per_second();
134 timecode.drop = session.timecode_drop_frames();
137 session.timecode_to_sample (timecode, mtc_frame, true, false);
142 current.position = mtc_frame;
143 current.timestamp = 0;
146 session.request_locate (mtc_frame, false);
147 session.request_transport_speed (0);
148 update_mtc_status (MIDI::Parser::MTC_Stopped);
154 /* We received the last quarter frame 7 quarter frames (1.75 mtc
155 frames) after the instance when the contents of the mtc quarter
156 frames were decided. Add time to compensate for the elapsed 1.75
158 Also compensate for audio latency.
161 mtc_frame += (long) (1.75 * session.frames_per_timecode_frame()) + session.worst_output_latency();
163 if (first_mtc_frame == 0) {
164 first_mtc_frame = mtc_frame;
165 first_mtc_time = now;
169 current.position = mtc_frame;
170 current.timestamp = now;
174 last_inbound_frame = now;
178 MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
182 mtc[4] = last_mtc_fps_byte;
183 mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
188 update_mtc_time (mtc, true);
192 MTC_Slave::update_mtc_status (MIDI::Parser::MTC_Status status)
201 current.position = mtc_frame;
202 current.timestamp = 0;
212 current.position = mtc_frame;
213 current.timestamp = 0;
223 current.position = mtc_frame;
224 current.timestamp = 0;
232 MTC_Slave::read_current (SafeTime *st) const
237 error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
245 } while (st->guard1 != st->guard2);
249 MTC_Slave::locked () const
251 return port->input()->mtc_locked();
255 MTC_Slave::ok() const
261 MTC_Slave::speed_and_position (double& speed, nframes64_t& pos)
263 nframes64_t now = session.engine().frame_time();
265 nframes_t frame_rate;
269 read_current (&last);
271 if (first_mtc_time == 0) {
277 /* no timecode for 1/4 second ? conclude that its stopped */
279 if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > session.frame_rate() / 4) {
282 session.request_locate (pos, false);
283 session.request_transport_speed (0);
284 update_mtc_status (MIDI::Parser::MTC_Stopped);
289 frame_rate = session.frame_rate();
291 speed_now = (double) ((last.position - first_mtc_frame) / (double) (now - first_mtc_time));
293 accumulator[accumulator_index++] = speed_now;
295 if (accumulator_index >= accumulator_size) {
296 have_first_accumulated_speed = true;
297 accumulator_index = 0;
300 if (have_first_accumulated_speed) {
303 for (int32_t i = 0; i < accumulator_size; ++i) {
304 total += accumulator[i];
307 mtc_speed = total / accumulator_size;
311 mtc_speed = speed_now;
315 if (mtc_speed == 0.0f) {
321 /* scale elapsed time by the current MTC speed */
323 if (last.timestamp && (now > last.timestamp)) {
324 elapsed = (nframes_t) floor (mtc_speed * (now - last.timestamp));
326 elapsed = 0; /* XXX is this right? */
330 /* now add the most recent timecode value plus the estimated elapsed interval */
332 pos = elapsed + last.position;
339 MTC_Slave::resolution() const
341 return (nframes_t) session.frames_per_timecode_frame();
347 /* XXX massive thread safety issue here. MTC could
348 be being updated as we call this. but this
349 supposed to be a realtime-safe call.
352 port->input()->reset_mtc_state ();
354 last_inbound_frame = 0;
356 current.position = 0;
357 current.timestamp = 0;
362 accumulator_index = 0;
363 have_first_accumulated_speed = false;