monster commit: transport mgmt changes from 2.X (omnibus edition); make slave use...
[ardour.git] / libs / ardour / ardour / slave.h
1 /*
2     Copyright (C) 2002 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 #ifndef __ardour_slave_h__
21 #define __ardour_slave_h__
22
23 #include <vector>
24
25 #include <jack/jack.h>
26
27 #include <sigc++/signal.h>
28 #include "ardour/types.h"
29 #include "midi++/parser.h"
30 #include "midi++/types.h"
31
32 namespace MIDI {
33         class Port;
34 }
35
36 namespace ARDOUR {
37
38 class TempoMap;
39 class Session;
40
41 /**
42  * @class Slave
43  *
44  * @brief The Slave interface can be used to sync ARDOURs tempo to an external source
45  * like MTC, MIDI Clock, etc.
46  *
47  * The name of the interface may be a bit misleading: A subclass of Slave actually
48  * acts as a time master for ARDOUR, that means ARDOUR will try to follow the
49  * speed and transport position of the implementation of Slave.
50  * Therefore it is rather that class, that makes ARDOUR a slave by connecting it
51  * to its external time master.
52  */
53 class Slave {
54   public:
55         Slave() { }
56         virtual ~Slave() {}
57
58         /**
59          * This is the most important function to implement:
60          * Each process cycle, Session::follow_slave will call this method.
61          *  and after the method call they should
62          *
63          * Session::follow_slave will then try to follow the given
64          * <emph>position</emph> using a delay locked loop (DLL),
65          * starting with the first given transport speed.
66          * If the values of speed and position contradict each other,
67          * ARDOUR will always follow the position and disregard the speed.
68          * Although, a correct speed is important so that ARDOUR
69          * can sync to the master time source quickly.
70          *
71          * For background information on delay locked loops,
72          * see http://www.kokkinizita.net/papers/usingdll.pdf
73          *
74          * The method has the following precondition:
75          * <ul>
76          *   <li>
77          *       Slave::ok() should return true, otherwise playback will stop
78          *       immediately and the method will not be called
79          *   </li>
80          *   <li>
81          *     when the references speed and position are passed into the Slave
82          *     they are uninitialized
83          *   </li>
84          * </ul>
85          *
86          * After the method call the following postconditions should be met:
87          * <ul>
88          *    <li>
89          *       The first position value on transport start should be 0,
90          *       otherwise ARDOUR will try to locate to the new position
91          *       rather than move to it
92          *    </li>
93          *    <li>
94          *      the references speed and position should be assigned
95          *      to the Slaves current requested transport speed
96          *      and transport position.
97          *    </li>
98          *   <li>
99          *     Slave::resolution() should be greater than the maximum distance of
100          *     ARDOURs transport position to the slaves requested transport position.
101          *   </li>
102          *   <li>Slave::locked() should return true, otherwise Session::no_roll will be called</li>
103          *   <li>Slave::starting() should be false, otherwise the transport will not move until it becomes true</li>     *
104          * </ul>
105          *
106          * @param speed - The transport speed requested
107          * @param position - The transport position requested
108          * @return - The return value is currently ignored (see Session::follow_slave)
109          */
110         virtual bool speed_and_position (double& speed, nframes64_t& position) = 0;
111
112         /**
113          * reports to ARDOUR whether the Slave is currently synced to its external
114          * time source.
115          *
116          * @return - when returning false, the transport will stop rolling
117          */
118         virtual bool locked() const = 0;
119
120         /**
121          * reports to ARDOUR whether the slave is in a sane state
122          *
123          * @return - when returning false, the transport will be stopped and the slave
124          * disconnected from ARDOUR.
125          */
126         virtual bool ok() const = 0;
127
128         /**
129          * reports to ARDOUR whether the slave is in the process of starting
130          * to roll
131          *
132          * @return - when returning false, transport will not move until this method returns true
133          */
134         virtual bool starting() const { return false; }
135
136         /**
137          * @return - the timing resolution of the Slave - If the distance of ARDOURs transport
138          * to the slave becomes greater than the resolution, sound will stop
139          */
140         virtual nframes_t resolution() const = 0;
141
142         /**
143          * @return - when returning true, ARDOUR will wait for one second before transport
144          * starts rolling
145          */
146         virtual bool requires_seekahead () const = 0;
147
148         /**
149          * @return - when returning true, ARDOUR will use transport speed 1.0 no matter what
150          *           the slave returns
151          */
152         virtual bool is_always_synced() const { return false; }
153
154         /**
155          * @return - whether ARDOUR should use the slave speed without any adjustments
156          */
157         virtual bool give_slave_full_control_over_transport_speed() const { return false; }
158 };
159
160 /// We need this wrapper for testability, it's just too hard to mock up a session class
161 class ISlaveSessionProxy {
162   public:
163         virtual TempoMap& tempo_map()                 const   { return *((TempoMap *) 0); }
164         virtual nframes_t   frame_rate()                const   { return 0; }
165         virtual nframes64_t audible_frame ()            const   { return 0; }
166         virtual nframes64_t transport_frame ()          const   { return 0; }
167         virtual nframes_t   frames_since_cycle_start () const   { return 0; }
168         virtual nframes64_t frame_time ()               const   { return 0; }
169
170         virtual void request_locate (nframes64_t /*frame*/, bool with_roll = false) {
171                 (void) with_roll;
172         }
173         virtual void request_transport_speed (double /*speed*/)                   {}
174 };
175
176
177 /// The Session Proxy for use in real Ardour
178 class SlaveSessionProxy : public ISlaveSessionProxy {
179         Session&    session;
180
181   public:
182         SlaveSessionProxy(Session &s) : session(s) {}
183
184         TempoMap&   tempo_map()                 const;
185         nframes_t   frame_rate()                const;
186         nframes64_t audible_frame ()            const;
187         nframes64_t transport_frame ()          const;
188         nframes_t   frames_since_cycle_start () const;
189         nframes64_t frame_time ()               const;
190
191         void request_locate (nframes64_t frame, bool with_roll = false);
192         void request_transport_speed (double speed);
193 };
194
195 struct SafeTime {
196     int guard1;
197     nframes_t   position;
198     nframes_t   timestamp;
199     int guard2;
200
201         SafeTime() {
202                 guard1 = 0;
203                 guard2 = 0;
204                 timestamp = 0;
205         }
206 };
207
208 class MTC_Slave : public Slave, public sigc::trackable {
209   public:
210         MTC_Slave (Session&, MIDI::Port&);
211         ~MTC_Slave ();
212
213         void rebind (MIDI::Port&);
214         bool speed_and_position (double&, nframes64_t&);
215
216         bool locked() const;
217         bool ok() const;
218         void handle_locate (const MIDI::byte*);
219
220         nframes_t resolution() const;
221         bool requires_seekahead () const { return true; }
222
223   private:
224         Session&    session;
225         MIDI::Port* port;
226         std::vector<sigc::connection> connections;
227         bool        can_notify_on_unknown_rate;
228
229         SafeTime    current;
230         nframes_t   mtc_frame;               /* current time */
231         nframes_t   last_inbound_frame;      /* when we got it; audio clocked */
232         MIDI::byte  last_mtc_fps_byte;
233
234         double      mtc_speed;
235         nframes_t   first_mtc_frame;
236         nframes_t   first_mtc_time;
237
238         static const int32_t accumulator_size = 128;
239         double   accumulator[accumulator_size];
240         int32_t accumulator_index;
241         bool    have_first_accumulated_speed;
242
243         void reset ();
244         void update_mtc_qtr (MIDI::Parser&);
245         void update_mtc_time (const MIDI::byte *, bool);
246         void update_mtc_status (MIDI::Parser::MTC_Status);
247         void read_current (SafeTime *) const;
248 };
249
250 class MIDIClock_Slave : public Slave, public sigc::trackable {
251   public:
252         MIDIClock_Slave (Session&, MIDI::Port&, int ppqn = 24);
253
254         /// Constructor for unit tests
255         MIDIClock_Slave (ISlaveSessionProxy* session_proxy, int ppqn = 24);
256         ~MIDIClock_Slave ();
257
258         void rebind (MIDI::Port&);
259         bool speed_and_position (double&, nframes64_t&);
260
261         bool locked() const;
262         bool ok() const;
263         bool starting() const;
264
265         nframes_t resolution() const;
266         bool requires_seekahead () const { return false; }
267         bool give_slave_full_control_over_transport_speed() const { return true; }
268
269         void set_bandwidth (double a_bandwith) { bandwidth = a_bandwith; }
270
271   private:
272         ISlaveSessionProxy* session;
273         MIDI::Port* port;
274         std::vector<sigc::connection> connections;
275
276         /// pulses per quarter note for one MIDI clock frame (default 24)
277         int         ppqn;
278
279         /// the duration of one ppqn in frame time
280         double      one_ppqn_in_frames;
281
282         /// the timestamp of the first MIDI clock message
283         nframes_t   first_timestamp;
284
285         /// the time stamp and should-be transport position of the last inbound MIDI clock message
286         nframes_t   last_timestamp;
287         double      should_be_position;
288
289         /// the number of midi clock messages received (zero-based)
290         /// since start
291         long midi_clock_count;
292
293         //the delay locked loop (DLL), see www.kokkinizita.net/papers/usingdll.pdf
294
295         /// time at the beginning of the MIDI clock frame
296         double t0;
297
298         /// calculated end of the MIDI clock frame
299         double t1;
300
301         /// loop error = real value - expected value
302         double e;
303
304         /// second order loop error
305         double e2;
306
307         /// DLL filter bandwidth
308         double bandwidth;
309
310         /// DLL filter coefficients
311         double b, c, omega;
312
313         void reset ();
314         void start (MIDI::Parser& parser, nframes64_t timestamp);
315         void contineu (MIDI::Parser& parser, nframes64_t timestamp);
316         void stop (MIDI::Parser& parser, nframes64_t timestamp);
317         void position (MIDI::Parser& parser, MIDI::byte* message, size_t size);
318         // we can't use continue because it is a C++ keyword
319         void calculate_one_ppqn_in_frames_at(nframes64_t time);
320         nframes64_t calculate_song_position(uint16_t song_position_in_sixteenth_notes);
321         void calculate_filter_coefficients();
322         void update_midi_clock (MIDI::Parser& parser, nframes64_t timestamp);
323         void read_current (SafeTime *) const;
324         bool stop_if_no_more_clock_events(nframes64_t& pos, nframes64_t now);
325
326         /// whether transport should be rolling
327         bool _started;
328
329         /// is true if the MIDI Start message has just been received until
330         /// the first MIDI Clock Event
331         bool _starting;
332 };
333
334 class ADAT_Slave : public Slave
335 {
336   public:
337         ADAT_Slave () {}
338         ~ADAT_Slave () {}
339
340         bool speed_and_position (double& speed, nframes64_t& pos) {
341                 speed = 0;
342                 pos = 0;
343                 return false;
344         }
345
346         bool locked() const { return false; }
347         bool ok() const { return false; }
348         nframes_t resolution() const { return 1; }
349         bool requires_seekahead () const { return true; }
350 };
351
352 class JACK_Slave : public Slave
353 {
354   public:
355         JACK_Slave (jack_client_t*);
356         ~JACK_Slave ();
357
358         bool speed_and_position (double& speed, nframes64_t& pos);
359
360         bool starting() const { return _starting; }
361         bool locked() const;
362         bool ok() const;
363         nframes_t resolution() const { return 1; }
364         bool requires_seekahead () const { return false; }
365         void reset_client (jack_client_t* jack);
366         bool is_always_synced() const { return true; }
367
368   private:
369         jack_client_t* jack;
370         double speed;
371         bool _starting;
372 };
373
374 } /* namespace */
375
376 #endif /* __ardour_slave_h__ */