4b6efdd9491bf1b5ba3556d13b96f3b97a806da6
[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 <ardour/slave.h>
30 #include <ardour/session.h>
31 #include <ardour/audioengine.h>
32 #include <ardour/cycles.h>
33 #include <ardour/tempo.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 MIDIClock_Slave::MIDIClock_Slave (Session& s, MIDI::Port& p, int ppqn)
43         : session (s)
44         , ppqn (ppqn)
45 {
46         rebind (p);
47         reset ();
48 }
49
50 MIDIClock_Slave::~MIDIClock_Slave()
51 {
52 }
53
54 void
55 MIDIClock_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         std::cerr << "MIDIClock_Slave: connecting to port " << port->name() << std::endl;
64
65         connections.push_back (port->input()->timing.connect (mem_fun (*this, &MIDIClock_Slave::update_midi_clock)));
66         connections.push_back (port->input()->start.connect  (mem_fun (*this, &MIDIClock_Slave::start)));
67         connections.push_back (port->input()->stop.connect   (mem_fun (*this, &MIDIClock_Slave::stop)));
68 }
69
70 void
71 MIDIClock_Slave::update_midi_clock (Parser& parser)
72 {
73         // ignore clock events if no start event received
74         if(!_started)
75                 return;
76
77         nframes_t now = session.engine().frame_time();
78
79         SafeTime last;
80         read_current (&last);
81
82         const Tempo& current_tempo = session.tempo_map().tempo_at(now);
83         const Meter& current_meter = session.tempo_map().meter_at(now);
84         double frames_per_beat =
85                 current_tempo.frames_per_beat(session.nominal_frame_rate(),
86                                               current_meter);
87
88         double quarter_notes_per_beat = 4.0 / current_tempo.note_type();
89         double frames_per_quarter_note = frames_per_beat / quarter_notes_per_beat;
90
91         one_ppqn_in_frames = frames_per_quarter_note / ppqn;
92
93         /*
94            Also compensate for audio latency.
95         */
96
97         midi_clock_frame += (long) (one_ppqn_in_frames)
98                             + session.worst_output_latency();
99
100         /*
101         std::cerr << "got MIDI Clock message at time " << now  
102                   << " result: " << midi_clock_frame 
103                   << " open_ppqn_in_frames: " << one_ppqn_in_frames << std::endl;
104          */
105         
106         if (first_midi_clock_frame == 0) {
107                 first_midi_clock_frame = midi_clock_frame;
108                 first_midi_clock_time = now;
109         }
110
111         current.guard1++;
112         current.position = midi_clock_frame;
113         current.timestamp = now;
114         current.guard2++;
115
116         last_inbound_frame = now;
117 }
118
119 void
120 MIDIClock_Slave::start (Parser& parser)
121 {
122         std::cerr << "MIDIClock_Slave got start message" << endl;
123
124         midi_clock_speed = 1.0f;
125         _started = true;
126 }
127
128 void
129 MIDIClock_Slave::stop (Parser& parser)
130 {
131         std::cerr << "MIDIClock_Slave got stop message" << endl;
132
133         midi_clock_speed = 0.0f;
134         midi_clock_frame = 0;
135         _started = false;
136
137         current.guard1++;
138         current.position = midi_clock_frame;
139         current.timestamp = 0;
140         current.guard2++;
141 }
142
143 void
144 MIDIClock_Slave::read_current (SafeTime *st) const
145 {
146         int tries = 0;
147         do {
148                 if (tries == 10) {
149                         error << _("MIDI Clock Slave: atomic read of current time failed, sleeping!") << endmsg;
150                         usleep (20);
151                         tries = 0;
152                 }
153
154                 *st = current;
155                 tries++;
156
157         } while (st->guard1 != st->guard2);
158 }
159
160 bool
161 MIDIClock_Slave::locked () const
162 {
163         return true;
164 }
165
166 bool
167 MIDIClock_Slave::ok() const
168 {
169         return true;
170 }
171
172 bool
173 MIDIClock_Slave::speed_and_position (float& speed, nframes_t& pos)
174 {
175         //std::cerr << "MIDIClock_Slave speed and position() called" << endl;
176         nframes_t now = session.engine().frame_time();
177         nframes_t frame_rate = session.frame_rate();
178         nframes_t elapsed;
179         float speed_now;
180
181         SafeTime last;
182         read_current (&last);
183
184         if (first_midi_clock_time == 0) {
185                 speed = 0;
186                 pos = last.position;
187                 return true;
188         }
189
190         /* no timecode for 1/4 second ? conclude that its stopped */
191
192         if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > frame_rate / 4) {
193                 midi_clock_speed = 0;
194                 pos = last.position;
195                 session.request_locate (pos, false);
196                 session.request_transport_speed (0);
197                 this->stop(*port->input());
198                 reset();
199                 return false;
200         }
201
202         speed_now = (float) ((last.position - first_midi_clock_frame) / (double) (now - first_midi_clock_time));
203
204         cerr << "speed_and_position: speed_now: " << speed_now ;
205         
206         midi_clock_speed = speed_now;
207
208         if (midi_clock_speed == 0.0f) {
209
210                 elapsed = 0;
211
212         } else {
213
214                 /* scale elapsed time by the current MIDI Clock speed */
215
216                 if (last.timestamp && (now > last.timestamp)) {
217                         elapsed = (nframes_t) floor (midi_clock_speed * (now - last.timestamp));
218                 } else {
219                         elapsed = 0; /* XXX is this right? */
220                 }
221         }
222
223         /* now add the most recent timecode value plus the estimated elapsed interval */
224
225         pos =  elapsed + last.position;
226
227         speed = midi_clock_speed;
228         
229         cerr << " final speed: " << speed << " position: " << pos << endl;
230         return true;
231 }
232
233 ARDOUR::nframes_t
234 MIDIClock_Slave::resolution() const
235 {
236         return (nframes_t) one_ppqn_in_frames;
237 }
238
239 void
240 MIDIClock_Slave::reset ()
241 {
242         /* XXX massive thread safety issue here. MTC could
243            be being updated as we call this. but this
244            supposed to be a realtime-safe call.
245         */
246
247         last_inbound_frame = 0;
248         current.guard1++;
249         current.position = 0;
250         current.timestamp = 0;
251         current.guard2++;
252         first_midi_clock_frame = 0;
253         first_midi_clock_time = 0;
254
255         midi_clock_speed = 0;
256 }