* made MidiClock_Slave conform more to to the Spec by starting transport
[ardour.git] / libs / ardour / midi_clock_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 <midi++/jack.h>
30 #include <ardour/slave.h>
31 #include <ardour/session.h>
32 #include <ardour/audioengine.h>
33 #include <ardour/cycles.h>
34 #include <ardour/tempo.h>
35
36
37 #include "i18n.h"
38
39 using namespace ARDOUR;
40 using namespace sigc;
41 using namespace MIDI;
42 using namespace PBD;
43
44 MIDIClock_Slave::MIDIClock_Slave (Session& s, MIDI::Port& p, int ppqn)
45         : session (s)
46         , ppqn (ppqn)
47         , accumulator_index (0)
48         , average_midi_clock_frame_duration (0.0)
49 {
50         rebind (p);
51         reset ();
52
53         for(int i = 0; i < accumulator_size; i++)
54                 accumulator[i]=0.0;
55 }
56
57 MIDIClock_Slave::~MIDIClock_Slave()
58 {
59 }
60
61 void
62 MIDIClock_Slave::rebind (MIDI::Port& p)
63 {
64         for (vector<sigc::connection>::iterator i = connections.begin(); i != connections.end(); ++i) {
65                 (*i).disconnect ();
66         }
67
68         port = &p;
69
70 #ifdef DEBUG_MIDI_CLOCK         
71         std::cerr << "MIDIClock_Slave: connecting to port " << port->name() << std::endl;
72 #endif
73
74         connections.push_back (port->input()->timing.connect   (mem_fun (*this, &MIDIClock_Slave::update_midi_clock)));
75         connections.push_back (port->input()->start.connect    (mem_fun (*this, &MIDIClock_Slave::start)));
76         connections.push_back (port->input()->contineu.connect (mem_fun (*this, &MIDIClock_Slave::contineu)));
77         connections.push_back (port->input()->stop.connect     (mem_fun (*this, &MIDIClock_Slave::stop)));
78 }
79
80 void 
81 MIDIClock_Slave::calculate_one_ppqn_in_frames_at(nframes_t time)
82 {
83         const Tempo& current_tempo = session.tempo_map().tempo_at(time);
84         const Meter& current_meter = session.tempo_map().meter_at(time);
85         double frames_per_beat =
86                 current_tempo.frames_per_beat(session.nominal_frame_rate(),
87                                               current_meter);
88
89         double quarter_notes_per_beat = 4.0 / current_tempo.note_type();
90         double frames_per_quarter_note = frames_per_beat / quarter_notes_per_beat;
91
92         one_ppqn_in_frames = frames_per_quarter_note / double (ppqn);
93 }
94
95 void
96 MIDIClock_Slave::update_midi_clock (Parser& parser, nframes_t timestamp)
97 {       
98         nframes_t now = timestamp;
99
100         SafeTime last;
101         read_current (&last);
102         
103         if (_starting) {
104                 assert(last.timestamp == 0);
105                 // let ardour go after first MIDI Clock Event
106                 _starting = false;
107         }
108                 
109         calculate_one_ppqn_in_frames_at(now);
110         
111         // for the first MIDI clock event we don't have any past
112         // data, so we assume a sane tempo
113         if(last.timestamp == 0) {
114                 current_midi_clock_frame_duration = one_ppqn_in_frames;
115         } else {
116                 current_midi_clock_frame_duration = now - last.timestamp;
117         }
118                 
119         // moving average over incoming intervals
120         accumulator[accumulator_index++] = current_midi_clock_frame_duration;
121         if(accumulator_index == accumulator_size)
122                 accumulator_index = 0;
123         
124         average_midi_clock_frame_duration = 0.0;
125         for(int i = 0; i < accumulator_size; i++)
126                 average_midi_clock_frame_duration += accumulator[i];
127         average_midi_clock_frame_duration /= double(accumulator_size);
128         
129 #ifdef DEBUG_MIDI_CLOCK         
130 #ifdef WITH_JACK_MIDI
131         JACK_MidiPort* jack_port = dynamic_cast<JACK_MidiPort*>(port);
132         pthread_t process_thread_id = 0;
133         if(jack_port) {
134                 process_thread_id = jack_port->get_process_thread();
135         }
136         
137         std::cerr 
138                   << " got MIDI Clock message at time " << now  
139                   << " session time: " << session.engine().frame_time() 
140                   << " transport position: " << session.transport_frame()
141                   << " real delta: " << current_midi_clock_frame_duration 
142                   << " reference: " << one_ppqn_in_frames
143                   << " average: " << average_midi_clock_frame_duration
144                   << std::endl;
145 #endif // DEBUG_MIDI_CLOCK
146 #endif // WITH_JACK_MIDI
147         
148         current.guard1++;
149         current.position += one_ppqn_in_frames;
150         current_position += one_ppqn_in_frames;
151         current.timestamp = now;
152         current.guard2++;
153
154         last_inbound_frame = now;
155 }
156
157 void
158 MIDIClock_Slave::start (Parser& parser, nframes_t timestamp)
159 {
160         
161         nframes_t now = timestamp;
162         
163 #ifdef DEBUG_MIDI_CLOCK 
164         cerr << "MIDIClock_Slave got start message at time "  <<  now << " session time: " << session.engine().frame_time() << endl;
165 #endif
166         
167         if(!locked()) {
168                 cerr << "Did not start because not locked!" << endl;
169                 return;
170         }
171         
172         current_midi_clock_frame_duration = 0;
173         
174         current.guard1++;
175         current.position = 0;
176         current_position = 0;   
177         current.timestamp = 0;
178         current.guard2++;
179         
180         _started = true;
181         _starting = true;
182 }
183
184 void
185 MIDIClock_Slave::contineu (Parser& parser, nframes_t timestamp)
186 {
187 #ifdef DEBUG_MIDI_CLOCK 
188         std::cerr << "MIDIClock_Slave got continue message" << endl;
189 #endif
190         start(parser, timestamp);
191 }
192
193
194 void
195 MIDIClock_Slave::stop (Parser& parser, nframes_t timestamp)
196 {
197 #ifdef DEBUG_MIDI_CLOCK 
198         std::cerr << "MIDIClock_Slave got stop message" << endl;
199 #endif
200         
201         current_midi_clock_frame_duration = 0;
202
203         current.guard1++;
204         current.position = 0;
205         current_position = 0;   
206         current.timestamp = 0;
207         current.guard2++;
208         
209         _started = false;
210         reset();
211 }
212
213 void
214 MIDIClock_Slave::read_current (SafeTime *st) const
215 {
216         int tries = 0;
217         do {
218                 if (tries == 10) {
219                         error << _("MIDI Clock Slave: atomic read of current time failed, sleeping!") << endmsg;
220                         usleep (20);
221                         tries = 0;
222                 }
223
224                 *st = current;
225                 tries++;
226
227         } while (st->guard1 != st->guard2);
228 }
229
230 bool
231 MIDIClock_Slave::locked () const
232 {
233         return true;
234 }
235
236 bool
237 MIDIClock_Slave::ok() const
238 {
239         return true;
240 }
241
242 bool
243 MIDIClock_Slave::starting() const
244 {
245         return _starting;
246 }
247
248 bool
249 MIDIClock_Slave::stop_if_no_more_clock_events(nframes_t& pos, nframes_t now, SafeTime& last)
250 {
251         /* no timecode for 1/4 second ? conclude that its stopped */
252         if (last_inbound_frame && 
253             now > last_inbound_frame && 
254             now - last_inbound_frame > session.frame_rate() / 4) {
255 #ifdef DEBUG_MIDI_CLOCK                 
256                 cerr << "No MIDI Clock frames received for some time, stopping!" << endl;
257 #endif          
258                 pos = last.position;
259                 session.request_locate (pos, false);
260                 session.request_transport_speed (0);
261                 this->stop(*port->input(), now);
262                 reset();
263                 return true;
264         } else {
265                 return false;
266         }
267 }
268
269 bool
270 MIDIClock_Slave::speed_and_position (float& speed, nframes_t& pos)
271 {
272         if (!_started) {
273                 speed = 0.0;
274                 pos = 0;
275                 return true;
276         }
277                 
278         nframes_t now = session.engine().frame_time();
279         SafeTime last;
280         read_current (&last);
281
282         if (stop_if_no_more_clock_events(pos, now, last)) {
283                 return false;
284         }
285         //cerr << " now: " << now << " last: " << last.timestamp;
286
287         // calculate speed
288         double speed_double = one_ppqn_in_frames / average_midi_clock_frame_duration;
289         speed = float(speed_double);
290         //cerr << " final speed: " << speed;
291         
292         // calculate position
293         if (now > last.timestamp) {
294                 // we are in between MIDI clock messages
295                 // so we interpolate position according to speed
296                 nframes_t elapsed = now - last.timestamp;
297                 pos = nframes_t (current_position + double(elapsed) * speed_double);
298         } else {
299                 // A new MIDI clock message has arrived this cycle
300                 pos = current_position;
301         }
302         
303         /*
304    cerr << " transport position now: " <<  session.transport_frame(); 
305    cerr << " calculated position: " << pos; 
306    cerr << endl;
307    */
308    
309         return true;
310 }
311
312 ARDOUR::nframes_t
313 MIDIClock_Slave::resolution() const
314 {
315         // one beat
316         return (nframes_t) one_ppqn_in_frames * ppqn;
317 }
318
319 void
320 MIDIClock_Slave::reset ()
321 {
322
323         last_inbound_frame = 0;
324         current.guard1++;
325         current.position = 0;
326         current_position = 0;           
327         current.timestamp = 0;
328         current.guard2++;
329         
330         session.request_locate(0, false);
331 }