basic work to permit "MIDI catchup" for MIDI state at any point on the timeline

This currently does nothing and cannot be enabled
This commit is contained in:
Paul Davis 2023-11-20 21:32:49 -07:00
parent 4647dd6b41
commit cb91334cc6
5 changed files with 115 additions and 3 deletions

View File

@ -103,6 +103,8 @@ public:
bool declick_in_progress () const;
void set_need_midi_catchup (bool);
/* inc/dec variants MUST be called as part of the process call tree, before any
* disk readers are invoked. We use it when the session needs the
* transport (and thus effective read position for DiskReaders) to keep
@ -248,6 +250,9 @@ private:
samplepos_t last_refill_loop_start;
void setup_preloop_buffer ();
bool _midi_catchup;
bool _need_midi_catchup;
};
} // namespace ARDOUR

View File

@ -37,6 +37,7 @@ namespace ARDOUR {
class MidiBuffer;
class MidiNoteTracker;
class MidiStateTracker;
/** */
class LIBARDOUR_API RTMidiBuffer : public Evoral::EventSink<samplepos_t>
@ -62,6 +63,7 @@ class LIBARDOUR_API RTMidiBuffer : public Evoral::EventSink<samplepos_t>
uint32_t write (TimeType time, Evoral::EventType type, uint32_t size, const uint8_t* buf);
uint32_t read (MidiBuffer& dst, samplepos_t start, samplepos_t end, MidiNoteTracker& tracker, samplecnt_t offset = 0);
void track (MidiStateTracker&, samplepos_t start, samplepos_t end);
void dump (uint32_t);
void reverse ();

View File

@ -65,6 +65,8 @@ DiskReader::DiskReader (Session& s, Track& t, string const& str, Temporal::TimeD
, _declick_offs (0)
, _declick_enabled (false)
, last_refill_loop_start (0)
, _midi_catchup (false)
, _need_midi_catchup (false)
{
file_sample[DataType::AUDIO] = 0;
file_sample[DataType::MIDI] = 0;
@ -1484,6 +1486,14 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
effective_end = min (effective_start + cnt, loop_end);
assert (effective_end > effective_start);
if (_midi_catchup && _need_midi_catchup) {
MidiStateTracker mst;
rtmb->track (mst, effective_start, effective_end);
mst.flush (dst, 0, false);
_need_midi_catchup = false;
}
const samplecnt_t this_read = effective_end - effective_start;
DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("playback buffer LOOP read, from %1 to %2 (%3)\n", effective_start, effective_end, this_read));
@ -1491,7 +1501,7 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
#ifndef NDEBUG
size_t events_read =
#endif
rtmb->read (*target, effective_start, effective_end, _tracker, offset);
rtmb->read (*target, effective_start, effective_end, _tracker, offset);
cnt -= this_read;
effective_start += this_read;
@ -1511,6 +1521,12 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
} else {
DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("playback buffer read, from %1 to %2 (%3)\n", start_sample, end_sample, nframes));
if (_midi_catchup && _need_midi_catchup) {
MidiStateTracker mst;
rtmb->track (mst, start_sample, end_sample);
mst.flush (dst, 0, false);
_need_midi_catchup = false;
}
DEBUG_RESULT (size_t, events_read, rtmb->read (*target, start_sample, end_sample, _tracker));
DEBUG_TRACE (DEBUG::MidiDiskIO, string_compose ("%1 MDS events read %2 range %3 .. %4\n", _name, events_read, playback_sample, playback_sample + nframes));
}
@ -1957,3 +1973,9 @@ DiskReader::setup_preloop_buffer ()
++channel;
}
}
void
DiskReader::set_need_midi_catchup (bool yn)
{
_need_midi_catchup = yn;
}

View File

@ -348,8 +348,6 @@ MidiStateTracker::track (const uint8_t* evbuf)
void
MidiStateTracker::flush (MidiBuffer& dst, samplepos_t time, bool reset)
{
/* XXX implement me */
uint8_t buf[3];
const size_t n_channels = 16;
const size_t n_controls = 127;
@ -377,6 +375,9 @@ MidiStateTracker::flush (MidiBuffer& dst, samplepos_t time, bool reset)
program[chn] = 0x80;
}
}
/* XXX bender */
/* XXX pressure */
}
}

View File

@ -251,6 +251,88 @@ item_item_earlier (ARDOUR::RTMidiBuffer::Item const & item, ARDOUR::RTMidiBuffer
return item.timestamp < other.timestamp;
}
void
RTMidiBuffer::track (MidiStateTracker& mst, samplepos_t start, samplepos_t end)
{
Glib::Threads::RWLock::ReaderLock lm (_lock, Glib::Threads::TRY_LOCK);
if (!lm.locked()) {
return;
}
bool reverse;
Item foo;
Item* iend;
Item* item;
foo.timestamp = start;
if (start < end) {
iend = _data+_size;
item = lower_bound (_data, iend, foo, item_item_earlier);
reverse = false;
} else {
iend = _data;
--iend; /* yes, this is technically "illegal" but we will never indirect */
Item* uend = _data + _size;
item = upper_bound (_data, uend, foo, item_item_earlier);
if (item == uend) {
--item;
}
reverse = true;
}
while ((item != iend) && ((reverse && (item->timestamp > end)) || (!reverse && (item->timestamp < end)))) {
TimeType evtime = item->timestamp;
/* Adjust event times to be relative to 'start', taking
* 'offset' into account.
*/
if (reverse) {
if (evtime > start) {
--item;
continue;
}
} else {
if (evtime < start) {
++item;
continue;
}
}
uint32_t size;
uint8_t* addr;
if (item->bytes[0]) {
/* more than 3 bytes ... indirect */
uint32_t offset = item->offset & ~(1<<(CHAR_BIT-1));
Blob* blob = reinterpret_cast<Blob*> (&_pool[offset]);
size = blob->size;
addr = blob->data;
} else {
size = Evoral::midi_event_size (item->bytes[1]);
addr = &item->bytes[1];
}
mst.track (addr);
if (reverse) {
--item;
} else {
++item;
}
}
}
uint32_t
RTMidiBuffer::read (MidiBuffer& dst, samplepos_t start, samplepos_t end, MidiNoteTracker& tracker, samplecnt_t offset)
{