merged with 1697 revision of trunk (which is post-rc1 but pre-rc2
[ardour.git] / libs / ardour / mtc_slave.cc
1 /*
2     Copyright (C) 2002-4 Paul Davis 
3
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.
8
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.
13
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.
17
18 */
19
20 #include <errno.h>
21 #include <poll.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 #include <pbd/error.h>
25 #include <pbd/failed_constructor.h>
26 #include <pbd/pthread_utils.h>
27
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>
33
34 #include "i18n.h"
35
36 using namespace ARDOUR;
37 using namespace sigc;
38 using namespace MIDI;
39 using namespace PBD;
40
41 MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p) 
42         : session (s)
43 {
44         can_notify_on_unknown_rate = true;
45
46         rebind (p);
47         reset ();
48 }
49
50 MTC_Slave::~MTC_Slave()
51 {
52 }
53
54 void
55 MTC_Slave::rebind (MIDI::Port& p)
56 {
57         for (vector<sigc::connection>::iterator i = connections.begin(); i != connections.end(); ++i) {
58                 (*i).disconnect ();
59         }
60
61         port = &p;
62
63         connections.push_back (port->input()->mtc_time.connect (mem_fun (*this, &MTC_Slave::update_mtc_time)));
64         connections.push_back (port->input()->mtc_qtr.connect (mem_fun (*this, &MTC_Slave::update_mtc_qtr)));
65         connections.push_back (port->input()->mtc_status.connect (mem_fun (*this, &MTC_Slave::update_mtc_status)));
66 }
67
68 void
69 MTC_Slave::update_mtc_qtr (Parser& p)
70 {
71         cycles_t cnow = get_cycles ();
72         nframes_t now = session.engine().frame_time();
73         nframes_t qtr;
74         static cycles_t last_qtr = 0;
75
76         qtr = (long) (session.frames_per_smpte_frame() / 4);
77         mtc_frame += qtr;
78         last_qtr = cnow;
79
80         current.guard1++;
81         current.position = mtc_frame;
82         current.timestamp = now;
83         current.guard2++;
84
85         last_inbound_frame = now;
86 }
87
88 void
89 MTC_Slave::update_mtc_time (const byte *msg, bool was_full)
90 {
91         nframes_t now = session.engine().frame_time();
92         SMPTE::Time smpte;
93         
94         smpte.hours = msg[3];
95         smpte.minutes = msg[2];
96         smpte.seconds = msg[1];
97         smpte.frames = msg[0];
98
99         switch (msg[4]) {
100         case MTC_24_FPS:
101                 smpte.rate = 24;
102                 smpte.drop = false;
103                 can_notify_on_unknown_rate = true;
104                 break;
105         case MTC_25_FPS:
106                 smpte.rate = 25;
107                 smpte.drop = false;
108                 can_notify_on_unknown_rate = true;
109                 break;
110         case MTC_30_FPS_DROP:
111                 smpte.rate = 30;
112                 smpte.drop = true;
113                 can_notify_on_unknown_rate = true;
114                 break;
115         case MTC_30_FPS:
116                 smpte.rate = 30;
117                 smpte.drop = false;
118                 can_notify_on_unknown_rate = true;
119                 break;
120         default:
121                 /* throttle error messages about unknown MTC rates */
122                 if (can_notify_on_unknown_rate) {
123                         error << _("Unknown rate/drop value in incoming MTC stream, session values used instead") << endmsg;
124                         can_notify_on_unknown_rate = false;
125                 }
126                 smpte.rate = session.smpte_frames_per_second();
127                 smpte.drop = session.smpte_drop_frames();
128         }
129
130         session.smpte_to_sample (smpte, mtc_frame, true, false);
131         
132         if (was_full) {
133                 
134                 current.guard1++;        
135                 current.position = mtc_frame;    
136                 current.timestamp = 0;   
137                 current.guard2++;        
138                 
139                 session.request_locate (mtc_frame, false);       
140                 update_mtc_status (MIDI::Parser::MTC_Stopped);   
141
142                 reset ();
143                 
144         } else {
145                 
146                 /* We received the last quarter frame 7 quarter frames (1.75 mtc
147                    frames) after the instance when the contents of the mtc quarter
148                    frames were decided. Add time to compensate for the elapsed 1.75
149                    frames.
150                    Also compensate for audio latency. 
151                 */
152
153                 mtc_frame += (long) (1.75 * session.frames_per_smpte_frame()) + session.worst_output_latency();
154                 
155                 if (first_mtc_frame == 0) {
156                         first_mtc_frame = mtc_frame;
157                         first_mtc_time = now;
158                 } 
159                 
160                 current.guard1++;
161                 current.position = mtc_frame;
162                 current.timestamp = now;
163                 current.guard2++;
164         }
165         
166         last_inbound_frame = now;
167 }
168
169 void
170 MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
171 {
172         MIDI::byte mtc[4];
173         
174         mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
175         mtc[2] = mmc_tc[1];
176         mtc[1] = mmc_tc[2];
177         mtc[0] = mmc_tc[3];
178
179         update_mtc_time (mtc, true);
180 }
181
182 void
183 MTC_Slave::update_mtc_status (MIDI::Parser::MTC_Status status)
184 {
185
186         switch (status) {
187         case MTC_Stopped:
188                 mtc_speed = 0.0f;
189                 mtc_frame = 0;
190
191                 current.guard1++;
192                 current.position = mtc_frame;
193                 current.timestamp = 0;
194                 current.guard2++;
195
196                 break;
197
198         case MTC_Forward:
199                 mtc_speed = 0.0f;
200                 mtc_frame = 0;
201
202                 current.guard1++;
203                 current.position = mtc_frame;
204                 current.timestamp = 0;
205                 current.guard2++;
206
207                 break;
208
209         case MTC_Backward:
210                 mtc_speed = 0.0f;
211                 mtc_frame = 0;
212
213                 current.guard1++;
214                 current.position = mtc_frame;
215                 current.timestamp = 0;
216                 current.guard2++;
217
218                 break;
219         }
220 }
221
222 void
223 MTC_Slave::read_current (SafeTime *st) const
224 {
225         int tries = 0;
226         do {
227                 if (tries == 10) {
228                         error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
229                         usleep (20);
230                         tries = 0;
231                 }
232
233                 *st = current;
234                 tries++;
235
236         } while (st->guard1 != st->guard2);
237 }
238
239 bool
240 MTC_Slave::locked () const
241 {
242         return port->input()->mtc_locked();
243 }
244
245 bool
246 MTC_Slave::ok() const
247 {
248         return true;
249 }
250
251 bool 
252 MTC_Slave::speed_and_position (float& speed, nframes_t& pos)
253 {
254         nframes_t now = session.engine().frame_time();
255         SafeTime last;
256         nframes_t frame_rate;
257         nframes_t elapsed;
258         float speed_now;
259
260         read_current (&last);
261
262         if (first_mtc_time == 0) {
263                 speed = 0;
264                 pos = last.position;
265                 return true;
266         }
267         
268         /* no timecode for 1/4 second ? conclude that its stopped */
269
270         if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > session.frame_rate() / 4) {
271                 mtc_speed = 0;
272                 pos = last.position;
273                 session.request_locate (pos, false);
274                 update_mtc_status (MIDI::Parser::MTC_Stopped);
275                 reset();
276                 return false;
277         }
278
279         frame_rate = session.frame_rate();
280
281         speed_now = (float) ((last.position - first_mtc_frame) / (double) (now - first_mtc_time));
282
283         accumulator[accumulator_index++] = speed_now;
284
285         if (accumulator_index >= accumulator_size) {
286                 have_first_accumulated_speed = true;
287                 accumulator_index = 0;
288         }
289
290         if (have_first_accumulated_speed) {
291                 float total = 0;
292
293                 for (int32_t i = 0; i < accumulator_size; ++i) {
294                         total += accumulator[i];
295                 }
296
297                 mtc_speed = total / accumulator_size;
298
299         } else {
300
301                 mtc_speed = speed_now;
302
303         }
304
305         if (mtc_speed == 0.0f) {
306
307                 elapsed = 0;
308
309         } else {
310         
311                 /* scale elapsed time by the current MTC speed */
312                 
313                 if (last.timestamp && (now > last.timestamp)) {
314                         elapsed = (nframes_t) floor (mtc_speed * (now - last.timestamp));
315                 } else {
316                         elapsed = 0; /* XXX is this right? */
317                 }
318         }
319
320         /* now add the most recent timecode value plus the estimated elapsed interval */
321
322         pos =  elapsed + last.position;
323
324         speed = mtc_speed;
325         return true;
326 }
327
328 nframes_t
329 MTC_Slave::resolution() const
330 {
331         return (nframes_t) session.frames_per_smpte_frame();
332 }
333
334 void
335 MTC_Slave::reset ()
336 {
337         /* XXX massive thread safety issue here. MTC could
338            be being updated as we call this. but this
339            supposed to be a realtime-safe call.
340         */
341         
342         port->input()->reset_mtc_state ();
343         
344         last_inbound_frame = 0;
345         current.guard1++;
346         current.position = 0;
347         current.timestamp = 0;
348         current.guard2++;
349         first_mtc_frame = 0;
350         first_mtc_time = 0;
351
352         accumulator_index = 0;
353         have_first_accumulated_speed = false;
354         mtc_speed = 0;
355 }