Overhaul MIDI Clock generator
* Fix absolute time alignment, sync clock ticks on exact beats * Fix start/cont sync to MIDI Clock **Beats** (MIDI Clock / 6) * Send timecode from engine (not session) - vari-speed independent * Special case MClk port, don't /resample/ * Support pre-roll, sync start.
This commit is contained in:
parent
29a6bfd3e7
commit
ef94663d1c
|
@ -289,6 +289,7 @@ public:
|
|||
void process (pframes_t nframes);
|
||||
|
||||
void send_ltc_for_cycle (samplepos_t, samplepos_t, pframes_t);
|
||||
void send_mclk_for_cycle (samplepos_t, samplepos_t, pframes_t, samplecnt_t);
|
||||
|
||||
BufferSet& get_silent_buffers (ChanCount count = ChanCount::ZERO);
|
||||
BufferSet& get_noinplace_buffers (ChanCount count = ChanCount::ZERO);
|
||||
|
|
|
@ -37,60 +37,36 @@ namespace ARDOUR
|
|||
class Session;
|
||||
class MidiPort;
|
||||
|
||||
class LIBARDOUR_API MidiClockTicker : public SessionHandlePtr, boost::noncopyable
|
||||
class LIBARDOUR_API MidiClockTicker : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
MidiClockTicker ();
|
||||
MidiClockTicker (Session*);
|
||||
virtual ~MidiClockTicker ();
|
||||
|
||||
void tick (const samplepos_t& transport_samples, pframes_t nframes);
|
||||
|
||||
bool has_midi_port () const
|
||||
{
|
||||
return _midi_port != 0;
|
||||
}
|
||||
|
||||
void set_session (Session* s);
|
||||
void session_going_away ();
|
||||
|
||||
/// slot for the signal session::MIDIClock_PortChanged
|
||||
void update_midi_clock_port ();
|
||||
|
||||
/// slot for the signal session::TransportStateChange
|
||||
void transport_state_changed ();
|
||||
|
||||
/// slot for the signal session::TransportLooped
|
||||
void transport_looped ();
|
||||
|
||||
/// slot for the signal session::Located
|
||||
void session_located ();
|
||||
|
||||
/// pulses per quarter note (default 24)
|
||||
void set_ppqn (int ppqn)
|
||||
{
|
||||
_ppqn = ppqn;
|
||||
}
|
||||
void tick (samplepos_t, samplepos_t, pframes_t, samplecnt_t);
|
||||
|
||||
private:
|
||||
boost::shared_ptr<MidiPort> _midi_port;
|
||||
|
||||
int _ppqn;
|
||||
double _last_tick;
|
||||
bool _send_pos;
|
||||
bool _send_state;
|
||||
|
||||
class Position;
|
||||
boost::scoped_ptr<Position> _pos;
|
||||
|
||||
double one_ppqn_in_samples (samplepos_t transport_position);
|
||||
void reset ();
|
||||
double one_ppqn_in_samples (samplepos_t transport_position) const;
|
||||
|
||||
void send_midi_clock_event (pframes_t offset, pframes_t nframes);
|
||||
void send_start_event (pframes_t offset, pframes_t nframes);
|
||||
void send_continue_event (pframes_t offset, pframes_t nframes);
|
||||
void send_stop_event (pframes_t offset, pframes_t nframes);
|
||||
void send_position_event (uint32_t midi_clocks, pframes_t offset, pframes_t nframes);
|
||||
|
||||
bool _rolling;
|
||||
double _next_tick;
|
||||
uint32_t _beat_pos;
|
||||
uint32_t _clock_cnt;
|
||||
samplepos_t _transport_pos;
|
||||
|
||||
ARDOUR::Session* _session;
|
||||
LatencyRange _mclk_out_latency;
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
// namespace
|
||||
|
||||
#endif /* __libardour_ticker_h__ */
|
||||
|
|
|
@ -470,6 +470,7 @@ AudioEngine::process_callback (pframes_t nframes)
|
|||
Freewheel (nframes);
|
||||
} else {
|
||||
samplepos_t start_sample = _session->transport_sample ();
|
||||
samplecnt_t pre_roll = _session->remaining_latency_preroll ();
|
||||
|
||||
if (Port::cycle_nframes () <= nframes) {
|
||||
_session->process (Port::cycle_nframes ());
|
||||
|
@ -495,6 +496,8 @@ AudioEngine::process_callback (pframes_t nframes)
|
|||
/* send timecode for current cycle */
|
||||
samplepos_t end_sample = _session->transport_sample ();
|
||||
_session->send_ltc_for_cycle (start_sample, end_sample, nframes);
|
||||
/* and MIDI Clock */
|
||||
_session->send_mclk_for_cycle (start_sample, end_sample, nframes, pre_roll);
|
||||
}
|
||||
|
||||
if (_freewheeling) {
|
||||
|
|
|
@ -78,7 +78,7 @@ MidiPortManager::create_ports ()
|
|||
p = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("MTC out"));
|
||||
_mtc_output_port= boost::dynamic_pointer_cast<MidiPort> (p);
|
||||
|
||||
p = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("MIDI Clock out"));
|
||||
p = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("MIDI Clock out"), false, TransportGenerator);
|
||||
_midi_clock_output_port= boost::dynamic_pointer_cast<MidiPort> (p);
|
||||
}
|
||||
|
||||
|
|
|
@ -7064,6 +7064,12 @@ Session::maybe_update_tempo_from_midiclock_tempo (float bpm)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Session::send_mclk_for_cycle (samplepos_t start_sample, samplepos_t end_sample, pframes_t n_samples, samplecnt_t pre_roll)
|
||||
{
|
||||
midi_clock->tick (start_sample, end_sample, n_samples, pre_roll);
|
||||
}
|
||||
|
||||
void
|
||||
Session::set_had_destructive_tracks (bool yn)
|
||||
{
|
||||
|
|
|
@ -147,12 +147,7 @@ Session::process (pframes_t nframes)
|
|||
*/
|
||||
|
||||
try {
|
||||
if (!_silent && !_engine.freewheeling() && Config->get_send_midi_clock() && (transport_speed() == 1.0f || transport_speed() == 0.0f) && midi_clock->has_midi_port()) {
|
||||
midi_clock->tick (transport_at_start, nframes);
|
||||
}
|
||||
|
||||
_scene_changer->run (transport_at_start, transport_at_start + nframes);
|
||||
|
||||
} catch (...) {
|
||||
/* don't bother with a message */
|
||||
}
|
||||
|
|
|
@ -277,8 +277,7 @@ Session::post_engine_init ()
|
|||
/* MidiClock requires a tempo map */
|
||||
|
||||
delete midi_clock;
|
||||
midi_clock = new MidiClockTicker ();
|
||||
midi_clock->set_session (this);
|
||||
midi_clock = new MidiClockTicker (this);
|
||||
|
||||
/* crossfades require sample rate knowledge */
|
||||
|
||||
|
|
|
@ -40,290 +40,179 @@
|
|||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
|
||||
/** MIDI Clock Position tracking */
|
||||
class MidiClockTicker::Position : public Timecode::BBT_Time
|
||||
MidiClockTicker::MidiClockTicker (Session* s)
|
||||
{
|
||||
public:
|
||||
Position ()
|
||||
: speed (0.0f)
|
||||
, sample (0)
|
||||
, midi_beats (0)
|
||||
{
|
||||
}
|
||||
~Position () {}
|
||||
|
||||
/** Sync timing information taken from the given Session
|
||||
* @return True if timings differed
|
||||
*/
|
||||
|
||||
bool sync (Session* s)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
double sp = s->transport_speed ();
|
||||
samplecnt_t fr = s->transport_sample ();
|
||||
|
||||
if (speed != sp) {
|
||||
speed = sp;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (sample != fr) {
|
||||
sample = fr;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
/* Midi beats and clocks always gets updated for now */
|
||||
|
||||
s->bbt_time (this->sample, *this);
|
||||
|
||||
const TempoMap& tempo = s->tempo_map ();
|
||||
const Meter& meter = tempo.meter_at_sample (sample);
|
||||
|
||||
const double divisions = meter.divisions_per_bar ();
|
||||
const double divisor = meter.note_divisor ();
|
||||
const double qnote_scale = divisor * 0.25f;
|
||||
double mb;
|
||||
|
||||
/** Midi Beats in terms of Song Position Pointer is equivalent to total
|
||||
* sixteenth notes at 'time'
|
||||
*/
|
||||
|
||||
mb = (((bars - 1) * divisions) + beats - 1);
|
||||
mb += (double)ticks / (double)Position::ticks_per_beat * qnote_scale;
|
||||
mb *= 16.0f / divisor;
|
||||
|
||||
if (mb != midi_beats) {
|
||||
midi_beats = mb;
|
||||
midi_clocks = midi_beats * 6.0f;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
double speed;
|
||||
samplecnt_t sample;
|
||||
double midi_beats;
|
||||
double midi_clocks;
|
||||
|
||||
void print (std::ostream& s)
|
||||
{
|
||||
s << "samples: " << sample << " midi beats: " << midi_beats << " speed: " << speed;
|
||||
}
|
||||
};
|
||||
|
||||
MidiClockTicker::MidiClockTicker ()
|
||||
: _ppqn (24)
|
||||
, _last_tick (0.0)
|
||||
, _send_pos (false)
|
||||
, _send_state (false)
|
||||
{
|
||||
_pos.reset (new Position ());
|
||||
_session = s;
|
||||
_midi_port = s->midi_clock_output_port ();
|
||||
reset ();
|
||||
}
|
||||
|
||||
MidiClockTicker::~MidiClockTicker ()
|
||||
{
|
||||
_pos.reset (0);
|
||||
}
|
||||
|
||||
void
|
||||
MidiClockTicker::set_session (Session* s)
|
||||
MidiClockTicker::reset ()
|
||||
{
|
||||
SessionHandlePtr::set_session (s);
|
||||
|
||||
if (_session) {
|
||||
_session->TransportStateChange.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::transport_state_changed, this));
|
||||
_session->TransportLooped.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::transport_looped, this));
|
||||
_session->Located.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::session_located, this));
|
||||
|
||||
update_midi_clock_port ();
|
||||
_pos->sync (_session);
|
||||
}
|
||||
_rolling = false;
|
||||
_next_tick = 0;
|
||||
_beat_pos = 0;
|
||||
_clock_cnt = 0;
|
||||
_transport_pos = -1;
|
||||
}
|
||||
|
||||
void
|
||||
MidiClockTicker::session_located ()
|
||||
MidiClockTicker::tick (samplepos_t start_sample, samplepos_t end_sample, pframes_t n_samples, samplecnt_t pre_roll)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Session Located: %1, speed: %2\n", _session->transport_sample (), _session->transport_speed ()));
|
||||
/* silence buffer */
|
||||
_midi_port->cycle_start (n_samples);
|
||||
|
||||
if (!_session || !_pos->sync (_session)) {
|
||||
return;
|
||||
double speed = (end_sample - start_sample) / (double)n_samples;
|
||||
|
||||
if (!Config->get_send_midi_clock () /*|| !TransportMasterManager::instance().current()*/) {
|
||||
if (_rolling) {
|
||||
send_stop_event (0, n_samples);
|
||||
}
|
||||
reset ();
|
||||
goto out;
|
||||
}
|
||||
|
||||
_last_tick = _pos->sample;
|
||||
// TODO: cache
|
||||
_midi_port->get_connected_latency_range (_mclk_out_latency, true);
|
||||
|
||||
if (!Config->get_send_midi_clock ()) {
|
||||
return;
|
||||
if (speed == 0 && start_sample == 0 && end_sample == 0) {
|
||||
/* test if pre-roll is active, special-case
|
||||
* "start at zero"
|
||||
*/
|
||||
|
||||
if (pre_roll > 0 && pre_roll >= _mclk_out_latency.max && pre_roll < _mclk_out_latency.max + n_samples) {
|
||||
assert (!_rolling);
|
||||
|
||||
pframes_t pos = pre_roll - _mclk_out_latency.max;
|
||||
_next_tick = one_ppqn_in_samples (0) - _mclk_out_latency.max;
|
||||
|
||||
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));
|
||||
|
||||
_rolling = true;
|
||||
_beat_pos = 0;
|
||||
_clock_cnt = 1;
|
||||
_transport_pos = 0;
|
||||
|
||||
send_start_event (pos, n_samples);
|
||||
send_midi_clock_event (pos, n_samples);
|
||||
}
|
||||
|
||||
/* Handle case _mclk_out_latency.max > one_ppqn_in_samples (0)
|
||||
* may need to send more than one clock */
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
send_midi_clock_event (pos, n_samples);
|
||||
|
||||
if (++_clock_cnt == 6) {
|
||||
_clock_cnt = 0;
|
||||
++_beat_pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pre_roll > 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
_send_pos = true;
|
||||
}
|
||||
|
||||
void
|
||||
MidiClockTicker::session_going_away ()
|
||||
{
|
||||
SessionHandlePtr::session_going_away ();
|
||||
_midi_port.reset ();
|
||||
}
|
||||
|
||||
void
|
||||
MidiClockTicker::update_midi_clock_port ()
|
||||
{
|
||||
_midi_port = _session->midi_clock_output_port ();
|
||||
}
|
||||
|
||||
void
|
||||
MidiClockTicker::transport_state_changed ()
|
||||
{
|
||||
if (_session->exporting ()) {
|
||||
/* no midi clock during export, for now */
|
||||
return;
|
||||
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 ();
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!_session->engine ().running ()) {
|
||||
/* Engine stopped, we can't do anything */
|
||||
return;
|
||||
/* 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);
|
||||
}
|
||||
_rolling = false;
|
||||
_transport_pos = -1;
|
||||
}
|
||||
|
||||
if (!_pos->sync (_session)) {
|
||||
return;
|
||||
}
|
||||
if (!_rolling) {
|
||||
if (_transport_pos < 0 || _next_tick < start_sample) {
|
||||
/* get the next downbeat */
|
||||
uint32_t beat_pos;
|
||||
samplepos_t clk_pos;
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiClock,
|
||||
string_compose ("Transport state change @ %4, speed: %1 position: %2 play loop: %3\n",
|
||||
_pos->speed, _pos->sample, _session->get_play_loop (), _pos->sample));
|
||||
_session->tempo_map ().midi_clock_beat_at_of_after (start_sample + _mclk_out_latency.max, clk_pos, beat_pos);
|
||||
|
||||
_last_tick = _pos->sample;
|
||||
_beat_pos = beat_pos;
|
||||
_next_tick = clk_pos - _mclk_out_latency.max;
|
||||
_transport_pos = end_sample;
|
||||
}
|
||||
|
||||
if (!Config->get_send_midi_clock ()) {
|
||||
return;
|
||||
}
|
||||
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));
|
||||
|
||||
_send_state = true;
|
||||
}
|
||||
_rolling = true;
|
||||
_clock_cnt = 0;
|
||||
|
||||
void
|
||||
MidiClockTicker::transport_looped ()
|
||||
{
|
||||
Location* loop_location = _session->locations ()->auto_loop_location ();
|
||||
assert (loop_location);
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiClock,
|
||||
string_compose ("Transport looped, position: %1, loop start: %2, loop end: %3, play loop: %4\n",
|
||||
_session->transport_sample (), loop_location->start (), loop_location->end (), _session->get_play_loop ()));
|
||||
|
||||
// adjust _last_tick, so that the next MIDI clock message is sent
|
||||
// in due time (and the tick interval is still constant)
|
||||
|
||||
samplecnt_t elapsed_since_last_tick = loop_location->end () - _last_tick;
|
||||
|
||||
if (loop_location->start () > elapsed_since_last_tick) {
|
||||
_last_tick = loop_location->start () - elapsed_since_last_tick;
|
||||
} else {
|
||||
_last_tick = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiClockTicker::tick (const samplepos_t& transport_sample, pframes_t nframes)
|
||||
{
|
||||
if (!Config->get_send_midi_clock () || _session == 0 || _midi_port == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_send_pos) {
|
||||
if (_pos->speed == 0.0f) {
|
||||
send_position_event (llrint (_pos->midi_beats), 0, nframes);
|
||||
} else if (_pos->speed == 1.0f) {
|
||||
send_stop_event (0, nframes);
|
||||
|
||||
if (_pos->sample == 0) {
|
||||
send_start_event (0, nframes);
|
||||
if (_beat_pos == 0 && _next_tick == 0 && start_sample == 0) {
|
||||
send_start_event (0, n_samples);
|
||||
} else {
|
||||
send_position_event (llrint (_pos->midi_beats), 0, nframes);
|
||||
send_continue_event (0, nframes);
|
||||
send_position_event (_beat_pos, 0, n_samples); // consider sending this early
|
||||
send_continue_event (_next_tick - start_sample, n_samples);
|
||||
}
|
||||
} else {
|
||||
/* Varispeed not supported */
|
||||
goto out;
|
||||
}
|
||||
|
||||
_send_pos = false;
|
||||
}
|
||||
|
||||
if (_send_state) {
|
||||
if (_pos->speed == 1.0f) {
|
||||
if (_session->get_play_loop ()) {
|
||||
assert (_session->locations ()->auto_loop_location ());
|
||||
assert (_rolling);
|
||||
|
||||
if (_pos->sample == _session->locations ()->auto_loop_location ()->start ()) {
|
||||
send_start_event (0, nframes);
|
||||
} else {
|
||||
send_continue_event (0, nframes);
|
||||
}
|
||||
|
||||
} else if (_pos->sample == 0) {
|
||||
send_start_event (0, nframes);
|
||||
} else {
|
||||
send_continue_event (0, nframes);
|
||||
}
|
||||
|
||||
// send_midi_clock_event (0);
|
||||
|
||||
} else if (_pos->speed == 0.0f) {
|
||||
send_stop_event (0, nframes);
|
||||
send_position_event (llrint (_pos->midi_beats), 0, nframes);
|
||||
while (_next_tick >= start_sample && _next_tick < end_sample) {
|
||||
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));
|
||||
send_midi_clock_event (_next_tick - start_sample, n_samples);
|
||||
if (++_clock_cnt == 6) {
|
||||
_clock_cnt = 0;
|
||||
++_beat_pos;
|
||||
}
|
||||
|
||||
_send_state = false;
|
||||
_next_tick += one_ppqn_in_samples (llrint (_next_tick));
|
||||
}
|
||||
|
||||
if (_session->transport_speed () != 1.0f) {
|
||||
/* no varispeed support and nothing to do after this if stopped */
|
||||
return;
|
||||
}
|
||||
_transport_pos = end_sample;
|
||||
|
||||
const samplepos_t end = _pos->sample + nframes;
|
||||
double iter = _last_tick;
|
||||
|
||||
while (true) {
|
||||
double clock_delta = one_ppqn_in_samples (llrint (iter));
|
||||
double next_tick = iter + clock_delta;
|
||||
sampleoffset_t next_tick_offset = llrint (next_tick) - end;
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiClock,
|
||||
string_compose ("Tick: iter: %1, last tick time: %2, next tick time: %3, offset: %4, cycle length: %5\n",
|
||||
iter, _last_tick, next_tick, next_tick_offset, nframes));
|
||||
|
||||
if (next_tick_offset >= nframes) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (next_tick_offset >= 0) {
|
||||
send_midi_clock_event (next_tick_offset, nframes);
|
||||
}
|
||||
|
||||
iter = next_tick;
|
||||
}
|
||||
|
||||
_last_tick = iter;
|
||||
_pos->sample = end;
|
||||
out:
|
||||
_midi_port->flush_buffers (n_samples);
|
||||
_midi_port->cycle_end (n_samples);
|
||||
}
|
||||
|
||||
double
|
||||
MidiClockTicker::one_ppqn_in_samples (samplepos_t transport_position)
|
||||
MidiClockTicker::one_ppqn_in_samples (samplepos_t transport_position) const
|
||||
{
|
||||
const double samples_per_quarter_note = _session->tempo_map ().samples_per_quarter_note_at (transport_position, _session->nominal_sample_rate ());
|
||||
|
||||
return samples_per_quarter_note / double(_ppqn);
|
||||
return samples_per_quarter_note / 24.0;
|
||||
}
|
||||
|
||||
void
|
||||
MidiClockTicker::send_midi_clock_event (pframes_t offset, pframes_t nframes)
|
||||
{
|
||||
if (!_midi_port) {
|
||||
return;
|
||||
}
|
||||
assert (_midi_port);
|
||||
|
||||
static uint8_t msg = MIDI_CMD_COMMON_CLOCK;
|
||||
|
||||
|
@ -336,51 +225,43 @@ MidiClockTicker::send_midi_clock_event (pframes_t offset, pframes_t nframes)
|
|||
void
|
||||
MidiClockTicker::send_start_event (pframes_t offset, pframes_t nframes)
|
||||
{
|
||||
if (!_midi_port) {
|
||||
return;
|
||||
}
|
||||
assert (_midi_port);
|
||||
|
||||
static uint8_t msg = { MIDI_CMD_COMMON_START };
|
||||
MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
|
||||
mb.push_back (offset, 1, &msg);
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Start %1\n", _last_tick));
|
||||
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Start %1\n", _next_tick));
|
||||
}
|
||||
|
||||
void
|
||||
MidiClockTicker::send_continue_event (pframes_t offset, pframes_t nframes)
|
||||
{
|
||||
if (!_midi_port) {
|
||||
return;
|
||||
}
|
||||
assert (_midi_port);
|
||||
|
||||
static uint8_t msg = { MIDI_CMD_COMMON_CONTINUE };
|
||||
MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
|
||||
mb.push_back (offset, 1, &msg);
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Continue %1\n", _last_tick));
|
||||
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Continue %1\n", _next_tick));
|
||||
}
|
||||
|
||||
void
|
||||
MidiClockTicker::send_stop_event (pframes_t offset, pframes_t nframes)
|
||||
{
|
||||
if (!_midi_port) {
|
||||
return;
|
||||
}
|
||||
assert (_midi_port);
|
||||
|
||||
static uint8_t msg = MIDI_CMD_COMMON_STOP;
|
||||
MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
|
||||
mb.push_back (offset, 1, &msg);
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Stop %1\n", _last_tick));
|
||||
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Stop %1\n", _next_tick));
|
||||
}
|
||||
|
||||
void
|
||||
MidiClockTicker::send_position_event (uint32_t midi_beats, pframes_t offset, pframes_t nframes)
|
||||
{
|
||||
if (!_midi_port) {
|
||||
return;
|
||||
}
|
||||
assert (_midi_port);
|
||||
|
||||
/* can only use 14bits worth */
|
||||
if (midi_beats > 0x3fff) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user