7#include <initializer_list>
36 sysex_copy_payload_capacity_(other.sysex_copy_payload_capacity_),
37 limit_to_reserved_capacity_(other.limit_to_reserved_capacity_),
38 dropped_events_(other.dropped_events_),
39 dropped_sysex_events_(other.dropped_sysex_events_) {
40 copy_event_storage(other);
41 reserve_copied_sysex_payloads(other);
47 sysex_copy_payload_capacity_ = other.sysex_copy_payload_capacity_;
48 limit_to_reserved_capacity_ = other.limit_to_reserved_capacity_;
49 dropped_events_ = other.dropped_events_;
50 dropped_sysex_events_ = other.dropped_sysex_events_;
51 copy_event_storage(other);
52 reserve_copied_sysex_payloads(other);
57 : events_(std::move(other.events_)),
58 sort_index_(std::move(other.sort_index_)),
59 sort_reorder_(std::move(other.sort_reorder_)),
60 sysex_copy_payload_pool_(std::move(other.sysex_copy_payload_pool_)),
61 sysex_(std::move(other.sysex_)),
63 sysex_copy_payload_capacity_(other.sysex_copy_payload_capacity_),
64 limit_to_reserved_capacity_(other.limit_to_reserved_capacity_),
65 dropped_events_(other.dropped_events_),
66 dropped_sysex_events_(other.dropped_sysex_events_) {
67 rebind_sysex_payloads_to_pool();
69 other.sysex_copy_payload_capacity_ = 0;
70 other.limit_to_reserved_capacity_ =
false;
71 other.dropped_events_ = 0;
72 other.dropped_sysex_events_ = 0;
77 events_ = std::move(other.events_);
78 sort_index_ = std::move(other.sort_index_);
79 sort_reorder_ = std::move(other.sort_reorder_);
80 sysex_copy_payload_pool_ = std::move(other.sysex_copy_payload_pool_);
81 sysex_ = std::move(other.sysex_);
83 sysex_copy_payload_capacity_ = other.sysex_copy_payload_capacity_;
84 limit_to_reserved_capacity_ = other.limit_to_reserved_capacity_;
85 dropped_events_ = other.dropped_events_;
86 dropped_sysex_events_ = other.dropped_sysex_events_;
87 rebind_sysex_payloads_to_pool();
89 other.sysex_copy_payload_capacity_ = 0;
90 other.limit_to_reserved_capacity_ =
false;
91 other.dropped_events_ = 0;
92 other.dropped_sysex_events_ = 0;
99 if (!can_append_event()) {
103 events_.push_back(event);
109 if (!can_append_event()) {
113 events_.push_back(std::move(event));
122 bool empty()
const {
return events_.empty(); }
123 std::size_t
size()
const {
return events_.size(); }
125 events_.reserve(capacity);
126 reserve_sort_scratch(capacity);
149 limit_to_reserved_capacity_ = enabled;
155 return sysex_copy_payload_capacity_;
178 const std::size_t n = events_.size();
180 sort_index_.resize(n);
181 for (std::size_t i = 0; i < n; ++i) {
182 sort_index_[i] =
static_cast<std::uint32_t
>(i);
184 const std::vector<MidiEvent>& ev = events_;
185 std::sort(sort_index_.begin(), sort_index_.end(),
186 [&ev](std::uint32_t a, std::uint32_t b) {
187 if (ev[a].sample_offset != ev[b].sample_offset) {
188 return ev[a].sample_offset < ev[b].sample_offset;
192 sort_reorder_.resize(n);
193 for (std::size_t i = 0; i < n; ++i) {
194 sort_reorder_[i] = std::move(events_[sort_index_[i]]);
196 events_.swap(sort_reorder_);
199 auto begin() {
return events_.begin(); }
200 auto end() {
return events_.end(); }
201 auto begin()
const {
return events_.begin(); }
202 auto end()
const {
return events_.end(); }
232 SysexPayload(std::initializer_list<uint8_t> values) : bytes_(values) {}
234 : bytes_(
std::move(bytes)) {}
237 : bytes_(other.bytes_),
238 realtime_pool_backed_(other.realtime_pool_backed_) {}
240 if (
this != &other) {
242 bytes_ = other.bytes_;
243 realtime_pool_backed_ = other.realtime_pool_backed_;
244 recycle_pool_ =
nullptr;
245 recycle_capacity_ = 0;
256 mutable_bytes().assign(values.begin(), values.end());
261 bytes_ = std::move(bytes);
262 realtime_pool_backed_ =
false;
266 std::size_t
size()
const {
return view().size(); }
267 bool empty()
const {
return view().empty(); }
268 std::size_t
capacity()
const {
return view().capacity(); }
269 const uint8_t*
data()
const {
return view().data(); }
270 uint8_t*
data() {
return mutable_bytes().data(); }
271 const uint8_t&
front()
const {
return view().front(); }
272 uint8_t&
front() {
return mutable_bytes().front(); }
273 const uint8_t&
back()
const {
return view().back(); }
274 uint8_t&
back() {
return mutable_bytes().back(); }
276 return view()[index];
279 return mutable_bytes()[index];
281 auto begin()
const {
return view().begin(); }
282 auto end()
const {
return view().end(); }
283 auto begin() {
return mutable_bytes().begin(); }
284 auto end() {
return mutable_bytes().end(); }
287 void clear() { mutable_bytes().clear(); }
288 void push_back(uint8_t
byte) { mutable_bytes().push_back(
byte); }
290 std::vector<uint8_t>
to_vector()
const {
return view(); }
293 const std::vector<uint8_t>& rhs) {
294 return lhs.view() == rhs;
298 return lhs == rhs.view();
301 const std::vector<uint8_t>& rhs) {
302 return !(lhs == rhs);
306 return !(lhs == rhs);
312 const std::vector<uint8_t>& view()
const {
return bytes_; }
313 std::vector<uint8_t>& mutable_bytes() {
return bytes_; }
314 std::vector<uint8_t> release_owned_vector() {
315 recycle_pool_ =
nullptr;
316 realtime_pool_backed_ =
false;
317 return std::move(bytes_);
319 bool realtime_pool_backed()
const {
return realtime_pool_backed_; }
320 void set_realtime_pool_backed(
322 std::vector<std::vector<uint8_t>>* recycle_pool =
nullptr,
323 std::size_t recycle_capacity = 0) {
324 realtime_pool_backed_ = backed;
325 recycle_pool_ = backed ? recycle_pool :
nullptr;
326 recycle_capacity_ = backed ? recycle_capacity : 0;
329 if (recycle_pool_ !=
nullptr && recycle_capacity_ > 0
330 && bytes_.capacity() >= recycle_capacity_
331 && recycle_pool_->size() < recycle_pool_->capacity()) {
333 recycle_pool_->push_back(std::move(bytes_));
335 recycle_pool_ =
nullptr;
336 recycle_capacity_ = 0;
337 realtime_pool_backed_ =
false;
340 std::vector<uint8_t> bytes_;
341 std::vector<std::vector<uint8_t>>* recycle_pool_ =
nullptr;
342 std::size_t recycle_capacity_ = 0;
343 bool realtime_pool_backed_ =
false;
352 bool add_sysex(std::vector<uint8_t> data, int32_t sample_offset = 0,
double ts = 0.0) {
353 if (!can_append_sysex()) {
357 sysex_.emplace_back();
358 auto&
event = sysex_.back();
359 event.data = std::move(data);
360 event.sample_offset = sample_offset;
361 event.timestamp = ts;
365 if (event.data.realtime_pool_backed()) {
371 if (!can_append_sysex()) {
375 sysex_.emplace_back();
376 auto& dst = sysex_.back();
377 dst.data =
event.data.release_owned_vector();
378 dst.sample_offset =
event.sample_offset;
379 dst.timestamp =
event.timestamp;
384 int32_t sample_offset = 0,
386 if (limit_to_reserved_capacity_) {
387 return add_sysex_copy_realtime(data,
size, sample_offset, ts);
389 if (!can_append_sysex()) {
398 std::size_t payload_capacity) {
399 sysex_copy_payload_capacity_ = payload_capacity;
400 sysex_copy_payload_pool_.clear();
401 sysex_copy_payload_pool_.reserve(payload_count);
402 for (std::size_t i = 0; i < payload_count; ++i) {
403 std::vector<uint8_t> payload;
404 payload.reserve(payload_capacity);
405 sysex_copy_payload_pool_.push_back(std::move(payload));
409 for (
auto& event : sysex_) {
410 recycle_sysex_payload(event.data.release_owned_vector());
413 dropped_sysex_events_ = 0;
416 const std::vector<SysexEvent>&
sysex()
const {
return sysex_; }
417 std::vector<SysexEvent>&
sysex() {
return sysex_; }
420 bool can_append_event()
const {
421 return !limit_to_reserved_capacity_ || events_.size() < events_.capacity();
423 bool can_append_sysex()
const {
424 return !limit_to_reserved_capacity_ || sysex_.size() < sysex_.capacity();
426 bool add_sysex_copy_realtime(
const uint8_t* data,
428 int32_t sample_offset,
430 if (!can_append_sysex() || sysex_copy_payload_pool_.empty()
431 || size > sysex_copy_payload_capacity_) {
435 auto payload = std::move(sysex_copy_payload_pool_.back());
436 sysex_copy_payload_pool_.pop_back();
437 payload.resize(size);
438 std::copy(data, data + size, payload.begin());
439 sysex_.emplace_back();
440 auto&
event = sysex_.back();
441 event.data = std::move(payload);
442 event.data.set_realtime_pool_backed(
true,
443 &sysex_copy_payload_pool_,
444 sysex_copy_payload_capacity_);
445 event.sample_offset = sample_offset;
446 event.timestamp = ts;
449 void recycle_sysex_payload(std::vector<uint8_t>&& payload) {
450 if (sysex_copy_payload_capacity_ == 0
451 || payload.capacity() < sysex_copy_payload_capacity_
452 || sysex_copy_payload_pool_.size()
453 >= sysex_copy_payload_pool_.capacity()) {
457 sysex_copy_payload_pool_.push_back(std::move(payload));
459 void copy_event_storage(
const MidiBuffer& other) {
461 events_.reserve(
other.events_.capacity());
462 events_ =
other.events_;
464 sysex_.reserve(
other.sysex_.capacity());
465 sysex_ =
other.sysex_;
467 void reserve_copied_sysex_payloads(
const MidiBuffer& other) {
468 sysex_copy_payload_pool_.clear();
469 if (sysex_copy_payload_capacity_ == 0) {
472 for (
auto& event : sysex_) {
473 if (event.data.size() <= sysex_copy_payload_capacity_) {
474 event.data.reserve(sysex_copy_payload_capacity_);
475 event.data.set_realtime_pool_backed(
476 event.data.realtime_pool_backed(),
477 event.data.realtime_pool_backed()
478 ? &sysex_copy_payload_pool_
480 event.data.realtime_pool_backed()
481 ? sysex_copy_payload_capacity_
485 sysex_copy_payload_pool_.reserve(
486 other.sysex_copy_payload_pool_.capacity());
487 for (std::size_t i = 0;
i <
other.sysex_copy_payload_pool_.size(); ++
i) {
488 std::vector<uint8_t> payload;
489 payload.reserve(sysex_copy_payload_capacity_);
490 sysex_copy_payload_pool_.push_back(std::move(payload));
493 void rebind_sysex_payloads_to_pool() {
494 for (
auto& event : sysex_) {
495 if (event.data.realtime_pool_backed()) {
496 event.data.set_realtime_pool_backed(
true,
497 &sysex_copy_payload_pool_,
498 sysex_copy_payload_capacity_);
502 static void saturating_increment(std::uint32_t& value) {
503 if (value < std::numeric_limits<std::uint32_t>::max()) {
507 void record_event_drop() { saturating_increment(dropped_events_); }
508 void record_sysex_drop() { saturating_increment(dropped_sysex_events_); }
514 void reserve_sort_scratch(std::size_t capacity) {
515 sort_index_.reserve(capacity);
516 sort_reorder_.reserve(capacity);
519 std::vector<MidiEvent> events_;
523 std::vector<std::uint32_t> sort_index_;
524 std::vector<MidiEvent> sort_reorder_;
525 std::vector<std::vector<uint8_t>> sysex_copy_payload_pool_;
526 std::vector<SysexEvent> sysex_;
528 std::size_t sysex_copy_payload_capacity_ = 0;
529 bool limit_to_reserved_capacity_ =
false;
530 std::uint32_t dropped_events_ = 0;
531 std::uint32_t dropped_sysex_events_ = 0;
Definition buffer.hpp:229
SysexPayload(std::initializer_list< uint8_t > values)
Definition buffer.hpp:232
SysexPayload(const SysexPayload &other)
Definition buffer.hpp:236
const uint8_t * data() const
Definition buffer.hpp:269
SysexPayload & operator=(std::initializer_list< uint8_t > values)
Definition buffer.hpp:255
auto end() const
Definition buffer.hpp:282
friend bool operator==(const SysexPayload &lhs, const std::vector< uint8_t > &rhs)
Definition buffer.hpp:292
~SysexPayload()
Definition buffer.hpp:250
auto end()
Definition buffer.hpp:284
void resize(std::size_t size)
Definition buffer.hpp:286
std::size_t capacity() const
Definition buffer.hpp:268
SysexPayload(SysexPayload &&)=delete
const uint8_t & front() const
Definition buffer.hpp:271
std::size_t size() const
Definition buffer.hpp:266
auto begin() const
Definition buffer.hpp:281
SysexPayload & operator=(SysexPayload &&)=delete
const uint8_t & operator[](std::size_t index) const
Definition buffer.hpp:275
SysexPayload(std::vector< uint8_t > bytes)
Definition buffer.hpp:233
const uint8_t & back() const
Definition buffer.hpp:273
void push_back(uint8_t byte)
Definition buffer.hpp:288
std::vector< uint8_t > to_vector() const
Definition buffer.hpp:290
SysexPayload & operator=(const SysexPayload &other)
Definition buffer.hpp:239
uint8_t & operator[](std::size_t index)
Definition buffer.hpp:278
friend class MidiBuffer
Definition buffer.hpp:310
friend bool operator!=(const SysexPayload &lhs, const std::vector< uint8_t > &rhs)
Definition buffer.hpp:300
bool empty() const
Definition buffer.hpp:267
uint8_t & back()
Definition buffer.hpp:274
friend bool operator==(const std::vector< uint8_t > &lhs, const SysexPayload &rhs)
Definition buffer.hpp:296
friend bool operator!=(const std::vector< uint8_t > &lhs, const SysexPayload &rhs)
Definition buffer.hpp:304
void clear()
Definition buffer.hpp:287
void reserve(std::size_t capacity)
Definition buffer.hpp:285
uint8_t * data()
Definition buffer.hpp:270
auto begin()
Definition buffer.hpp:283
uint8_t & front()
Definition buffer.hpp:272
SysexPayload & operator=(std::vector< uint8_t > bytes)
Definition buffer.hpp:259
Collection of timestamped MIDI events within a single audio buffer period.
Definition buffer.hpp:31
std::size_t size() const
Definition buffer.hpp:123
bool add(MidiEvent &&event)
Append a MIDI event to the buffer.
Definition buffer.hpp:108
void set_realtime_capacity_limit(bool enabled=true)
When enabled, add() and move-based add_sysex() drop once reserved capacity is full instead of growing...
Definition buffer.hpp:148
std::size_t event_capacity() const
Definition buffer.hpp:152
bool add_sysex(SysexEvent &&event)
Definition buffer.hpp:364
const std::vector< SysexEvent > & sysex() const
Definition buffer.hpp:416
bool empty() const
Definition buffer.hpp:122
bool add_sysex(std::vector< uint8_t > data, int32_t sample_offset=0, double ts=0.0)
Definition buffer.hpp:352
MidiBuffer & operator=(const MidiBuffer &other)
Definition buffer.hpp:43
std::size_t sysex_copy_payload_capacity() const
Definition buffer.hpp:154
auto end()
Definition buffer.hpp:200
void attach_ump(class UmpBuffer *ump)
Attach a UmpBuffer sidecar carrying MIDI 2.0 packets that can't be represented as choc::midi::ShortMe...
Definition buffer.hpp:212
void clear()
Remove all events.
Definition buffer.hpp:118
MidiBuffer & operator=(MidiBuffer &&other) noexcept
Definition buffer.hpp:74
std::vector< SysexEvent > & sysex()
Definition buffer.hpp:417
auto begin()
Definition buffer.hpp:199
std::uint32_t dropped_sysex_count() const
Definition buffer.hpp:158
MidiBuffer(MidiBuffer &&other) noexcept
Definition buffer.hpp:56
auto begin() const
Definition buffer.hpp:201
std::size_t sysex_size() const
Definition buffer.hpp:415
void sort()
Sort events by sample_offset for sample-accurate processing.
Definition buffer.hpp:177
const MidiEvent & operator[](std::size_t index) const
Definition buffer.hpp:204
bool realtime_capacity_limited() const
Definition buffer.hpp:151
const class UmpBuffer * ump() const
Attached UmpBuffer or nullptr.
Definition buffer.hpp:217
std::size_t sysex_capacity() const
Definition buffer.hpp:153
void clear_sysex()
Definition buffer.hpp:408
bool add_sysex_copy(const uint8_t *data, std::size_t size, int32_t sample_offset=0, double ts=0.0)
Definition buffer.hpp:382
void reserve_sysex(std::size_t capacity)
Definition buffer.hpp:128
void reserve_sysex_copy_payloads(std::size_t payload_count, std::size_t payload_capacity)
Definition buffer.hpp:397
bool add(const MidiEvent &event)
Append a MIDI event to the buffer.
Definition buffer.hpp:98
std::uint32_t dropped_event_count() const
Definition buffer.hpp:157
MidiBuffer(const MidiBuffer &other)
Definition buffer.hpp:34
void reserve_events(std::size_t capacity)
Definition buffer.hpp:124
class UmpBuffer * ump()
Definition buffer.hpp:218
auto end() const
Definition buffer.hpp:202
void reserve(std::size_t event_capacity, std::size_t sysex_capacity=0, std::size_t sysex_copy_payload_capacity=0)
Preallocate storage for realtime callers that append during process().
Definition buffer.hpp:131
A time-ordered list of UMP events for one audio block.
Definition ump_buffer.hpp:41
Definition custom_node_events.hpp:26
@ other
Definition udev_monitor.hpp:26
@ i
Definition formant_filter_bank.hpp:20
@ value
the widget's primary value (Knob, Fader, RangeSlider, Toggle, ProgressBar)
Definition binding_diagnostics.hpp:19
Definition buffer.hpp:346
double timestamp
absolute time in seconds
Definition buffer.hpp:349
int32_t sample_offset
sample position within the block
Definition buffer.hpp:348
SysexPayload data
full F0 .. F7 payload
Definition buffer.hpp:347
Timestamped MIDI event combining a choc::midi::ShortMessage with timing info.
Definition message.hpp:32