2 Copyright (C) 2002-4 Paul Davis
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.
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.
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.
22 #include <sys/types.h>
24 #include <pbd/error.h>
25 #include <pbd/failed_constructor.h>
26 #include <pbd/pthread_utils.h>
28 #include <midi++/port.h>
29 #include <midi++/jack.h>
30 #include <ardour/slave.h>
31 #include <ardour/session.h>
32 #include <ardour/audioengine.h>
33 #include <ardour/cycles.h>
34 #include <ardour/tempo.h>
39 using namespace ARDOUR;
44 MIDIClock_Slave::MIDIClock_Slave (Session& s, MIDI::Port& p, int ppqn)
47 , accumulator_index (0)
48 , average_midi_clock_frame_duration (0.0)
53 for(int i = 0; i < accumulator_size; i++)
57 MIDIClock_Slave::~MIDIClock_Slave()
62 MIDIClock_Slave::rebind (MIDI::Port& p)
64 for (vector<sigc::connection>::iterator i = connections.begin(); i != connections.end(); ++i) {
70 #ifdef DEBUG_MIDI_CLOCK
71 std::cerr << "MIDIClock_Slave: connecting to port " << port->name() << std::endl;
74 connections.push_back (port->input()->timing.connect (mem_fun (*this, &MIDIClock_Slave::update_midi_clock)));
75 connections.push_back (port->input()->start.connect (mem_fun (*this, &MIDIClock_Slave::start)));
76 connections.push_back (port->input()->contineu.connect (mem_fun (*this, &MIDIClock_Slave::contineu)));
77 connections.push_back (port->input()->stop.connect (mem_fun (*this, &MIDIClock_Slave::stop)));
81 MIDIClock_Slave::calculate_one_ppqn_in_frames_at(nframes_t time)
83 const Tempo& current_tempo = session.tempo_map().tempo_at(time);
84 const Meter& current_meter = session.tempo_map().meter_at(time);
85 double frames_per_beat =
86 current_tempo.frames_per_beat(session.frame_rate(),
89 double quarter_notes_per_beat = 4.0 / current_tempo.note_type();
90 double frames_per_quarter_note = frames_per_beat / quarter_notes_per_beat;
92 one_ppqn_in_frames = frames_per_quarter_note / double (ppqn);
96 MIDIClock_Slave::update_midi_clock (Parser& parser, nframes_t timestamp)
98 calculate_one_ppqn_in_frames_at(last_position);
100 // for the first MIDI clock event we don't have any past
101 // data, so we assume a sane tempo
103 current_midi_clock_frame_duration = one_ppqn_in_frames;
105 current_midi_clock_frame_duration = timestamp - last_timestamp;
108 // moving average over incoming intervals
109 accumulator[accumulator_index++] = current_midi_clock_frame_duration;
110 if(accumulator_index == accumulator_size) {
111 accumulator_index = 0;
113 average_midi_clock_frame_duration = 0.0;
114 for(int i = 0; i < accumulator_size; i++) {
115 average_midi_clock_frame_duration += accumulator[i];
117 average_midi_clock_frame_duration /= double(accumulator_size);
119 #ifdef DEBUG_MIDI_CLOCK
121 << " got MIDI Clock message at time " << timestamp
122 << " engine time: " << session.engine().frame_time()
123 << " transport position: " << session.transport_frame()
124 << " real delta: " << current_midi_clock_frame_duration
125 << " reference: " << one_ppqn_in_frames
126 << " average: " << average_midi_clock_frame_duration
128 #endif // DEBUG_MIDI_CLOCK
131 assert(last_timestamp == 0);
132 assert(last_position == 0);
135 last_timestamp = timestamp;
137 // let ardour go after first MIDI Clock Event
139 session.request_transport_speed (1.0);
141 last_position += double(one_ppqn_in_frames);
142 last_timestamp = timestamp;
148 MIDIClock_Slave::start (Parser& parser, nframes_t timestamp)
150 #ifdef DEBUG_MIDI_CLOCK
151 cerr << "MIDIClock_Slave got start message at time " << timestamp << " session time: " << session.engine().frame_time() << endl;
155 cerr << "Did not start because not locked!" << endl;
159 // initialize accumulator to sane values
160 calculate_one_ppqn_in_frames_at(0);
162 for(int i = 0; i < accumulator_size; i++) {
163 accumulator[i] = one_ppqn_in_frames;
174 MIDIClock_Slave::contineu (Parser& parser, nframes_t timestamp)
176 #ifdef DEBUG_MIDI_CLOCK
177 std::cerr << "MIDIClock_Slave got continue message" << endl;
179 start(parser, timestamp);
184 MIDIClock_Slave::stop (Parser& parser, nframes_t timestamp)
186 #ifdef DEBUG_MIDI_CLOCK
187 std::cerr << "MIDIClock_Slave got stop message" << endl;
190 current_midi_clock_frame_duration = 0;
200 MIDIClock_Slave::locked () const
206 MIDIClock_Slave::ok() const
212 MIDIClock_Slave::starting() const
218 MIDIClock_Slave::stop_if_no_more_clock_events(nframes_t& pos, nframes_t now)
220 /* no timecode for 1/4 second ? conclude that its stopped */
221 if (last_timestamp &&
222 now > last_timestamp &&
223 now - last_timestamp > session.frame_rate() / 4) {
224 #ifdef DEBUG_MIDI_CLOCK
225 cerr << "No MIDI Clock frames received for some time, stopping!" << endl;
228 session.request_locate (pos, false);
229 session.request_transport_speed (0);
230 this->stop(*port->input(), now);
239 MIDIClock_Slave::speed_and_position (float& speed, nframes_t& pos)
241 if (!_started || _starting) {
247 nframes_t engine_now = session.engine().frame_time();
249 if (stop_if_no_more_clock_events(pos, engine_now)) {
253 #ifdef DEBUG_MIDI_CLOCK
254 cerr << "speed_and_position: engine time: " << engine_now << " last message timestamp: " << last_timestamp;
258 double speed_double = one_ppqn_in_frames / average_midi_clock_frame_duration;
259 speed = float(speed_double);
261 #ifdef DEBUG_MIDI_CLOCK
262 cerr << " final speed: " << speed;
265 // calculate position
266 if (engine_now > last_timestamp) {
267 // we are in between MIDI clock messages
268 // so we interpolate position according to speed
269 nframes_t elapsed = engine_now - last_timestamp;
270 pos = nframes_t (last_position + double(elapsed) * speed_double);
272 // A new MIDI clock message has arrived this cycle
276 #ifdef DEBUG_MIDI_CLOCK
277 cerr << " transport position engine_now: " << session.transport_frame();
278 cerr << " calculated position: " << pos;
286 MIDIClock_Slave::resolution() const
289 return (nframes_t) one_ppqn_in_frames * ppqn;
293 MIDIClock_Slave::reset ()
299 session.request_locate(0, false);