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