26struct GraphEventLaneTestAccess;
67 static_assert(Capacity > 0);
70 std::size_t frames)
noexcept {
73 if (
epoch == 0 || frames == 0 || frames > kMaxFrames ||
74 sample_start > std::numeric_limits<std::uint64_t>::max() - frames)
84 if (
epoch == epoch_) {
86 for (std::size_t i = 0; i < size_; ++i) {
88 static_cast<std::uint64_t
>(records_[i].absolute_sample) >= sample_start)
89 records_[keep++] = records_[i];
94 sample_start_ = sample_start;
101 if (!active_ || event.epoch != epoch_)
103 if (event.producer == 0)
105 if (event.absolute_sample < 0)
107 if (
static_cast<std::uint64_t
>(event.absolute_sample) < sample_start_)
109 for (std::size_t i = 0; i < size_; ++i) {
114 if (records_[i].producer == event.producer &&
115 event.producer_sequence == records_[i].producer_sequence)
118 if (size_ == Capacity)
120 records_[size_++] = event;
131 const auto end = sample_start_ + frames_;
132 std::size_t matching = 0;
133 for (std::size_t i = 0; i < size_; ++i) {
134 if (records_[i].
epoch != epoch_)
136 const auto sample =
static_cast<std::uint64_t
>(records_[i].absolute_sample);
137 if (sample >= sample_start_ && sample < end)
140 if (matching > out.size())
142 for (std::size_t i = 0; i < size_; ++i) {
143 if (records_[i].
epoch != epoch_)
145 const auto sample =
static_cast<std::uint64_t
>(records_[i].absolute_sample);
146 if (sample >= sample_start_ && sample < end)
147 out[count++] = records_[i];
151 for (std::size_t i = 1; i < count; ++i) {
154 while (j > 0 && less(value, out[j - 1])) {
160 std::size_t write = 0;
161 for (std::size_t i = 0; i < size_; ++i) {
162 if (records_[i].
epoch != epoch_)
164 const auto sample =
static_cast<std::uint64_t
>(records_[i].absolute_sample);
166 records_[write++] = records_[i];
187 std::uint64_t
epoch() const noexcept {
198 static constexpr std::size_t kMaxFrames = 4096;
200 const auto lsample =
static_cast<std::uint64_t
>(lhs.absolute_sample);
201 const auto rsample =
static_cast<std::uint64_t
>(rhs.absolute_sample);
202 if (lsample != rsample)
203 return lsample < rsample;
204 if (lhs.producer != rhs.producer)
205 return lhs.producer < rhs.producer;
206 return lhs.producer_sequence < rhs.producer_sequence;
209 std::array<HostEventRecord, Capacity> records_{};
210 std::uint64_t epoch_ = 0;
211 std::uint64_t sample_start_ = 0;
212 std::size_t frames_ = 0;
213 std::size_t size_ = 0;
214 bool active_ =
false;
223 bool dropped =
false;
230 if (!tracker.flush_pending_note_offs())
232 for (
const auto& event :
events) {
233 if (event.absolute_sample < 0)
235 const auto absolute =
static_cast<std::uint64_t
>(
event.absolute_sample);
236 if (absolute < block_start ||
237 absolute - block_start >
238 static_cast<std::uint64_t
>(std::numeric_limits<int32_t>::max()))
240 offset =
static_cast<int32_t
>(absolute - block_start);
242 (void)tracker.process(event.ump);
244 (
void)tracker.process(event.midi);
262 std::uint64_t block_start, std::size_t frames,
266 if (frames == 0 || frames > 4096 ||
267 block_start > std::numeric_limits<std::uint64_t>::max() - frames)
269 if ((tracker ==
nullptr) != (mpe_out ==
nullptr))
272 std::size_t midi_count = 0;
273 std::size_t ump_count = 0;
274 for (
const auto& record : records) {
275 if (record.absolute_sample < 0)
277 const auto absolute =
static_cast<std::uint64_t
>(record.absolute_sample);
278 if (absolute < block_start || absolute >= block_start + frames ||
279 absolute - block_start >
280 static_cast<std::uint64_t
>(std::numeric_limits<std::int32_t>::max()))
290 auto* ump = midi_out.
ump();
291 if (ump_count != 0 && ump ==
nullptr)
293 if (ump !=
nullptr && ump->realtime_capacity_limited() &&
294 ump_count > ump->capacity() - std::min(ump->capacity(), ump->size()))
296 if (mpe_out !=
nullptr) {
304 const auto event_mpe =
305 records.size() > std::numeric_limits<std::size_t>::max() / kMpeRecordsPerSource
306 ? std::numeric_limits<std::size_t>::max()
307 : records.size() * kMpeRecordsPerSource;
308 const auto pending_mpe = tracker->pending_note_off_count();
309 const auto required_mpe = pending_mpe > std::numeric_limits<std::size_t>::max() - event_mpe
310 ? std::numeric_limits<std::size_t>::max()
311 : pending_mpe + event_mpe;
312 if (required_mpe > mpe_out->capacity() - std::min(mpe_out->capacity(), mpe_out->size()))
316 for (
const auto& record : records) {
317 auto event = record.midi;
318 event.sample_offset =
static_cast<std::int32_t
>(
319 static_cast<std::uint64_t
>(record.absolute_sample) - block_start);
320 if (!midi_out.
add(event))
322 if (record.has_ump && !ump->add(record.ump, event.sample_offset))
328 if (tracker !=
nullptr && mpe_out !=
nullptr) {
329 const auto result =
derive_mpe(records, block_start, *tracker, *mpe_out);
374template <std::
size_t MaxFrames = 4096, std::
size_t MaxAcceptedPerSample = 8>
383 if (
epoch == 0 || active_)
387 counters_.epoch =
epoch;
391 consumed_frontier_ = 0;
396 std::size_t frames)
noexcept {
399 if (
epoch == 0 ||
epoch != epoch_ || frames == 0 || frames > MaxFrames ||
400 sample_start > std::numeric_limits<std::uint64_t>::max() - frames)
402 block_start_ = sample_start;
403 block_frames_ = frames;
411 if (!active_ || offset != sample_offset_ || offset >= block_frames_)
413 current_sample_ = block_start_ + offset;
414 attempts_ = accepted_ = 0;
415 attempt_closed_ =
false;
416 sample_begin_ = trace_size_;
422 std::uint64_t target_sample, std::uint8_t source_port = 0,
423 std::uint8_t kind = 1) noexcept {
427 ++counters_.diagnostic_refusals;
431 attempt_closed_ =
true;
435 counters_.attempts = attempts_;
440 counters_.sequence = sequence_;
442 ++counters_.diagnostic_refusals;
447 if (producer == 0 || source_port != 0 || kind != 1)
449 if (target_sample < current_sample_)
451 if (target_sample > current_sample_)
453 if (accepted_ == MaxAcceptedPerSample) {
454 if (counters_.overflow != std::numeric_limits<std::uint64_t>::max())
455 ++counters_.overflow;
458 records_[trace_size_++] = {epoch_, current_sample_,
sequence, producer,
459 payload, source_port, kind};
461 counters_.accepted = accepted_;
466 if (!active_ || phase_ != 1)
471 std::span<const GraphAuthoredEvent>
received() const noexcept {
474 return {records_.data() + sample_begin_, trace_size_ - sample_begin_};
477 if (!active_ || phase_ != 2)
480 consumed_frontier_ = current_sample_ + 1;
485 if (!active_ || phase_ != 0 || sample_offset_ != block_frames_)
487 if (cursor_ > std::numeric_limits<std::uint64_t>::max() - block_frames_) {
494 cursor_ += block_frames_;
495 counters_.cursor = cursor_;
503 std::span<const GraphAuthoredEvent>
trace() const noexcept {
504 return {records_.data(), trace_size_};
506 std::uint64_t
epoch() const noexcept {
516 void test_set_cursor_(std::uint64_t cursor)
noexcept {
520 std::array<GraphAuthoredEvent, MaxFrames * MaxAcceptedPerSample> records_{};
521 GraphEventCounters counters_{};
522 std::uint64_t epoch_ = 0;
523 std::uint64_t cursor_ = 0;
524 std::uint64_t sequence_ = 0;
525 std::uint64_t block_start_ = 0;
526 std::uint64_t current_sample_ = 0;
527 std::uint64_t consumed_frontier_ = 0;
528 std::size_t block_frames_ = 0;
529 std::size_t sample_offset_ = 0;
530 std::size_t sample_begin_ = 0;
531 std::size_t trace_size_ = 0;
532 std::uint8_t attempts_ = 0;
533 std::uint8_t accepted_ = 0;
534 std::uint8_t phase_ = 0;
535 bool attempt_closed_ =
false;
536 bool active_ =
false;
Explicit producer/consumer phases for a bounded, single-producer event lane.
Definition graph_event_contract.hpp:375
GraphEventResult reset(std::uint64_t epoch) noexcept
Definition graph_event_contract.hpp:382
GraphEventResult end_sample() noexcept
Definition graph_event_contract.hpp:476
GraphEventResult emit(std::uint64_t epoch, std::uint32_t producer, std::uint16_t payload, std::uint64_t target_sample, std::uint8_t source_port=0, std::uint8_t kind=1) noexcept
Definition graph_event_contract.hpp:421
static constexpr std::size_t kRecordBytes
Definition graph_event_contract.hpp:378
GraphEventResult begin_consumer() noexcept
Definition graph_event_contract.hpp:465
std::span< const GraphAuthoredEvent > trace() const noexcept
Definition graph_event_contract.hpp:503
std::span< const GraphAuthoredEvent > received() const noexcept
Definition graph_event_contract.hpp:471
friend struct detail::GraphEventLaneTestAccess
Definition graph_event_contract.hpp:514
GraphEventResult begin_sample(std::size_t offset) noexcept
Definition graph_event_contract.hpp:410
GraphEventResult end_block() noexcept
Definition graph_event_contract.hpp:484
GraphEventResult begin_block(std::uint64_t epoch, std::uint64_t sample_start, std::size_t frames) noexcept
Definition graph_event_contract.hpp:395
const GraphEventCounters & counters() const noexcept
Definition graph_event_contract.hpp:500
bool active() const noexcept
Definition graph_event_contract.hpp:509
static constexpr std::size_t kMaxAttemptsPerSample
Definition graph_event_contract.hpp:377
std::uint64_t epoch() const noexcept
Definition graph_event_contract.hpp:506
Fixed-capacity host ingress staging area.
Definition graph_event_contract.hpp:65
bool active() const noexcept
Definition graph_event_contract.hpp:193
std::size_t pending() const noexcept
Definition graph_event_contract.hpp:190
std::uint64_t epoch() const noexcept
Definition graph_event_contract.hpp:187
void reset(std::uint64_t epoch) noexcept
Definition graph_event_contract.hpp:180
HostEventResult begin_block(std::uint64_t epoch, std::uint64_t sample_start, std::size_t frames) noexcept
Definition graph_event_contract.hpp:69
HostEventResult project(std::span< HostEventRecord > out, std::size_t &count) noexcept
Copy events in [sample_start, sample_start + frames) into out.
Definition graph_event_contract.hpp:127
HostEventResult end_block() noexcept
Finish the current block. Future records remain staged.
Definition graph_event_contract.hpp:173
HostEventResult publish(const HostEventRecord &event) noexcept
Definition graph_event_contract.hpp:100
Collection of timestamped MIDI events within a single audio buffer period.
Definition buffer.hpp:31
std::size_t size() const
Definition buffer.hpp:123
std::size_t event_capacity() const
Definition buffer.hpp:152
void sort()
Sort events by sample_offset for sample-accurate processing.
Definition buffer.hpp:177
bool realtime_capacity_limited() const
Definition buffer.hpp:151
const class UmpBuffer * ump() const
Attached UmpBuffer or nullptr.
Definition buffer.hpp:217
bool add(const MidiEvent &event)
Append a MIDI event to the buffer.
Definition buffer.hpp:98
A time-ordered list of per-note expression events for one block.
Definition mpe_buffer.hpp:51
Tracks MPE voice state across channels.
Definition mpe_voice_tracker.hpp:67
static constexpr std::size_t kMaxNotes
Maximum concurrently active notes the tracker can hold.
Definition mpe_voice_tracker.hpp:70
Sample-accurate MPE expression event stream.
Definition child_process_manager.hpp:18
Definition baked_codec.hpp:158
Bounded event contracts shared by host ingress and graph-authored nodes.
Definition anticipation_eligibility.hpp:9
HostEventResult
Definition graph_event_contract.hpp:29
@ Overflow
Definition graph_event_contract.hpp:35
@ OutOfRange
Definition graph_event_contract.hpp:33
@ Accepted
Definition graph_event_contract.hpp:30
@ OutputCapacity
Definition graph_event_contract.hpp:37
@ Busy
Definition graph_event_contract.hpp:38
@ Late
Definition graph_event_contract.hpp:34
@ Unbound
Definition graph_event_contract.hpp:32
@ StaleEpoch
Definition graph_event_contract.hpp:31
@ InvalidSequence
Definition graph_event_contract.hpp:36
HostEventResult project_host_event_block(std::span< const HostEventRecord > records, std::uint64_t block_start, std::size_t frames, midi::MidiBuffer &midi_out, midi::MpeVoiceTracker *tracker=nullptr, midi::MpeBuffer *mpe_out=nullptr) noexcept
Project an already-sorted absolute host-event block into the fixed MIDI, UMP, and optional MPE sideca...
Definition graph_event_contract.hpp:261
@ Busy
Definition custom_node_diagnostics.hpp:18
GraphEventResult
Definition graph_event_contract.hpp:336
@ Overflow
Definition graph_event_contract.hpp:343
@ OutOfRange
Definition graph_event_contract.hpp:340
@ Accepted
Definition graph_event_contract.hpp:337
@ InvalidPhase
Definition graph_event_contract.hpp:345
@ Future
Definition graph_event_contract.hpp:341
@ AttemptLimit
Definition graph_event_contract.hpp:344
@ Busy
Definition graph_event_contract.hpp:346
@ Late
Definition graph_event_contract.hpp:342
@ Unbound
Definition graph_event_contract.hpp:339
@ StaleEpoch
Definition graph_event_contract.hpp:338
HostEventResult derive_mpe(std::span< const HostEventRecord > events, std::uint64_t block_start, midi::MpeVoiceTracker &tracker, midi::MpeBuffer &out) noexcept
Convert projected records to the existing MPE sidecar.
Definition graph_event_contract.hpp:220
void bind_tracker_to_buffer(MpeVoiceTracker &tracker, MpeBuffer &out, int32_t ¤t_sample_offset, bool *expression_event_dropped=nullptr)
Convenience: install tracker callbacks that forward events to out with the given sample offset.
Definition mpe_buffer.hpp:163
Definition host_transport_projector.hpp:8
Definition graph_event_contract.hpp:349
std::uint8_t kind
Definition graph_event_contract.hpp:356
std::uint32_t producer
Definition graph_event_contract.hpp:353
std::uint8_t source_port
Definition graph_event_contract.hpp:355
std::uint64_t epoch
Definition graph_event_contract.hpp:350
std::uint64_t sequence
Definition graph_event_contract.hpp:352
friend bool operator==(const GraphAuthoredEvent &, const GraphAuthoredEvent &)=default
std::uint16_t payload
Definition graph_event_contract.hpp:354
std::uint64_t sample
Definition graph_event_contract.hpp:351
Definition graph_event_contract.hpp:361
std::uint64_t overflow
Definition graph_event_contract.hpp:365
std::uint64_t sequence
Definition graph_event_contract.hpp:363
std::uint64_t cursor
Definition graph_event_contract.hpp:362
std::uint64_t epoch
Definition graph_event_contract.hpp:364
std::uint8_t attempts
Definition graph_event_contract.hpp:367
std::uint8_t accepted
Definition graph_event_contract.hpp:368
std::uint64_t diagnostic_refusals
Definition graph_event_contract.hpp:366
One immutable host event in the graph's epoch-local sample domain.
Definition graph_event_contract.hpp:48
midi::UmpPacket ump
Definition graph_event_contract.hpp:54
midi::MidiEvent midi
Definition graph_event_contract.hpp:53
std::uint32_t producer
Definition graph_event_contract.hpp:52
std::int64_t absolute_sample
Definition graph_event_contract.hpp:50
bool has_ump
Definition graph_event_contract.hpp:55
std::uint64_t producer_sequence
Definition graph_event_contract.hpp:51
std::uint64_t epoch
Definition graph_event_contract.hpp:49
Timestamped MIDI event combining a choc::midi::ShortMessage with timing info.
Definition message.hpp:32
Sample-accurate MIDI 2.0 UMP packet stream.