Housekeeping, code style
[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 "midi++/port.h"
23 #include "midi++/jack_midi_port.h"
24 #include "midi++/manager.h"
25
26 #include "evoral/midi_events.h"
27
28 #include "ardour/audioengine.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
36 MidiClockTicker::MidiClockTicker ()
37         : _midi_port (0)
38         , _ppqn (24)
39         , _last_tick (0.0)
40 {
41 }
42
43 void
44 MidiClockTicker::set_session (Session* s)
45 {
46         SessionHandlePtr::set_session (s);
47         
48          if (_session) {
49                  _session->TransportStateChange.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::transport_state_changed, this));
50                  _session->PositionChanged.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::position_changed, this, _1));
51                  _session->TransportLooped.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::transport_looped, this));
52                  update_midi_clock_port();
53          }
54 }
55
56 void
57 MidiClockTicker::session_going_away ()
58 {
59         SessionHandlePtr::session_going_away();
60         _midi_port = 0;
61 }
62
63 void
64 MidiClockTicker::update_midi_clock_port()
65 {
66         _midi_port = MIDI::Manager::instance()->midi_clock_output_port();
67 }
68
69 void
70 MidiClockTicker::transport_state_changed()
71 {
72         if (_session->exporting()) {
73                 /* no midi clock during export, for now */
74                 return;
75         }
76
77         if (!_session->engine().running()) {
78                 /* Engine stopped, we can't do anything */
79                 return;
80         }
81
82         float      speed    = _session->transport_speed();
83         framepos_t position = _session->transport_frame();
84
85         DEBUG_TRACE (PBD::DEBUG::MidiClock,
86                  string_compose ("Transport state change @ %4, speed: %1 position: %2 play loop: %3\n", speed, position, _session->get_play_loop(), position)
87         );
88
89         if (speed == 1.0f) {
90                 _last_tick = position;
91
92                 if (!Config->get_send_midi_clock())
93                         return;
94
95                 if (_session->get_play_loop()) {
96                         assert(_session->locations()->auto_loop_location());
97                         if (position == _session->locations()->auto_loop_location()->start()) {
98                                 send_start_event(0);
99                         } else {
100                                 send_continue_event(0);
101                         }
102                 } else if (position == 0) {
103                         send_start_event(0);
104                 } else {
105                         send_continue_event(0);
106                 }
107
108                 send_midi_clock_event(0);
109
110         } else if (speed == 0.0f) {
111                 if (!Config->get_send_midi_clock())
112                         return;
113
114                 send_stop_event(0);
115                 send_position_event (position, 0);
116         }
117
118         tick (position);
119 }
120
121 void
122 MidiClockTicker::position_changed (framepos_t position)
123 {
124         const double speed = _session->transport_speed();
125         DEBUG_TRACE (PBD::DEBUG::MidiClock, string_compose ("Transport Position Change: %1, speed: %2\n", position, speed));
126
127         if (speed == 0.0f && Config->get_send_midi_clock()) {
128                 send_position_event (position, 0);
129         }
130
131         _last_tick = position;
132 }
133
134 void
135 MidiClockTicker::transport_looped()
136 {
137         Location* loop_location = _session->locations()->auto_loop_location();
138         assert(loop_location);
139
140         DEBUG_TRACE (PBD::DEBUG::MidiClock,
141                      string_compose ("Transport looped, position: %1, loop start: %2, loop end: %3, play loop: %4\n",
142                                      _session->transport_frame(), loop_location->start(), loop_location->end(), _session->get_play_loop())
143                 );
144
145         // adjust _last_tick, so that the next MIDI clock message is sent
146         // in due time (and the tick interval is still constant)
147
148         framecnt_t elapsed_since_last_tick = loop_location->end() - _last_tick;
149
150         if (loop_location->start() > elapsed_since_last_tick) {
151                 _last_tick = loop_location->start() - elapsed_since_last_tick;
152         } else {
153                 _last_tick = 0;
154         }
155 }
156
157 void
158 MidiClockTicker::tick (const framepos_t& transport_frame)
159 {
160         if (!Config->get_send_midi_clock() || _session == 0 || _session->transport_speed() != 1.0f || _midi_port == 0) {
161                 return;
162         }
163
164         while (true) {
165                 double next_tick = _last_tick + one_ppqn_in_frames (transport_frame);
166                 frameoffset_t next_tick_offset = llrint (next_tick) - transport_frame;
167
168                 MIDI::JackMIDIPort* mp = dynamic_cast<MIDI::JackMIDIPort*> (_midi_port);
169                 
170                 /*
171                 DEBUG_TRACE (PBD::DEBUG::MidiClock,
172                              string_compose ("Transport: %1, last tick time: %2, next tick time: %3, offset: %4, cycle length: %5\n",
173                                              transport_frame, _last_tick, next_tick, next_tick_offset, mp ? mp->nframes_this_cycle() : 0));
174                 */
175
176                 if (!mp || (next_tick_offset >= mp->nframes_this_cycle())) {
177                         break;
178                 }
179
180                 if (next_tick_offset >= 0) {
181                         send_midi_clock_event (next_tick_offset);
182                 }
183
184                 _last_tick = next_tick;
185         }
186 }
187
188 double
189 MidiClockTicker::one_ppqn_in_frames (framepos_t transport_position)
190 {
191         const Tempo& current_tempo = _session->tempo_map().tempo_at (transport_position);
192         double frames_per_beat = current_tempo.frames_per_beat (_session->nominal_frame_rate());
193
194         double quarter_notes_per_beat = 4.0 / current_tempo.note_type();
195         double frames_per_quarter_note = frames_per_beat / quarter_notes_per_beat;
196
197         return frames_per_quarter_note / double (_ppqn);
198 }
199
200 void
201 MidiClockTicker::send_midi_clock_event (pframes_t offset)
202 {
203         if (!_midi_port) {
204                 return;
205         }
206
207         // DEBUG_TRACE (PBD::DEBUG::MidiClock, string_compose ("Tick with offset %1\n", offset));
208
209         static uint8_t _midi_clock_tick[1] = { MIDI_CMD_COMMON_CLOCK };
210         _midi_port->write (_midi_clock_tick, 1, offset);
211 }
212
213 void
214 MidiClockTicker::send_start_event (pframes_t offset)
215 {
216         if (!_midi_port) {
217                 return;
218         }
219
220         DEBUG_TRACE (PBD::DEBUG::MidiClock, string_compose ("Start %1\n", _last_tick));
221
222         static uint8_t _midi_clock_tick[1] = { MIDI_CMD_COMMON_START };
223         _midi_port->write (_midi_clock_tick, 1, offset);
224 }
225
226 void
227 MidiClockTicker::send_continue_event (pframes_t offset)
228 {
229         if (!_midi_port) {
230                 return;
231         }
232
233         DEBUG_TRACE (PBD::DEBUG::MidiClock, string_compose ("Continue %1\n", _last_tick));
234
235         static uint8_t _midi_clock_tick[1] = { MIDI_CMD_COMMON_CONTINUE };
236         _midi_port->write (_midi_clock_tick, 1, offset);
237 }
238
239 void
240 MidiClockTicker::send_stop_event (pframes_t offset)
241 {
242         if (!_midi_port) {
243                 return;
244         }
245
246         DEBUG_TRACE (PBD::DEBUG::MidiClock, string_compose ("Stop %1\n", _last_tick));
247
248         static uint8_t _midi_clock_tick[1] = { MIDI_CMD_COMMON_STOP };
249         _midi_port->write (_midi_clock_tick, 1, offset);
250 }
251
252 void
253 MidiClockTicker::send_position_event (framepos_t transport_position, pframes_t offset)
254 {
255         if (_midi_port == 0 || _session == 0 || _session->engine().freewheeling()) {
256                 return;
257         }
258
259         const TempoMap& tempo = _session->tempo_map();
260
261         Timecode::BBT_Time time;
262         _session->bbt_time (transport_position, time);
263         const double beats_per_bar = tempo.meter_at(transport_position).divisions_per_bar();
264
265         /* Midi Beats in terms of Song Position Pointer is equivalent to total
266            sixteenth notes at 'time' */
267         const uint32_t midi_beats = 4 * (((time.bars - 1) * beats_per_bar) + time.beats - 1);
268
269         /* can only use 14bits worth */
270         if (midi_beats > 0x3fff) {
271                 return;
272         }
273
274         /* split midi beats into a 14bit value */
275         MIDI::byte msg[3] = {
276                 MIDI_CMD_COMMON_SONG_POS,
277                 midi_beats & 0x007f,
278                 midi_beats & 0x3f80
279         };
280
281         DEBUG_TRACE (PBD::DEBUG::MidiClock, string_compose ("Song Position: %1\n", midi_beats));
282
283         _midi_port->midimsg (msg, sizeof (msg), offset);
284 }