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