2008-11-26 04:50:29 -05:00
|
|
|
/*
|
2019-08-02 22:01:25 -04:00
|
|
|
* Copyright (C) 2008-2009 Hans Baier <hansfbaier@googlemail.com>
|
|
|
|
* Copyright (C) 2009-2011 Carl Hetherington <carl@carlh.net>
|
|
|
|
* Copyright (C) 2009-2011 David Robillard <d@drobilla.net>
|
|
|
|
* Copyright (C) 2009-2017 Paul Davis <paul@linuxaudiosystems.com>
|
|
|
|
* Copyright (C) 2013-2015 Robin Gareus <robin@gareus.org>
|
|
|
|
* Copyright (C) 2013 Michael Fisher <mfisher31@gmail.com>
|
|
|
|
* Copyright (C) 2015-2016 Nick Mainsbridge <mainsbridge@gmail.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
2008-11-26 18:32:55 -05:00
|
|
|
|
2011-04-22 23:34:42 -04:00
|
|
|
#include "pbd/compose.h"
|
2012-01-27 08:22:55 -05:00
|
|
|
|
2010-07-05 20:16:36 -04:00
|
|
|
#include "evoral/midi_events.h"
|
2012-01-27 08:22:55 -05:00
|
|
|
|
2013-08-07 22:22:11 -04:00
|
|
|
#include "ardour/async_midi_port.h"
|
2012-02-06 10:05:18 -05:00
|
|
|
#include "ardour/audioengine.h"
|
2020-05-29 17:03:46 -04:00
|
|
|
#include "ardour/debug.h"
|
|
|
|
#include "ardour/lmath.h"
|
2013-08-07 22:22:11 -04:00
|
|
|
#include "ardour/midi_buffer.h"
|
|
|
|
#include "ardour/midi_port.h"
|
2008-11-26 04:50:29 -05:00
|
|
|
#include "ardour/session.h"
|
2008-11-26 18:32:55 -05:00
|
|
|
#include "ardour/tempo.h"
|
2020-05-29 17:03:46 -04:00
|
|
|
#include "ardour/ticker.h"
|
2008-11-26 04:50:29 -05:00
|
|
|
|
2009-12-17 13:24:23 -05:00
|
|
|
using namespace ARDOUR;
|
2013-08-01 01:09:42 -04:00
|
|
|
using namespace PBD;
|
2020-11-11 11:31:52 -05:00
|
|
|
using namespace Temporal;
|
2013-07-31 08:02:28 -04:00
|
|
|
|
2023-03-23 20:42:59 -04:00
|
|
|
MidiClockTicker::MidiClockTicker (Session& s)
|
|
|
|
: _session (s)
|
|
|
|
, _midi_port (s.midi_clock_output_port ())
|
|
|
|
, _rolling (false)
|
|
|
|
, _next_tick (0)
|
|
|
|
, _beat_pos (0)
|
|
|
|
, _clock_cnt (0)
|
|
|
|
, _transport_pos (-1)
|
2013-07-31 08:02:28 -04:00
|
|
|
{
|
2020-09-22 15:15:31 -04:00
|
|
|
resync_latency (true);
|
2023-03-23 20:42:59 -04:00
|
|
|
_session.LatencyUpdated.connect_same_thread (_latency_connection, boost::bind (&MidiClockTicker::resync_latency, this, _1));
|
2013-07-31 08:02:28 -04:00
|
|
|
}
|
|
|
|
|
2020-05-29 17:03:46 -04:00
|
|
|
MidiClockTicker::~MidiClockTicker ()
|
2013-07-31 08:02:28 -04:00
|
|
|
{
|
2008-11-26 04:50:29 -05:00
|
|
|
}
|
|
|
|
|
2013-07-29 13:30:37 -04:00
|
|
|
void
|
2020-05-29 21:45:46 -04:00
|
|
|
MidiClockTicker::reset ()
|
2008-11-26 18:32:55 -05:00
|
|
|
{
|
2020-05-29 21:45:46 -04:00
|
|
|
_rolling = false;
|
|
|
|
_next_tick = 0;
|
|
|
|
_beat_pos = 0;
|
|
|
|
_clock_cnt = 0;
|
|
|
|
_transport_pos = -1;
|
2020-07-11 19:50:01 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MidiClockTicker::resync_latency (bool playback)
|
|
|
|
{
|
2023-03-23 20:42:59 -04:00
|
|
|
if (_session.deletion_in_progress() || !playback) {
|
2020-07-11 19:50:01 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
assert (_midi_port);
|
|
|
|
_midi_port->get_connected_latency_range(_mclk_out_latency, true);
|
|
|
|
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("resync latency: %1\n", _mclk_out_latency.max));
|
2008-11-26 18:32:55 -05:00
|
|
|
}
|
2008-11-26 04:50:29 -05:00
|
|
|
|
2013-07-31 08:02:28 -04:00
|
|
|
void
|
2023-04-18 15:39:53 -04:00
|
|
|
MidiClockTicker::tick (samplepos_t start_sample, samplepos_t end_sample, pframes_t n_samples, samplecnt_t pre_roll)
|
2013-07-31 08:02:28 -04:00
|
|
|
{
|
2023-04-18 15:39:53 -04:00
|
|
|
/* silence buffer */
|
2020-05-29 21:45:46 -04:00
|
|
|
_midi_port->cycle_start (n_samples);
|
2013-07-31 08:02:28 -04:00
|
|
|
|
2023-04-18 15:39:53 -04:00
|
|
|
double speed = (end_sample - start_sample) / (double)n_samples;
|
2013-07-31 08:02:28 -04:00
|
|
|
|
2023-04-18 15:39:53 -04:00
|
|
|
if (!Config->get_send_midi_clock () /*|| !TransportMasterManager::instance().current()*/) {
|
2020-05-29 21:45:46 -04:00
|
|
|
if (_rolling) {
|
|
|
|
send_stop_event (0, n_samples);
|
|
|
|
}
|
|
|
|
reset ();
|
|
|
|
goto out;
|
2013-07-31 08:02:28 -04:00
|
|
|
}
|
2013-08-01 14:47:22 -04:00
|
|
|
|
2023-04-18 15:39:53 -04:00
|
|
|
if (speed == 0 && start_sample == 0 && end_sample == 0) {
|
2020-05-29 21:45:46 -04:00
|
|
|
/* test if pre-roll is active, special-case
|
|
|
|
* "start at zero"
|
|
|
|
*/
|
2008-11-26 18:32:55 -05:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
if (pre_roll > 0 && pre_roll >= _mclk_out_latency.max && pre_roll < _mclk_out_latency.max + n_samples) {
|
|
|
|
assert (!_rolling);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
pframes_t pos = pre_roll - _mclk_out_latency.max;
|
|
|
|
_next_tick = one_ppqn_in_samples (0) - _mclk_out_latency.max;
|
2012-02-06 10:05:18 -05:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
DEBUG_TRACE (DEBUG::MidiClock, string_compose (
|
|
|
|
"Preroll Start offset: %1 (port latency: %2, remaining preroll: %3) next %4\n",
|
|
|
|
pos, _mclk_out_latency.max, pre_roll, _next_tick));
|
2010-12-13 20:05:51 -05:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
_rolling = true;
|
|
|
|
_beat_pos = 0;
|
|
|
|
_clock_cnt = 1;
|
|
|
|
_transport_pos = 0;
|
2011-06-01 12:50:12 -04:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
send_start_event (pos, n_samples);
|
|
|
|
send_midi_clock_event (pos, n_samples);
|
|
|
|
}
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
/* Handle case _mclk_out_latency.max > one_ppqn_in_samples (0)
|
|
|
|
* may need to send more than one clock */
|
2013-08-01 01:09:42 -04:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
if (pre_roll > 0 && _next_tick < 0) {
|
|
|
|
assert (_rolling);
|
|
|
|
while (_next_tick < 0 && pre_roll >= -_next_tick && pre_roll < n_samples - _next_tick) {
|
|
|
|
pframes_t pos = pre_roll + _next_tick;
|
|
|
|
_next_tick += one_ppqn_in_samples (llrint (0));
|
2008-11-29 00:40:17 -05:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
DEBUG_TRACE (DEBUG::MidiClock, string_compose (
|
|
|
|
"Preroll Tick offset: %1 (port latency: %2, remaining preroll: %3) next %4\n",
|
|
|
|
pos, _mclk_out_latency.max, pre_roll, _next_tick));
|
2008-11-30 22:49:50 -05:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
send_midi_clock_event (pos, n_samples);
|
2009-10-14 12:10:01 -04:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
if (++_clock_cnt == 6) {
|
|
|
|
_clock_cnt = 0;
|
|
|
|
++_beat_pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-02-06 10:05:18 -05:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
if (pre_roll > 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2012-02-06 10:05:18 -05:00
|
|
|
|
2023-04-18 15:39:53 -04:00
|
|
|
if (speed != 1.0) {
|
|
|
|
if (_rolling) {
|
|
|
|
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Speed != 1 - Stop @ cycle: 1 .. %2 (- preroll %3) nsamples: %4\n",
|
|
|
|
start_sample, end_sample, end_sample, pre_roll, n_samples));
|
|
|
|
send_stop_event (0, n_samples);
|
|
|
|
}
|
|
|
|
reset ();
|
2020-05-29 21:45:46 -04:00
|
|
|
goto out;
|
2012-02-06 10:05:18 -05:00
|
|
|
}
|
2008-11-30 22:07:11 -05:00
|
|
|
|
2023-04-18 15:39:53 -04:00
|
|
|
/* test for discontinuity */
|
|
|
|
if (start_sample != _transport_pos) {
|
|
|
|
if (_rolling) {
|
|
|
|
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Discontinuty start_sample: %1 ticker-pos: %2\n", start_sample, _transport_pos));
|
|
|
|
send_stop_event (0, n_samples);
|
2020-05-29 21:45:46 -04:00
|
|
|
}
|
2023-04-18 15:39:53 -04:00
|
|
|
_rolling = false;
|
|
|
|
_transport_pos = -1;
|
2012-02-06 10:05:18 -05:00
|
|
|
}
|
2008-11-30 22:07:11 -05:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
if (!_rolling) {
|
|
|
|
if (_transport_pos < 0 || _next_tick < start_sample) {
|
|
|
|
/* get the next downbeat */
|
|
|
|
uint32_t beat_pos;
|
|
|
|
samplepos_t clk_pos;
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2021-03-17 15:03:56 -04:00
|
|
|
Temporal::TempoMap::use()->midi_clock_beat_at_or_after (start_sample + _mclk_out_latency.max, clk_pos, beat_pos);
|
2013-08-07 22:22:11 -04:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
_beat_pos = beat_pos;
|
|
|
|
_next_tick = clk_pos - _mclk_out_latency.max;
|
2023-04-18 15:39:53 -04:00
|
|
|
_transport_pos = end_sample;
|
|
|
|
}
|
2013-08-01 14:47:22 -04:00
|
|
|
|
2023-04-18 15:39:53 -04:00
|
|
|
if (_next_tick >= start_sample && _next_tick < end_sample) {
|
|
|
|
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Start rolling at %1 beat-pos: %2\n", _next_tick, _beat_pos));
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2023-04-18 15:39:53 -04:00
|
|
|
_rolling = true;
|
|
|
|
_clock_cnt = 0;
|
2015-10-05 10:17:49 -04:00
|
|
|
|
2023-04-18 15:39:53 -04:00
|
|
|
if (_beat_pos == 0 && _next_tick == 0 && start_sample == 0) {
|
|
|
|
send_start_event (0, n_samples);
|
2013-08-07 22:22:11 -04:00
|
|
|
} else {
|
2023-04-18 15:39:53 -04:00
|
|
|
send_position_event (_beat_pos, 0, n_samples); // consider sending this early
|
|
|
|
send_continue_event (_next_tick - start_sample, n_samples);
|
2013-08-07 22:22:11 -04:00
|
|
|
}
|
2023-04-18 15:39:53 -04:00
|
|
|
} else {
|
|
|
|
goto out;
|
2013-08-07 22:22:11 -04:00
|
|
|
}
|
2013-12-02 23:34:12 -05:00
|
|
|
}
|
2013-08-07 22:22:11 -04:00
|
|
|
|
2023-04-18 15:39:53 -04:00
|
|
|
assert (_rolling);
|
2023-03-23 20:44:56 -04:00
|
|
|
|
2023-04-18 15:39:53 -04:00
|
|
|
while (_next_tick >= start_sample && _next_tick < end_sample) {
|
2020-05-29 21:45:46 -04:00
|
|
|
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Tick @ %1 cycle: %2 .. %3 nsamples: %4, ticker-pos: %5\n",
|
|
|
|
_next_tick, start_sample, end_sample, n_samples, _transport_pos));
|
2023-04-18 15:38:43 -04:00
|
|
|
send_midi_clock_event (_next_tick - start_sample, n_samples);
|
2020-05-29 21:45:46 -04:00
|
|
|
if (++_clock_cnt == 6) {
|
|
|
|
_clock_cnt = 0;
|
|
|
|
++_beat_pos;
|
2012-02-06 10:05:18 -05:00
|
|
|
}
|
2020-05-29 21:45:46 -04:00
|
|
|
_next_tick += one_ppqn_in_samples (llrint (_next_tick));
|
2008-11-30 22:07:11 -05:00
|
|
|
}
|
2013-08-01 11:07:18 -04:00
|
|
|
|
2023-04-18 15:39:53 -04:00
|
|
|
_transport_pos = end_sample;
|
|
|
|
|
|
|
|
out:
|
|
|
|
_midi_port->flush_buffers (n_samples);
|
|
|
|
_midi_port->cycle_end (n_samples);
|
2008-11-30 22:07:11 -05:00
|
|
|
}
|
|
|
|
|
2013-07-29 13:30:37 -04:00
|
|
|
double
|
2020-05-29 21:45:46 -04:00
|
|
|
MidiClockTicker::one_ppqn_in_samples (samplepos_t transport_position) const
|
2008-11-30 22:07:11 -05:00
|
|
|
{
|
2023-06-12 14:36:16 -04:00
|
|
|
Tempo const & tempo (TempoMap::use()->metric_at (timepos_t (transport_position)).tempo());
|
2023-04-18 15:48:54 -04:00
|
|
|
const double samples_per_quarter_note = tempo.samples_per_quarter_note (_session.nominal_sample_rate());
|
2020-05-29 21:45:46 -04:00
|
|
|
return samples_per_quarter_note / 24.0;
|
2008-11-29 00:40:17 -05:00
|
|
|
}
|
|
|
|
|
2013-07-29 13:30:37 -04:00
|
|
|
void
|
2013-08-07 22:22:11 -04:00
|
|
|
MidiClockTicker::send_midi_clock_event (pframes_t offset, pframes_t nframes)
|
2008-11-29 00:40:17 -05:00
|
|
|
{
|
2020-05-29 21:45:46 -04:00
|
|
|
assert (_midi_port);
|
2009-12-01 14:08:59 -05:00
|
|
|
|
2013-12-02 23:34:12 -05:00
|
|
|
static uint8_t msg = MIDI_CMD_COMMON_CLOCK;
|
2010-12-13 11:03:24 -05:00
|
|
|
|
2013-08-07 22:22:11 -04:00
|
|
|
MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
|
2020-09-20 13:14:20 -04:00
|
|
|
mb.push_back (offset, Evoral::MIDI_EVENT, 1, &msg);
|
2013-12-02 23:34:12 -05:00
|
|
|
|
|
|
|
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Tick with offset %1\n", offset));
|
2008-11-29 00:40:17 -05:00
|
|
|
}
|
|
|
|
|
2013-07-29 13:30:37 -04:00
|
|
|
void
|
2013-08-07 22:22:11 -04:00
|
|
|
MidiClockTicker::send_start_event (pframes_t offset, pframes_t nframes)
|
2008-11-29 00:40:17 -05:00
|
|
|
{
|
2020-05-29 21:45:46 -04:00
|
|
|
assert (_midi_port);
|
2011-06-01 12:50:12 -04:00
|
|
|
|
2013-12-02 23:34:12 -05:00
|
|
|
static uint8_t msg = { MIDI_CMD_COMMON_START };
|
2020-05-29 17:03:46 -04:00
|
|
|
MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
|
2020-09-20 13:14:20 -04:00
|
|
|
mb.push_back (offset, Evoral::MIDI_EVENT, 1, &msg);
|
2013-12-02 23:34:12 -05:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Start %1\n", _next_tick));
|
2008-11-29 00:40:17 -05:00
|
|
|
}
|
|
|
|
|
2013-07-29 13:30:37 -04:00
|
|
|
void
|
2013-08-07 22:22:11 -04:00
|
|
|
MidiClockTicker::send_continue_event (pframes_t offset, pframes_t nframes)
|
2008-11-29 00:40:17 -05:00
|
|
|
{
|
2020-05-29 21:45:46 -04:00
|
|
|
assert (_midi_port);
|
2011-06-01 12:50:12 -04:00
|
|
|
|
2013-12-02 23:34:12 -05:00
|
|
|
static uint8_t msg = { MIDI_CMD_COMMON_CONTINUE };
|
2020-05-29 17:03:46 -04:00
|
|
|
MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
|
2020-09-20 13:14:20 -04:00
|
|
|
mb.push_back (offset, Evoral::MIDI_EVENT, 1, &msg);
|
2013-12-02 23:34:12 -05:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Continue %1\n", _next_tick));
|
2008-11-29 00:40:17 -05:00
|
|
|
}
|
|
|
|
|
2013-07-29 13:30:37 -04:00
|
|
|
void
|
2013-08-07 22:22:11 -04:00
|
|
|
MidiClockTicker::send_stop_event (pframes_t offset, pframes_t nframes)
|
2008-11-29 00:40:17 -05:00
|
|
|
{
|
2020-05-29 21:45:46 -04:00
|
|
|
assert (_midi_port);
|
2011-06-01 12:50:12 -04:00
|
|
|
|
2013-12-02 23:34:12 -05:00
|
|
|
static uint8_t msg = MIDI_CMD_COMMON_STOP;
|
2020-05-29 17:03:46 -04:00
|
|
|
MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
|
2020-09-20 13:14:20 -04:00
|
|
|
mb.push_back (offset, Evoral::MIDI_EVENT, 1, &msg);
|
2013-12-02 23:34:12 -05:00
|
|
|
|
2020-05-29 21:45:46 -04:00
|
|
|
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Stop %1\n", _next_tick));
|
2008-11-29 00:40:17 -05:00
|
|
|
}
|
|
|
|
|
2013-07-29 13:19:55 -04:00
|
|
|
void
|
2013-08-07 22:22:11 -04:00
|
|
|
MidiClockTicker::send_position_event (uint32_t midi_beats, pframes_t offset, pframes_t nframes)
|
2013-07-29 13:19:55 -04:00
|
|
|
{
|
2020-05-29 21:45:46 -04:00
|
|
|
assert (_midi_port);
|
2013-07-29 13:19:55 -04:00
|
|
|
|
|
|
|
/* can only use 14bits worth */
|
|
|
|
if (midi_beats > 0x3fff) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* split midi beats into a 14bit value */
|
2013-08-02 19:23:36 -04:00
|
|
|
MIDI::byte msg[3];
|
|
|
|
msg[0] = MIDI_CMD_COMMON_SONG_POS;
|
|
|
|
msg[1] = midi_beats & 0x007f;
|
|
|
|
msg[2] = midi_beats >> 7;
|
2013-07-29 13:19:55 -04:00
|
|
|
|
2013-08-07 22:22:11 -04:00
|
|
|
MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
|
2020-09-20 13:14:20 -04:00
|
|
|
mb.push_back (offset, Evoral::MIDI_EVENT, 3, &msg[0]);
|
2013-08-01 01:09:42 -04:00
|
|
|
|
2020-05-29 17:03:46 -04:00
|
|
|
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Song Position Sent: %1 to %2 (events now %3, buf = %4)\n", midi_beats, _midi_port->name (), mb.size (), &mb));
|
2013-07-29 13:19:55 -04:00
|
|
|
}
|