diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2019-10-15 21:34:03 -0600 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2019-11-02 16:32:18 -0600 |
commit | 088e4bca5a52157e5b8437c91360ccb24133aa1c (patch) | |
tree | 8275460e4b80584b3d5d1670421dd1e4bc76be64 /libs/ardour/rt_midibuffer.cc | |
parent | e87e0ec0283674c3e649c1218822fa03c3fc9016 (diff) |
reimplement RTMidiBuffer using a highly optimized data structure
Reduces load time of Glass MIDI piece with 48k note events by about 35%. Improves data
locality. Omits size for all 3 byte or less MIDI events, uses implicit size. No limit
on size of sysex.
Relies on the fact that the data structure is always filled linearly in time, and
never modified.
Diffstat (limited to 'libs/ardour/rt_midibuffer.cc')
-rw-r--r-- | libs/ardour/rt_midibuffer.cc | 176 |
1 files changed, 140 insertions, 36 deletions
diff --git a/libs/ardour/rt_midibuffer.cc b/libs/ardour/rt_midibuffer.cc index c2fec01e59..bd4a7e3d42 100644 --- a/libs/ardour/rt_midibuffer.cc +++ b/libs/ardour/rt_midibuffer.cc @@ -24,6 +24,8 @@ #include "pbd/debug.h" #include "pbd/stacktrace.h" +#include "evoral/midi_util.h" + #include "ardour/debug.h" #include "ardour/midi_buffer.h" #include "ardour/midi_state_tracker.h" @@ -37,6 +39,9 @@ RTMidiBuffer::RTMidiBuffer (size_t capacity) : _size (0) , _capacity (0) , _data (0) + , _pool_size (0) + , _pool_capacity (0) + , _pool (0) { if (capacity) { resize (capacity); @@ -46,6 +51,7 @@ RTMidiBuffer::RTMidiBuffer (size_t capacity) RTMidiBuffer::~RTMidiBuffer() { cache_aligned_free (_data); + cache_aligned_free (_pool); } void @@ -61,35 +67,49 @@ RTMidiBuffer::resize (size_t size) return; } - uint8_t* old_data = _data; + Item* old_data = _data; - cache_aligned_malloc ((void**) &_data, size); + cache_aligned_malloc ((void**) &_data, size * sizeof (Item)); if (_size) { memcpy (_data, old_data, _size); + cache_aligned_free (old_data); } - cache_aligned_free (old_data); _capacity = size; - - assert(_data); } void RTMidiBuffer::dump (uint32_t cnt) { - for (Map::iterator iter = _map.begin(); iter != _map.end() && cnt; ++iter, --cnt) { + for (uint32_t i = 0; i < _size && i < cnt; ++i) { + + Item* item = &_data[i]; + uint8_t* addr; + uint32_t size; + + if (item->bytes[0]) { + + /* more than 3 bytes ... indirect */ + + uint32_t offset = item->offset & ~(1<<(sizeof(uint8_t)-1)); + Blob* blob = reinterpret_cast<Blob*> (&_pool[offset]); + + size = blob->size; + addr = blob->data; + + } else { - uint8_t* addr = &_data[iter->second]; - TimeType evtime = iter->first; - uint32_t size = *(reinterpret_cast<Evoral::EventType*>(addr)); - addr += sizeof (size); + size = Evoral::midi_event_size (item->bytes[1]); + addr = &item->bytes[1]; - cerr << "@ " << evtime << " sz=" << size << '\t'; + } + + cerr << "@ " << item->timestamp << " sz=" << size << '\t'; cerr << hex; - for (size_t i =0 ; i < size; ++i) { - cerr << "0x" << hex << (int)addr[i] << dec << '/' << (int)addr[i] << ' '; + for (size_t j =0 ; j < size; ++j) { + cerr << "0x" << hex << (int)addr[j] << dec << '/' << (int)addr[i] << ' '; } cerr << dec << endl; } @@ -100,52 +120,75 @@ RTMidiBuffer::write (TimeType time, Evoral::EventType /*type*/, uint32_t size, c { /* This buffer stores only MIDI, we don't care about the value of "type" */ - const size_t bytes_to_merge = sizeof (size) + size; + const size_t bytes_to_merge = sizeof (time) + sizeof (uint32_t); if (_size + bytes_to_merge > _capacity) { resize (_capacity + 8192); // XXX 8192 is completely arbitrary } - _map.insert (make_pair (time, _size)); + _data[_size].timestamp = time; - uint8_t* addr = &_data[_size]; + if (size > 3) { - *(reinterpret_cast<uint32_t*>(addr)) = size; - addr += sizeof (size); - memcpy (addr, buf, size); + uint32_t off = store_blob (size, buf); + + /* this indicates that the data (more than 3 bytes) is not inline */ + _data[_size].offset = (off | (1<<(sizeof(uint8_t)-1))); - _size += bytes_to_merge; + } else { + + assert ((int) size == Evoral::midi_event_size (buf[0])); + + /* this indicates that the data (up to 3 bytes) is inline */ + _data[_size].bytes[0] = 0; + + switch (size) { + case 3: + _data[_size].bytes[3] = buf[2]; + /* fallthru */ + case 2: + _data[_size].bytes[2] = buf[1]; + /* fallthru */ + case 1: + _data[_size].bytes[1] = buf[0]; + break; + } + } + + ++_size; return size; } +static +bool +item_timestamp_earlier (ARDOUR::RTMidiBuffer::Item const & item, samplepos_t time) +{ + return item.timestamp < time; +} + + + uint32_t RTMidiBuffer::read (MidiBuffer& dst, samplepos_t start, samplepos_t end, MidiStateTracker& tracker, samplecnt_t offset) { - Map::iterator iter = _map.lower_bound (start); + Item* iend = _data+_size; + Item* item = lower_bound (_data, iend, start, item_timestamp_earlier); uint32_t count = 0; + #ifndef NDEBUG TimeType unadjusted_time; #endif - DEBUG_TRACE (DEBUG::MidiRingBuffer, string_compose ("read from %1 .. %2\n", start, end)); - - while ((iter != _map.end()) && (iter->first < end)) { + DEBUG_TRACE (DEBUG::MidiRingBuffer, string_compose ("read from %1 .. %2 .. initial index = %3 (time = %4)\n", start, end, item, item->timestamp)); - /* the event consists of a size followed by bytes of MIDI - * data. It begins at _data[iter->second], which was stored in - * our map when we wrote the event into the data structure. - */ + while ((item < iend) && (item->timestamp < end)) { - uint8_t* addr = &_data[iter->second]; - TimeType evtime = iter->first; + TimeType evtime = item->timestamp; #ifndef NDEBUG unadjusted_time = evtime; #endif - uint32_t size = *(reinterpret_cast<Evoral::EventType*>(addr)); - addr += sizeof (size); - /* Adjust event times to be relative to 'start', taking * 'offset' into account. */ @@ -153,6 +196,26 @@ RTMidiBuffer::read (MidiBuffer& dst, samplepos_t start, samplepos_t end, MidiSta evtime -= start; evtime += offset; + uint32_t size; + uint8_t* addr; + + if (item->bytes[0]) { + + /* more than 3 bytes ... indirect */ + + uint32_t offset = item->offset & ~(1<<(sizeof(uint8_t)-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]; + + } + uint8_t* write_loc = dst.reserve (evtime, size); if (write_loc == 0) { @@ -160,15 +223,56 @@ RTMidiBuffer::read (MidiBuffer& dst, samplepos_t start, samplepos_t end, MidiSta break; } - DEBUG_TRACE (DEBUG::MidiRingBuffer, string_compose ("read event sz %1 @ %2\n", size, unadjusted_time)); - memcpy (write_loc, addr, size); + + DEBUG_TRACE (DEBUG::MidiRingBuffer, string_compose ("read event sz %1 @ %2\n", size, unadjusted_time)); tracker.track (addr); - ++iter; + ++item; ++count; } DEBUG_TRACE (DEBUG::MidiRingBuffer, string_compose ("total events found for %1 .. %2 = %3\n", start, end, count)); return count; } + +uint32_t +RTMidiBuffer::alloc_blob (uint32_t size) +{ + if (_pool_size + size > _pool_capacity) { + uint8_t* old_pool = _pool; + + _pool_capacity += size * 4; + + cache_aligned_malloc ((void **) &_pool, _pool_capacity * 2); + memcpy (_pool, old_pool, _pool_size); + cache_aligned_free (old_pool); + } + + uint32_t offset = _pool_size; + _pool_size += size; + + return offset; +} + +uint32_t +RTMidiBuffer::store_blob (uint32_t size, uint8_t const * data) +{ + uint32_t offset = alloc_blob (size); + uint8_t* addr = &_pool[offset]; + + *(reinterpret_cast<uint32_t*> (addr)) = size; + addr += sizeof (size); + memcpy (addr, data, size); + + return offset; +} + +void +RTMidiBuffer::clear () +{ + /* mark main array as empty */ + _size = 0; + /* free the entire current pool size, if any */ + _pool_size = 0; +} |