13
0

templatize RTMidiBuffer to allow use in different time domains

Also add ::track_state() method to configue a MidiStateTracker
to correspond to the state implied by an RTMidiBuffer<T,D>
at a given point in time.
This commit is contained in:
Paul Davis 2024-06-25 22:04:04 -06:00
parent b693d07fcb
commit a4a277c141
2 changed files with 77 additions and 41 deletions

View File

@ -40,7 +40,8 @@ class MidiNoteTracker;
class MidiStateTracker;
/** */
class LIBARDOUR_API RTMidiBuffer : public Evoral::EventSink<samplepos_t>
template<typename TimeType, typename DistanceType>
class LIBARDOUR_API RTMidiBufferBase : public Evoral::EventSink<TimeType>
{
private:
struct Blob {
@ -49,28 +50,26 @@ class LIBARDOUR_API RTMidiBuffer : public Evoral::EventSink<samplepos_t>
};
public:
typedef samplepos_t TimeType;
RTMidiBuffer ();
~RTMidiBuffer();
RTMidiBufferBase ();
~RTMidiBufferBase ();
void clear();
void resize(size_t);
size_t size() const { return _size; }
bool empty() const { return _size == 0; }
samplecnt_t span() const;
DistanceType span() const;
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);
uint32_t read (MidiBuffer& dst, TimeType start, TimeType end, MidiNoteTracker& tracker, DistanceType offset = 0);
void track (MidiStateTracker&, TimeType start, TimeType end);
void dump (uint32_t);
void reverse ();
bool reversed() const;
struct Item {
samplepos_t timestamp;
TimeType timestamp;
union {
uint8_t bytes[4];
uint32_t offset;
@ -84,7 +83,7 @@ class LIBARDOUR_API RTMidiBuffer : public Evoral::EventSink<samplepos_t>
return _data[n];
}
uint8_t const * bytes (Item const & item, uint32_t& size) {
uint8_t const * bytes (Item const & item, uint32_t& size) const {
if (!item.bytes[0]) {
size = Evoral::midi_event_size (item.bytes[1]);
return &item.bytes[1];
@ -97,7 +96,11 @@ class LIBARDOUR_API RTMidiBuffer : public Evoral::EventSink<samplepos_t>
}
}
void shift (sampleoffset_t distance) {
/* XXX this really requires a 3rd template argument for a potentially
* negative offset
*/
void shift (DistanceType distance) {
if (_size == 0) {
return;
}
@ -106,6 +109,8 @@ class LIBARDOUR_API RTMidiBuffer : public Evoral::EventSink<samplepos_t>
}
}
void track_state (TimeType when, MidiStateTracker& mst) const;
private:
friend struct WriteProtectRender;
@ -130,7 +135,7 @@ class LIBARDOUR_API RTMidiBuffer : public Evoral::EventSink<samplepos_t>
public:
class WriteProtectRender {
public:
WriteProtectRender (RTMidiBuffer& rtm) : lm (rtm._lock, Glib::Threads::NOT_LOCK) {}
WriteProtectRender (RTMidiBufferBase& rtm) : lm (rtm._lock, Glib::Threads::NOT_LOCK) {}
void acquire () { lm.acquire(); }
private:
@ -138,6 +143,8 @@ class LIBARDOUR_API RTMidiBuffer : public Evoral::EventSink<samplepos_t>
};
};
typedef RTMidiBufferBase<samplepos_t,samplecnt_t> RTMidiBuffer;
} // namespace ARDOUR
#endif // __ardour_rt_midi_buffer_h__

View File

