Merge branch 'windowspush' of https://github.com/mojofunk/ardour into windows
[ardour.git] / libs / ardour / ticker.cc
1 /*
2     Copyright (C) 2008 Hans Baier
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 #include "pbd/compose.h"
20 #include "pbd/stacktrace.h"
21
22 #include "evoral/midi_events.h"
23
24 #include "ardour/async_midi_port.h"
25 #include "ardour/audioengine.h"
26 #include "ardour/midi_buffer.h"
27 #include "ardour/midi_port.h"
28 #include "ardour/ticker.h"
29 #include "ardour/session.h"
30 #include "ardour/tempo.h"
31 #include "ardour/debug.h"
32
33 using namespace ARDOUR;
34 using namespace PBD;
35
36 /** MIDI Clock Position tracking */
37 class MidiClockTicker::Position : public Timecode::BBT_Time
38 {
39 public:
40
41     Position() : speed(0.0f), frame(0) { }
42     ~Position() { }
43     
44     /** Sync timing information taken from the given Session
45      * @return True if timings differed 
46      */
47
48     bool sync (Session* s) {
49
50             bool changed = false;
51             
52             double     sp = s->transport_speed();
53             framecnt_t fr = s->transport_frame();
54             
55             if (speed != sp) {
56                     speed = sp;
57                     changed = true;
58             }
59
60             if (frame != fr) {
61                     frame = fr;
62                     changed = true;
63             }
64
65             /* Midi beats and clocks always gets updated for now */
66
67             s->bbt_time (this->frame, *this);
68
69             const TempoMap& tempo = s->tempo_map();
70
71             const double divisions   = tempo.meter_at(frame).divisions_per_bar();
72             const double divisor     = tempo.meter_at(frame).note_divisor();
73             const double qnote_scale = divisor * 0.25f;
74             double mb;
75
76             /** Midi Beats in terms of Song Position Pointer is equivalent to total
77              * sixteenth notes at 'time' 
78              */
79
80             mb  = (((bars - 1) * divisions) + beats - 1);
81             mb += (double)ticks / (double)Position::ticks_per_beat * qnote_scale;
82             mb *= 16.0f / divisor;
83
84             if (mb != midi_beats) {
85                     midi_beats = mb;
86                     midi_clocks = midi_beats * 6.0f;
87                     changed = true;
88             }
89
90             return changed;
91     }
92
93     double      speed;
94     framecnt_t  frame;
95     double      midi_beats;
96     double      midi_clocks;
97     
98     void print (std::ostream& s) {
99             s << "frames: " << frame << " midi beats: " << midi_beats << " speed: " << speed;
100     }
101 };
102
103
104 MidiClockTicker::MidiClockTicker ()
105         : _ppqn (24)
106         , _last_tick (0.0)
107         , _send_pos (false)
108         , _send_state (false)
109 {
110         _pos.reset (new Position());
111 }
112
113 MidiClockTicker::~MidiClockTicker()
114 {
115         _pos.reset (0);
116 }
117
118 void
119 MidiClockTicker::set_session (Session* s)
120 {
121         SessionHandlePtr::set_session (s);
122         
123          if (_session) {
124                  _session->TransportStateChange.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::transport_state_changed, this));
125                  _session->TransportLooped.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::transport_looped, this));
126                  _session->Located.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::session_located, this));
127
128                  update_midi_clock_port();
129                  _pos->sync (_session);
130          }
131 }
132
133 void
134 MidiClockTicker::session_located()
135 {
136         DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Session Located: %1, speed: %2\n", _session->transport_frame(), _session->transport_speed()));
137
138         if (!_session || !_pos->sync (_session)) {
139                 return;
140         }
141
142         _last_tick = _pos->frame;
143
144         if (!Config->get_send_midi_clock()) {
145                 return;
146         }
147
148         _send_pos = true;
149 }
150
151 void
152 MidiClockTicker::session_going_away ()
153 {
154         SessionHandlePtr::session_going_away();
155         _midi_port.reset ();
156 }
157
158 void
159 MidiClockTicker::update_midi_clock_port()
160 {
161         _midi_port = _session->midi_clock_output_port();
162 }
163
164 void
165 MidiClockTicker::transport_state_changed()
166 {
167         if (_session->exporting()) {
168                 /* no midi clock during export, for now */
169                 return;
170         }
171
172         if (!_session->engine().running()) {
173                 /* Engine stopped, we can't do anything */
174                 return;
175         }
176
177         if (! _pos->sync (_session)) {
178                 return;
179         }
180
181         DEBUG_TRACE (DEBUG::MidiClock,
182                  string_compose ("Transport state change @ %4, speed: %1 position: %2 play loop: %3\n",
183                                                         _pos->speed, _pos->frame, _session->get_play_loop(), _pos->frame)
184         );
185
186         _last_tick = _pos->frame;
187
188         if (! Config->get_send_midi_clock()) {
189                 return;
190         }
191
192         _send_state = true;
193
194         // tick (_pos->frame);
195 }
196
197 void
198 MidiClockTicker::transport_looped()
199 {
200         Location* loop_location = _session->locations()->auto_loop_location();
201         assert(loop_location);
202
203         DEBUG_TRACE (DEBUG::MidiClock,
204                      string_compose ("Transport looped, position: %1, loop start: %2, loop end: %3, play loop: %4\n",
205                                      _session->transport_frame(), loop_location->start(), loop_location->end(), _session->get_play_loop())
206                 );
207
208         // adjust _last_tick, so that the next MIDI clock message is sent
209         // in due time (and the tick interval is still constant)
210
211         framecnt_t elapsed_since_last_tick = loop_location->end() - _last_tick;
212
213         if (loop_location->start() > elapsed_since_last_tick) {
214                 _last_tick = loop_location->start() - elapsed_since_last_tick;
215         } else {
216                 _last_tick = 0;
217         }
218 }
219
220 void
221 MidiClockTicker::tick (const framepos_t& /* transport_frame */, pframes_t nframes)
222 {
223         if (!Config->get_send_midi_clock() || _session == 0 || _midi_port == 0) {
224                 return;
225         }
226
227         if (_send_pos) {
228                 if (_pos->speed == 0.0f) {
229                         send_position_event (llrint (_pos->midi_beats), 0, nframes);
230                 } else if (_pos->speed == 1.0f) {
231                         send_stop_event (0, nframes);
232                         
233                         if (_pos->frame == 0) {
234                                 send_start_event (0, nframes);
235                         } else {
236                                 send_position_event (llrint (_pos->midi_beats), 0, nframes);
237                                 send_continue_event (0, nframes);
238                         }
239                 } else {
240                         /* Varispeed not supported */
241                 }
242
243                 _send_pos = false;
244         }
245
246
247         if (_send_state) {
248                 if (_pos->speed == 1.0f) {
249                         if (_session->get_play_loop()) {
250                                 assert(_session->locations()->auto_loop_location());
251                                 
252                                 if (_pos->frame == _session->locations()->auto_loop_location()->start()) {
253                                         send_start_event (0, nframes);
254                                 } else {
255                                         send_continue_event (0, nframes);
256                                 }
257                                 
258                         } else if (_pos->frame == 0) {
259                                 send_start_event (0, nframes);
260                         } else {
261                                 send_continue_event (0, nframes);
262                         }
263                         
264                         // send_midi_clock_event (0);
265                         
266                 } else if (_pos->speed == 0.0f) {
267                         send_stop_event (0, nframes);
268                         send_position_event (llrint (_pos->midi_beats), 0, nframes);
269                 }
270
271                 _send_state = false;
272         }
273
274         if (_session->transport_speed() != 1.0f) {
275                 /* no varispeed support and nothing to do after this if stopped */
276                 return;
277         }
278
279         const framepos_t end = _pos->frame + nframes;
280         double iter = _last_tick;
281
282         while (true) {
283                 double clock_delta = one_ppqn_in_frames (llrint (iter));
284                 double next_tick   = iter + clock_delta;
285                 frameoffset_t next_tick_offset = llrint (next_tick) - end;
286
287                 DEBUG_TRACE (DEBUG::MidiClock,
288                                  string_compose ("Tick: iter: %1, last tick time: %2, next tick time: %3, offset: %4, cycle length: %5\n",
289                                                  iter, _last_tick, next_tick, next_tick_offset, nframes));
290
291                 if (next_tick_offset >= nframes) {
292                         break;
293                 }
294
295                 if (next_tick_offset >= 0) {
296                         send_midi_clock_event (next_tick_offset, nframes);
297                 }
298
299                 iter = next_tick;
300         }
301
302         _last_tick  = iter;
303         _pos->frame = end;
304 }
305
306 double
307 MidiClockTicker::one_ppqn_in_frames (framepos_t transport_position)
308 {
309         const Tempo& current_tempo = _session->tempo_map().tempo_at (transport_position);
310         double frames_per_beat = current_tempo.frames_per_beat (_session->nominal_frame_rate());
311
312         double quarter_notes_per_beat = 4.0 / current_tempo.note_type();
313         double frames_per_quarter_note = frames_per_beat / quarter_notes_per_beat;
314
315         return frames_per_quarter_note / double (_ppqn);
316 }
317
318 void
319 MidiClockTicker::send_midi_clock_event (pframes_t offset, pframes_t nframes)
320 {
321         if (!_midi_port) {
322                 return;
323         }
324
325         static uint8_t msg = MIDI_CMD_COMMON_CLOCK;
326
327         MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
328         mb.push_back (offset, 1, &msg);
329
330         DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Tick with offset %1\n", offset));
331 }
332
333 void
334 MidiClockTicker::send_start_event (pframes_t offset, pframes_t nframes)
335 {
336         if (!_midi_port) {
337                 return;
338         }
339
340         static uint8_t msg = { MIDI_CMD_COMMON_START };
341         MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
342         mb.push_back (offset, 1, &msg);
343
344         DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Start %1\n", _last_tick));
345 }
346
347 void
348 MidiClockTicker::send_continue_event (pframes_t offset, pframes_t nframes)
349 {
350         if (!_midi_port) {
351                 return;
352         }
353
354         static uint8_t msg = { MIDI_CMD_COMMON_CONTINUE };
355         MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
356         mb.push_back (offset, 1, &msg);
357
358         DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Continue %1\n", _last_tick));
359 }
360
361 void
362 MidiClockTicker::send_stop_event (pframes_t offset, pframes_t nframes)
363 {
364         if (!_midi_port) {
365                 return;
366         }
367
368         static uint8_t msg = MIDI_CMD_COMMON_STOP;
369         MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
370         mb.push_back (offset, 1, &msg);
371
372         DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Stop %1\n", _last_tick));
373 }
374
375 void
376 MidiClockTicker::send_position_event (uint32_t midi_beats, pframes_t offset, pframes_t nframes)
377 {
378         if (!_midi_port) {
379                 return;
380         }
381
382         /* can only use 14bits worth */
383         if (midi_beats > 0x3fff) {
384                 return;
385         }
386
387         /* split midi beats into a 14bit value */
388         MIDI::byte msg[3];
389         msg[0] = MIDI_CMD_COMMON_SONG_POS;
390         msg[1] = midi_beats & 0x007f;
391         msg[2] = midi_beats >> 7;
392
393         MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
394         mb.push_back (offset, 3, &msg[0]);
395
396         DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Song Position Sent: %1 to %2 (events now %3, buf = %4)\n", midi_beats, _midi_port->name(),
397                                                        mb.size(), &mb));
398
399 }