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