@ -24,6 +24,8 @@
#include "pbd/error.h"
#include "pbd/debug.h"
#include "temporal/beats.h"
#include "ardour/debug.h"
#include "ardour/midi_buffer.h"
#include "ardour/midi_state_tracker.h"
@ -33,7 +35,8 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
RTMidiBuffer::RTMidiBuffer ()
template<class TimeType, class DistanceType>
RTMidiBufferBase<TimeType,DistanceType>::RTMidiBufferBase ()
: _size (0)
, _capacity (0)
, _data (0)
@ -44,14 +47,16 @@ RTMidiBuffer::RTMidiBuffer ()
{
}
RTMidiBuffer::~RTMidiBuffer()
template<class TimeType, class DistanceType>
RTMidiBufferBase<TimeType,DistanceType>::~RTMidiBufferBase()
{
cache_aligned_free (_data);
cache_aligned_free (_pool);
}
template<class TimeType, class DistanceType>
void
RTMidiBuffer::resize (size_t size)
RTMidiBufferBase<TimeType,DistanceType>::resize (size_t size)
{
if (_data && size < _capacity) {
@ -69,21 +74,23 @@ RTMidiBuffer::resize (size_t size)
if (_size) {
assert (old_data);
memcpy (_data, old_data, _size * sizeof (Item));
memcpy ((void*) _data, (void*) old_data, _size * sizeof (Item));
cache_aligned_free (old_data);
}
_capacity = size;
}
template<class TimeType, class DistanceType>
bool
RTMidiBuffer::reversed () const
RTMidiBufferBase<TimeType,DistanceType>::reversed () const
{
return _reversed;
}
template<class TimeType, class DistanceType>
void
RTMidiBuffer::reverse ()
RTMidiBufferBase<TimeType,DistanceType>::reverse ()
{
if (_size == 0) {
return;
@ -148,8 +155,9 @@ RTMidiBuffer::reverse ()
_reversed = !_reversed;
}
template<class TimeType, class DistanceType>
void
RTMidiBuffer::dump (uint32_t cnt)
RTMidiBufferBase<TimeType,DistanceType>::dump (uint32_t cnt)
{
cerr << this << " total items: " << _size << " within " << _capacity << " blob pool: " << _pool_capacity << " used " << _pool_size << endl;
@ -187,8 +195,9 @@ RTMidiBuffer::dump (uint32_t cnt)
}
}
template<class TimeType, class DistanceType>
uint32_t
RTMidiBuffer::write (TimeType time, Evoral::EventType /*type*/, uint32_t size, const uint8_t* buf)
RTMidiBufferBase<TimeType,DistanceType>::write (TimeType time, Evoral::EventType /*type*/, uint32_t size, const uint8_t* buf)
{
/* This buffer stores only MIDI, we don't care about the value of "type" */
@ -238,21 +247,16 @@ RTMidiBuffer::write (TimeType time, Evoral::EventType /*type*/, uint32_t size, c
/*
static
bool
item_timestamp_earlier (ARDOUR::RTMidiBuffer::Item const & item, samplepos_t const & time)
item_timestamp_earlier (ARDOUR::template<class TimeType, class DistanceType>
RTMidiBufferBase<TimeType,DistanceType>::Item const & item, samplepos_t const & time)
{
return item.timestamp < time;
}
*/
static
bool
item_item_earlier (ARDOUR::RTMidiBuffer::Item const & item, ARDOUR::RTMidiBuffer::Item const & other)
{
return item.timestamp < other.timestamp;
}
template<class TimeType, class DistanceType>
void
RTMidiBuffer::track (MidiStateTracker& mst, samplepos_t start, samplepos_t end)
RTMidiBufferBase<TimeType,DistanceType>::track (MidiStateTracker& mst, TimeType start, TimeType end)
{
Glib::Threads::RWLock::ReaderLock lm (_lock, Glib::Threads::TRY_LOCK);
@ -268,13 +272,13 @@ RTMidiBuffer::track (MidiStateTracker& mst, samplepos_t start, samplepos_t end)
if (start < end) {
iend = _data+_size;
item = lower_bound (_data, iend, foo, item_item_earlier);
item = lower_bound (_data, iend, foo, [](Item const & a, Item const & b) { return a.timestamp < b.timestamp; });
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);
item = upper_bound (_data, uend, foo, [](Item const & a, Item const & b) { return a.timestamp < b.timestamp; });
if (item == uend) {
--item;
@ -330,8 +334,9 @@ RTMidiBuffer::track (MidiStateTracker& mst, samplepos_t start, samplepos_t end)
}
}
template<class TimeType, class DistanceType>
uint32_t
RTMidiBuffer::read (MidiBuffer& dst, samplepos_t start, samplepos_t end, MidiNoteTracker& tracker, samplecnt_t offset)
RTMidiBufferBase<TimeType,DistanceType>::read (MidiBuffer& dst, TimeType start, TimeType end, MidiNoteTracker& tracker, DistanceType offset)
{
Glib::Threads::RWLock::ReaderLock lm (_lock, Glib::Threads::TRY_LOCK);
@ -348,13 +353,13 @@ RTMidiBuffer::read (MidiBuffer& dst, samplepos_t start, samplepos_t end, MidiNot
if (start < end) {
iend = _data+_size;
item = lower_bound (_data, iend, foo, item_item_earlier);
item = lower_bound (_data, iend, foo, [](Item const & a, Item const & b) { return a.timestamp < b.timestamp; });
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);
item = upper_bound (_data, uend, foo, [](Item const & a, Item const & b) { return a.timestamp < b.timestamp; });
if (item == uend) {
--item;
@ -418,7 +423,9 @@ RTMidiBuffer::read (MidiBuffer& dst, samplepos_t start, samplepos_t end, MidiNot
}
if (!dst.push_back (evtime, Evoral::MIDI_EVENT, size, addr)) {
const samplepos_t evsamples (timepos_t (evtime).samples());
if (!dst.push_back (evsamples, Evoral::MIDI_EVENT, size, addr)) {
DEBUG_TRACE (DEBUG::MidiRingBuffer, string_compose ("MidiRingBuffer: overflow in destination MIDI buffer, stopped after %1 events, dst size = %2\n", count, dst.size()));
break;
}
@ -448,8 +455,9 @@ RTMidiBuffer::read (MidiBuffer& dst, samplepos_t start, samplepos_t end, MidiNot
return count;
}
template<class TimeType, class DistanceType>
uint32_t
RTMidiBuffer::alloc_blob (uint32_t size)
RTMidiBufferBase<TimeType,DistanceType>::alloc_blob (uint32_t size)
{
if (_pool_size + size > _pool_capacity) {
uint8_t* old_pool = _pool;
@ -471,8 +479,9 @@ RTMidiBuffer::alloc_blob (uint32_t size)
return offset;
}
template<class TimeType, class DistanceType>
uint32_t
RTMidiBuffer::store_blob (uint32_t size, uint8_t const * data)
RTMidiBufferBase<TimeType,DistanceType>::store_blob (uint32_t size, uint8_t const * data)
{
uint32_t offset = alloc_blob (size);
uint8_t* addr = &_pool[offset];
@ -484,8 +493,9 @@ RTMidiBuffer::store_blob (uint32_t size, uint8_t const * data)
return offset;
}
template<class TimeType, class DistanceType>
void
RTMidiBuffer::clear ()
RTMidiBufferBase<TimeType,DistanceType>::clear ()
{
/* mark main array as empty */
_size = 0;
@ -495,11 +505,12 @@ RTMidiBuffer::clear ()
_reversed = false;
}
samplecnt_t
RTMidiBuffer::span() const
template<class TimeType, class DistanceType>
DistanceType
RTMidiBufferBase<TimeType,DistanceType>::span() const
{
if (_size == 0 || _size == 1) {
return 0;
return DistanceType ();
}
const Item* last = &_data[_size-1];
@ -508,3 +519,21 @@ RTMidiBuffer::span() const
return last->timestamp - first->timestamp;
}
template<class TimeType, class DistanceType>
void
RTMidiBufferBase<TimeType,DistanceType>::track_state (TimeType when, MidiStateTracker& mst) const
{
uint32_t evsize;
for (uint32_t index = 0; index < _size; ++index) {
if (_data[index].timestamp >= when) {
break;
}
mst.track (bytes (_data[index], evsize));
}
}
// Explicit instantiation
template class RTMidiBufferBase<samplepos_t,samplecnt_t>;
template class RTMidiBufferBase<Temporal::Beats,Temporal::Beats>;