From 395b759f423e9107d146edbd601d473385888a08 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 10 Feb 2022 19:59:08 -0700 Subject: [PATCH] midi state tracker: extend, fix, improve API for flushing state --- libs/ardour/ardour/midi_state_tracker.h | 9 +- libs/ardour/midi_state_tracker.cc | 113 ++++++++++++++---------- 2 files changed, 73 insertions(+), 49 deletions(-) diff --git a/libs/ardour/ardour/midi_state_tracker.h b/libs/ardour/ardour/midi_state_tracker.h index 317ccc59a8..10528c1494 100644 --- a/libs/ardour/ardour/midi_state_tracker.h +++ b/libs/ardour/ardour/midi_state_tracker.h @@ -50,11 +50,11 @@ public: void track (const MidiBuffer::const_iterator& from, const MidiBuffer::const_iterator& to); void add (uint8_t note, uint8_t chn); void remove (uint8_t note, uint8_t chn); - void resolve_notes (MidiBuffer& buffer, samplepos_t time); + void resolve_notes (MidiBuffer& buffer, samplepos_t time, bool reset = true); void resolve_notes (Evoral::EventSink& buffer, samplepos_t time); void resolve_notes (MidiSource& src, const Glib::Threads::Mutex::Lock& lock, Temporal::Beats time); - void flush_notes (MidiBuffer&, samplepos_t); + void flush_notes (MidiBuffer& buffer, samplepos_t time, bool reset = true); bool empty() const { return _on == 0; } uint16_t on() const { return _on; } @@ -70,6 +70,9 @@ public: private: uint8_t _active_notes[128*16]; uint16_t _on; + + void push_notes (MidiBuffer &dst, samplepos_t time, bool reset, int cmd, int velocity); + }; class LIBARDOUR_API MidiStateTracker : public MidiNoteTracker @@ -82,7 +85,7 @@ class LIBARDOUR_API MidiStateTracker : public MidiNoteTracker void dump (std::ostream&); void reset (); - void flush (MidiBuffer&, samplepos_t); + void flush (MidiBuffer&, samplepos_t, bool reset); private: uint8_t program[16]; diff --git a/libs/ardour/midi_state_tracker.cc b/libs/ardour/midi_state_tracker.cc index ddad3fad1a..084888e356 100644 --- a/libs/ardour/midi_state_tracker.cc +++ b/libs/ardour/midi_state_tracker.cc @@ -110,7 +110,19 @@ MidiNoteTracker::track (const uint8_t* evbuf) } void -MidiNoteTracker::resolve_notes (MidiBuffer &dst, samplepos_t time) +MidiNoteTracker::resolve_notes (MidiBuffer &dst, samplepos_t time, bool reset) +{ + push_notes (dst, time, reset, MIDI_CMD_NOTE_OFF, 64); +} + +void +MidiNoteTracker::flush_notes (MidiBuffer &dst, samplepos_t time, bool reset) +{ + push_notes (dst, time, reset, MIDI_CMD_NOTE_ON, 64); +} + +void +MidiNoteTracker::push_notes (MidiBuffer &dst, samplepos_t time, bool reset, int cmd, int velocity) { DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1 MB-resolve notes @ %2 on = %3\n", this, time, _on)); @@ -121,20 +133,20 @@ MidiNoteTracker::resolve_notes (MidiBuffer &dst, samplepos_t time) for (int channel = 0; channel < 16; ++channel) { for (int note = 0; note < 128; ++note) { while (_active_notes[note + 128 * channel]) { - uint8_t buffer[3] = { ((uint8_t) (MIDI_CMD_NOTE_OFF | channel)), uint8_t (note), 0 }; - Evoral::Event noteoff - (Evoral::MIDI_EVENT, time, 3, buffer, false); + uint8_t buffer[3] = { ((uint8_t) (cmd | channel)), uint8_t (note), (uint8_t) velocity }; + Evoral::Event ev (Evoral::MIDI_EVENT, time, 3, buffer, false); /* note that we do not care about failure from push_back() ... should we warn someone ? */ - dst.push_back (noteoff); + dst.push_back (ev); _active_notes[note + 128 * channel]--; - DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1: MB-resolved note %2/%3 at %4\n", - this, (int) note, (int) channel, time)); + DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1: MB-push note %2/%3 at %4\n", this, (int) note, (int) channel, time)); } } } - _on = 0; + if (reset) { + _on = 0; + } } void @@ -164,6 +176,7 @@ MidiNoteTracker::resolve_notes (Evoral::EventSink &dst, samplepos_t } } } + _on = 0; } @@ -195,40 +208,14 @@ MidiNoteTracker::resolve_notes (MidiSource& src, const MidiSource::Lock& lock, T } } } - _on = 0; -} -void -MidiNoteTracker::flush_notes (MidiBuffer &dst, samplepos_t time) -{ - DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1 MB-flushing notes @ %2 on = %3\n", this, time, _on)); - - if (!_on) { - return; - } - - for (int channel = 0; channel < 16; ++channel) { - for (int note = 0; note < 128; ++note) { - while (_active_notes[note + 128 * channel]) { - uint8_t buffer[3] = { ((uint8_t) (MIDI_CMD_NOTE_ON | channel)), uint8_t (note), 0 }; - Evoral::Event noteoff (Evoral::MIDI_EVENT, time, 3, buffer, false); - /* note that we do not care about failure from - push_back() ... should we warn someone ? - */ - dst.push_back (noteoff); - _active_notes[note + 128 * channel]--; - DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1: MB-flushed note %2/%3 at %4\n", - this, (int) note, (int) channel, time)); - } - } - } _on = 0; } void MidiNoteTracker::dump (ostream& o) { - o << "******\n"; + o << "****** NOTES\n"; for (int c = 0; c < 16; ++c) { for (int x = 0; x < 128; ++x) { if (_active_notes[c * 128 + x]) { @@ -269,8 +256,38 @@ MidiStateTracker::reset () void MidiStateTracker::dump (ostream& o) { + const size_t n_channels = 16; + const size_t n_controls = 127; + bool need_comma = false; + + o << "DUMP for MidiStateTracker @ " << this << std::endl; MidiNoteTracker::dump (o); - o << "implement MidiStateTracker::dump()"; + + for (size_t chn = 0; chn < n_channels; ++chn) { + if ((program[chn] & 0x80) == 0) { + if (need_comma) { + o << ", "; + } + o << "program[" << chn << "] = " << int (program[chn] & 0x7f); + need_comma = true; + } + } + o << std::endl; + + need_comma = false; + + for (size_t chn = 0; chn < n_channels; ++chn) { + for (size_t ctl = 0; ctl < n_controls; ++ctl) { + if ((control[chn][ctl] & 0x80) == 0) { + if (need_comma) { + o << ", "; + } + o << "ctrl[" << chn << "][" << ctl << "] = " << int (control[chn][ctl] & 0x7f); + need_comma = true; + } + } + } + o << std::endl; } void @@ -314,12 +331,12 @@ MidiStateTracker::track (const uint8_t* evbuf) break; default: - break; + break; } } void -MidiStateTracker::flush (MidiBuffer& dst, samplepos_t time) +MidiStateTracker::flush (MidiBuffer& dst, samplepos_t time, bool reset) { /* XXX implement me */ @@ -327,25 +344,29 @@ MidiStateTracker::flush (MidiBuffer& dst, samplepos_t time) const size_t n_channels = 16; const size_t n_controls = 127; - /* XXX need MidiNoteTracker::flush() method that will emit NoteOn for - * all currently-on notes. - */ + flush_notes (dst, time, reset); - for (int chn = 0; chn < n_channels; ++chn) { - if (program[chn] & 0x80 == 0) { + for (size_t chn = 0; chn < n_channels; ++chn) { + if ((program[chn] & 0x80) == 0) { buf[0] = MIDI_CMD_PGM_CHANGE|chn; buf[1] = program[chn] & 0x7f; dst.write (time, Evoral::MIDI_EVENT, 2, buf); + if (reset) { + program[chn] = 0x80; + } } } - for (int chn = 0; chn < n_channels; ++chn) { - for (int ctl = 0; ctl < n_controls; ++ctl) { - if (control[chn][ctl] & 0x80 == 0) { + for (size_t chn = 0; chn < n_channels; ++chn) { + for (size_t ctl = 0; ctl < n_controls; ++ctl) { + if ((control[chn][ctl] & 0x80) == 0) { buf[0] = MIDI_CMD_CONTROL|chn; buf[1] = ctl; buf[2] = control[chn][ctl] & 0x7f; dst.write (time, Evoral::MIDI_EVENT, 3, buf); + if (reset) { + control[chn][ctl] = 0x80; + } } } }