17#include "utils/units.h"
32 units::sample_u32_t
time () const noexcept [[clang::nonblocking]]
34 return units::samples (time_raw_);
37 std::span<const midi_byte_t> data () const noexcept [[clang::nonblocking]]
39 return { data_, size_ };
43 friend class MidiEventBuffer;
45 MidiEventView (uint32_t t,
const midi_byte_t * d, uint16_t s) noexcept
46 : time_raw_ (t), data_ (d), size_ (s)
77 static constexpr size_t kHeaderSize =
sizeof (uint16_t) +
sizeof (uint32_t);
107 using iterator_category = std::forward_iterator_tag;
109 using difference_type = std::ptrdiff_t;
110 using pointer = void;
113 Iterator () =
default;
115 : ptr_ (ptr), end_ (end)
121 assert (ptr_ !=
nullptr && ptr_ < end_);
123 std::memcpy (&
size, ptr_,
sizeof (uint16_t));
125 std::memcpy (&time_raw, ptr_ +
sizeof (uint16_t),
sizeof (uint32_t));
129 Iterator &operator++ ()
noexcept
131 assert (ptr_ !=
nullptr && ptr_ < end_);
133 std::memcpy (&
size, ptr_,
sizeof (uint16_t));
138 Iterator operator++ (
int)
noexcept
140 Iterator tmp = *
this;
145 bool operator== (
const Iterator &other)
const noexcept
147 return ptr_ == other.ptr_;
166 units::sample_u32_t time,
167 std::span<const midi_byte_t> event_data)
noexcept [[clang::nonblocking]]
169 assert (event_data.size () <= std::numeric_limits<uint16_t>::max ());
170 const auto total =
kHeaderSize + event_data.size ();
171 const auto old_size = storage_.size ();
172 assert (old_size + total <= storage_.capacity ());
173 if (old_size + total > storage_.capacity ())
175 storage_.resize (old_size + total);
177 const auto time_raw = time.in<uint32_t> (units::samples);
178 auto * ptr = storage_.data () + old_size;
179 auto sz =
static_cast<uint16_t
> (event_data.size ());
180 std::memcpy (ptr, &sz,
sizeof (uint16_t));
181 std::memcpy (ptr +
sizeof (uint16_t), &time_raw,
sizeof (uint32_t));
182 std::memcpy (ptr +
kHeaderSize, event_data.data (), event_data.size ());
188 void clear () noexcept [[clang::nonblocking]]
197 storage_.reserve (bytes);
198 scratch_.reserve (bytes);
205 storage_.swap (other.storage_);
206 scratch_.swap (other.scratch_);
207 index_.swap (other.index_);
208 std::swap (event_count_, other.event_count_);
211 bool empty () const noexcept {
return event_count_ == 0; }
213 size_t size () const noexcept {
return event_count_; }
217 size_t capacity () const noexcept {
return storage_.capacity (); }
225 Iterator begin () const noexcept
227 return Iterator (storage_.data (), storage_.data () + storage_.size ());
230 Iterator end () const noexcept
232 const auto * end_ptr = storage_.data () + storage_.size ();
233 return Iterator (end_ptr, end_ptr);
241 template <
typename Comp>
242 void sort (Comp &&comp)
noexcept [[clang::nonblocking]]
244 if (event_count_ <= 1)
247 assert (storage_.size () <= scratch_.capacity ());
248 scratch_.resize (storage_.size ());
249 assert (event_count_ <= index_.capacity ());
253 while (offset < storage_.size ())
256 std::memcpy (&
size, storage_.data () + offset, sizeof (uint16_t));
259 &time_raw, storage_.data () + offset + sizeof (uint16_t),
261 assert (index_.size () < index_.capacity ());
262 index_.push_back ({ offset, time_raw,
size });
266 const auto * storage_ptr = storage_.data ();
268 index_, [storage_ptr, &comp] (
const IndexEntry &a,
const IndexEntry &b) {
270 a.time_raw, storage_ptr + a.offset +
kHeaderSize, a.size
273 b.time_raw, storage_ptr + b.offset +
kHeaderSize, b.size
275 return comp (va, vb);
278 size_t write_pos = 0;
279 for (
const auto &entry : index_)
283 scratch_.data () + write_pos, storage_.data () + entry.offset, chunk);
287 scratch_.resize (write_pos);
288 storage_.swap (scratch_);
292 template <
typename Pred>
293 void remove_if (Pred &&pred)
noexcept [[clang::nonblocking]]
298 assert (storage_.size () <= scratch_.capacity ());
299 scratch_.resize (storage_.size ());
301 size_t write_pos = 0;
302 size_t new_count = 0;
303 const auto * read_ptr = storage_.data ();
304 const auto * end_ptr = storage_.data () + storage_.size ();
306 while (read_ptr < end_ptr)
309 std::memcpy (&
size, read_ptr,
sizeof (uint16_t));
311 std::memcpy (&time_raw, read_ptr +
sizeof (uint16_t),
sizeof (uint32_t));
318 std::memcpy (scratch_.data () + write_pos, read_ptr, chunk);
326 if (new_count == event_count_)
329 scratch_.resize (write_pos);
330 storage_.swap (scratch_);
331 event_count_ = new_count;
342 std::vector<midi_byte_t> storage_;
343 std::vector<midi_byte_t> scratch_;
344 std::vector<IndexEntry> index_;
345 size_t event_count_ = 0;
353 [[clang::nonblocking]]
355 buf.sort ([] (
const MidiEventView &a,
const MidiEventView &b) {
356 if (a.time () != b.time ())
357 return a.time () < b.time ();
358 return !utils::midi::midi_is_note_on (a.data ())
359 && utils::midi::midi_is_note_on (b.data ());
371 std::pair<units::sample_u32_t, units::sample_u32_t> range)
noexcept
372 [[clang::nonblocking]]
374 for (
const auto &ev : src)
376 if (ev.time () >= range.first && ev.time () < range.second)
377 dst.push_back (ev.time (), ev.data ());
Packed contiguous buffer for RT MIDI events.
void remove_if(Pred &&pred) noexcept
Remove all events matching the predicate.
void sort(Comp &&comp) noexcept
Sort events using the given comparator on MidiEventView.
size_t size_in_bytes() const noexcept
Bytes used in storage.
static constexpr size_t kMaxReserveBytes
Max bytes to hold in queues.
static constexpr size_t kDefaultReserveBytes
Default pre-allocation.
size_t capacity() const noexcept
Bytes reserved in storage.
static constexpr size_t kHeaderSize
Bytes per event header: 2 (size) + 4 (timestamp).
size_t size() const noexcept
Number of events (not bytes).
static MidiEventBuffer make_reserved()
Create a pre-allocated buffer suitable for test use.
void reserve(size_t bytes=kMaxReserveBytes)
Pre-allocate internal byte storage (and scratch/index buffers).
void clear() noexcept
Reset buffer.
bool push_back(units::sample_u32_t time, std::span< const midi_byte_t > event_data) noexcept
Append an event.
std::uint8_t midi_byte_t
MIDI byte.
Factory functions and algorithms for MidiEvent containers.
void sort_with_note_off_priority(Container &container)
Sorts events by time, with noteOff before noteOn at the same timestamp.
Non-owning view into a MIDI event stored in a MidiEventBuffer.
units::sample_u32_t time() const noexcept
Event timestamp as a sample offset within the current processing cycle.