From cb91334cc6d2da45c9076da664109b3ebd564e2d Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 20 Nov 2023 21:32:49 -0700 Subject: [PATCH] basic work to permit "MIDI catchup" for MIDI state at any point on the timeline This currently does nothing and cannot be enabled --- libs/ardour/ardour/disk_reader.h | 5 ++ libs/ardour/ardour/rt_midibuffer.h | 2 + libs/ardour/disk_reader.cc | 24 ++++++++- libs/ardour/midi_state_tracker.cc | 5 +- libs/ardour/rt_midibuffer.cc | 82 ++++++++++++++++++++++++++++++ 5 files changed, 115 insertions(+), 3 deletions(-) diff --git a/libs/ardour/ardour/disk_reader.h b/libs/ardour/ardour/disk_reader.h index c9053ce542..ed240c744a 100644 --- a/libs/ardour/ardour/disk_reader.h +++ b/libs/ardour/ardour/disk_reader.h @@ -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 diff --git a/libs/ardour/ardour/rt_midibuffer.h b/libs/ardour/ardour/rt_midibuffer.h index 4469a9590a..9cef6218b9 100644 --- a/libs/ardour/ardour/rt_midibuffer.h +++ b/libs/ardour/ardour/rt_midibuffer.h @@ -37,6 +37,7 @@ namespace ARDOUR { class MidiBuffer; class MidiNoteTracker; +class MidiStateTracker; /** */ class LIBARDOUR_API RTMidiBuffer : public Evoral::EventSink @@ -62,6 +63,7 @@ class LIBARDOUR_API RTMidiBuffer : public Evoral::EventSink 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 (); diff --git a/libs/ardour/disk_reader.cc b/libs/ardour/disk_reader.cc index c50cd74949..943f48a411 100644 --- a/libs/ardour/disk_reader.cc +++ b/libs/ardour/disk_reader.cc @@ -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; +} diff --git a/libs/ardour/midi_state_tracker.cc b/libs/ardour/midi_state_tracker.cc index 76cf6657a0..e72c48d1b8 100644 --- a/libs/ardour/midi_state_tracker.cc +++ b/libs/ardour/midi_state_tracker.cc @@ -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 */ } } diff --git a/libs/ardour/rt_midibuffer.cc b/libs/ardour/rt_midibuffer.cc index d1938e307e..9186252f8d 100644 --- a/libs/ardour/rt_midibuffer.cc +++ b/libs/ardour/rt_midibuffer.cc @@ -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 (&_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) {