Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
block_ops.hpp
Go to the documentation of this file.
1// block_ops.hpp — whole-buffer MIDI block clear / drop-check / copy helpers.
2//
3// These three operations reset a MIDI block, test whether it dropped any
4// message (event / sysex / UMP) against its realtime capacity, and append one
5// block's full contents into another while propagating that incompleteness.
6// The host graph's reference walk and the format layer's routed executor both
7// gather inbound MIDI edge-by-edge, and their bit-exactness (routed-vs-walk
8// parity) depends on these semantics being identical on both sides of the
9// host/format boundary — which cannot share a header from either layer. They
10// live here, in core/midi, the one module both already depend on.
11//
12// Defined `inline` so every including TU shares one ODR-safe definition.
13#pragma once
14
15#include <pulp/midi/buffer.hpp>
18#include <algorithm>
19#include <array>
20#include <span>
21
22namespace pulp::midi {
23
24// Clear a MIDI block's events, sysex, and attached UMP — the full reset applied
25// before gathering a node's inbound MIDI.
26inline void clear_midi_block(MidiBuffer& block) noexcept {
27 block.clear();
28 block.clear_sysex();
29 if (auto* ump = block.ump()) ump->clear();
30}
31
32// True if the block dropped any event / sysex / UMP message (capacity limit
33// hit).
34inline bool midi_block_has_drops(const MidiBuffer& block) noexcept {
35 if (block.dropped_event_count() > 0 || block.dropped_sysex_count() > 0) {
36 return true;
37 }
38 const auto* ump = block.ump();
39 return ump != nullptr && ump->dropped_event_count() > 0;
40}
41
42// Append only SysEx and UMP sidecars after a caller has transformed the short
43// messages. This keeps sidecar capacity and missing-destination behavior shared
44// with whole-block copies.
45inline bool copy_midi_sidecars(const MidiBuffer& src, MidiBuffer& dst) noexcept {
46 bool copied_all = !midi_block_has_drops(src);
47 for (const auto& sx : src.sysex()) {
48 if (sx.data.empty()) {
49 if (!dst.add_sysex({}, sx.sample_offset, sx.timestamp)) {
50 copied_all = false;
51 }
52 } else if (!dst.add_sysex_copy(sx.data.data(), sx.data.size(),
53 sx.sample_offset, sx.timestamp)) {
54 copied_all = false;
55 }
56 }
57 const auto* src_ump = src.ump();
58 auto* dst_ump = dst.ump();
59 if (src_ump != nullptr && dst_ump != nullptr) {
60 for (const auto& ev : *src_ump) {
61 if (!dst_ump->add(ev)) copied_all = false;
62 }
63 } else if (src_ump != nullptr && !src_ump->empty()) {
64 copied_all = false;
65 }
66 return copied_all;
67}
68
69// Append every event / sysex / UMP message from `src` to `dst`. Returns false
70// if `src` already carried a drop or an add() dropped here (the incompleteness
71// propagated downstream). RT-safe when both buffers are reserved (add() respects
72// the realtime capacity limit).
73inline bool copy_midi_block(const MidiBuffer& src, MidiBuffer& dst) noexcept {
74 bool copied_all = !midi_block_has_drops(src);
75 for (const auto& ev : src) {
76 if (!dst.add(ev)) copied_all = false;
77 }
78 return copy_midi_sidecars(src, dst) && copied_all;
79}
80
81// Append an MPE sidecar while preserving producer order. The fixed chunk keeps
82// the copy bounded even when a caller supplies a large prepared buffer.
83inline bool copy_mpe_block(const MpeBuffer& src, MpeBuffer& dst) noexcept {
84 bool copied_all = src.dropped_event_count() == 0;
85 constexpr std::size_t kChunk = 64;
86 std::array<MpeExpressionEvent, kChunk> chunk{};
87 std::size_t offset = 0;
88 while (offset < src.size()) {
89 const auto count = std::min<std::size_t>(kChunk, src.size() - offset);
90 for (std::size_t i = 0; i < count; ++i)
91 chunk[i] = src[offset + i];
92 if (!dst.add_batch(std::span<const MpeExpressionEvent>(chunk.data(), count)))
93 copied_all = false;
94 offset += count;
95 }
96 return copied_all;
97}
98
99inline bool mpe_buffer_has_drops(const MpeBuffer& block) noexcept {
100 return block.dropped_event_count() != 0;
101}
102
103} // namespace pulp::midi
Collection of timestamped MIDI events within a single audio buffer period.
Definition buffer.hpp:31
A time-ordered list of per-note expression events for one block.
Definition mpe_buffer.hpp:51
Sample-accurate MPE expression event stream.
Definition custom_node_events.hpp:26
void clear_midi_block(MidiBuffer &block) noexcept
Definition block_ops.hpp:26
bool copy_midi_sidecars(const MidiBuffer &src, MidiBuffer &dst) noexcept
Definition block_ops.hpp:45
bool mpe_buffer_has_drops(const MpeBuffer &block) noexcept
Definition block_ops.hpp:99
bool copy_midi_block(const MidiBuffer &src, MidiBuffer &dst) noexcept
Definition block_ops.hpp:73
bool copy_mpe_block(const MpeBuffer &src, MpeBuffer &dst) noexcept
Definition block_ops.hpp:83
bool midi_block_has_drops(const MidiBuffer &block) noexcept
Definition block_ops.hpp:34
Sample-accurate MIDI 2.0 UMP packet stream.