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