ardour
ticker.cc
Go to the documentation of this file.
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 
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 
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 
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;
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 
106  : _ppqn (24)
107  , _last_tick (0.0)
108  , _send_pos (false)
109  , _send_state (false)
110 {
111  _pos.reset (new Position());
112 }
113 
115 {
116  _pos.reset (0);
117 }
118 
119 void
121 {
123 
124  if (_session) {
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 
130  _pos->sync (_session);
131  }
132 }
133 
134 void
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
154 {
156  _midi_port.reset ();
157 }
158 
159 void
161 {
163 }
164 
165 void
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 
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
200 {
201  Location* loop_location = _session->locations()->auto_loop_location();
202  assert(loop_location);
203 
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 
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
309 {
310  const Tempo& current_tempo = _session->tempo_map().tempo_at (transport_position);
311  double frames_per_beat = current_tempo.frames_per_beat (_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
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
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 
346 }
347 
348 void
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 
360 }
361 
362 void
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 
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 }
boost::shared_ptr< MidiPort > _midi_port
Definition: ticker.h:68
framecnt_t nominal_frame_rate() const
Definition: session.h:367
#define MIDI_CMD_COMMON_CLOCK
Definition: midi_events.h:120
PBD::Signal0< void > TransportStateChange
Definition: session.h:308
virtual void session_going_away()
Location * auto_loop_location() const
Definition: location.cc:1359
double transport_speed() const
Definition: session.h:590
bool running() const
Definition: audioengine.h:130
bool get_play_loop() const
Definition: session.h:342
void session_located()
slot for the signal session::Located
Definition: ticker.cc:135
TempoMap & tempo_map()
Definition: session.h:596
PBD::Signal0< void > Located
Definition: session.h:315
PBD::Signal0< void > TransportLooped
Definition: session.h:312
uint32_t pframes_t
Definition: types.h:61
tuple f
Definition: signals.py:35
framepos_t end() const
Definition: location.h:72
void transport_state_changed()
slot for the signal session::TransportStateChange
Definition: ticker.cc:166
#define MIDI_CMD_COMMON_SONG_POS
Definition: midi_events.h:116
void update_midi_clock_port()
slot for the signal session::MIDIClock_PortChanged
Definition: ticker.cc:160
void transport_looped()
slot for the signal session::TransportLooped
Definition: ticker.cc:199
const Meter & meter_at(framepos_t) const
Definition: tempo.cc:1652
Locations * locations()
Definition: session.h:382
double frames_per_beat(framecnt_t sr) const
Definition: tempo.h:55
#define MIDI_CMD_COMMON_CONTINUE
Definition: midi_events.h:123
void send_start_event(pframes_t offset, pframes_t nframes)
Definition: ticker.cc:335
int64_t framecnt_t
Definition: types.h:76
LIBARDOUR_API RCConfiguration * Config
Definition: globals.cc:119
framepos_t transport_frame() const
Definition: session.h:551
Definition: amp.h:29
void send_stop_event(pframes_t offset, pframes_t nframes)
Definition: ticker.cc:363
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
int64_t framepos_t
Definition: types.h:66
void send_continue_event(pframes_t offset, pframes_t nframes)
Definition: ticker.cc:349
PBD::ScopedConnectionList _session_connections
double note_divisor() const
Definition: tempo.h:71
int64_t frameoffset_t
Definition: types.h:71
LIBARDOUR_API uint64_t MidiClock
Definition: debug.cc:46
void tick(const framepos_t &transport_frames, pframes_t nframes)
Definition: ticker.cc:222
#define MIDI_CMD_COMMON_START
Definition: midi_events.h:122
double one_ppqn_in_frames(framepos_t transport_position)
Definition: ticker.cc:308
bool push_back(const Evoral::MIDIEvent< TimeType > &event)
Definition: midi_buffer.cc:136
void set_session(Session *s)
Definition: ticker.cc:120
Definition: debug.h:30
double divisions_per_bar() const
Definition: tempo.h:70
void print(std::ostream &s)
Definition: ticker.cc:99
bool exporting() const
Definition: session.h:868
framepos_t start() const
Definition: location.h:71
virtual void set_session(ARDOUR::Session *)
#define MIDI_CMD_COMMON_STOP
Definition: midi_events.h:124
void send_midi_clock_event(pframes_t offset, pframes_t nframes)
Definition: ticker.cc:320
void bbt_time(framepos_t when, Timecode::BBT_Time &)
Definition: session_time.cc:47
double note_type() const
Definition: tempo.h:54
ARDOUR::Session * _session
const Tempo & tempo_at(framepos_t) const
Definition: tempo.cc:1617
void send_position_event(uint32_t midi_clocks, pframes_t offset, pframes_t nframes)
Definition: ticker.cc:377
AudioEngine & engine()
Definition: session.h:546
boost::scoped_ptr< Position > _pos
Definition: ticker.h:74
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
virtual ~MidiClockTicker()
Definition: ticker.cc:114
boost::shared_ptr< MidiPort > midi_clock_output_port() const