96using Sysex7EmitFn = void(*)(
const std::vector<std::uint8_t>& payload,
158 const std::uint8_t group =
static_cast<std::uint8_t
>((word0 >> 24) & 0x0F);
159 auto& stream = streams_[group];
161 const std::uint8_t status =
162 static_cast<std::uint8_t
>((word0 >> 20) & 0x0F);
163 const std::uint32_t size =
164 static_cast<std::uint32_t
>((word0 >> 16) & 0x0F);
167 stream.payload.clear();
168 stream.in_progress =
false;
172 if (!payload_is_7bit(word0, word1, size)) {
173 stream.payload.clear();
174 stream.in_progress =
false;
189 stream.payload.clear();
190 stream.in_progress =
false;
191 stream.scratch.clear();
192 if (size > maximum_payload_bytes_) {
196 extract_bytes(word0, word1, size, stream.scratch);
198 emit(stream.scratch, user);
207 if (stream.in_progress)
209 stream.payload.clear();
210 stream.in_progress =
false;
211 if (size > maximum_payload_bytes_) {
215 extract_bytes(word0, word1, size, stream.payload);
216 stream.in_progress =
true;
220 if (stream.in_progress) {
221 if (size > maximum_payload_bytes_ ||
222 stream.payload.size() > maximum_payload_bytes_ - size) {
223 stream.payload.clear();
224 stream.in_progress =
false;
228 extract_bytes(word0, word1, size, stream.payload);
235 if (stream.in_progress) {
236 if (size > maximum_payload_bytes_ ||
237 stream.payload.size() > maximum_payload_bytes_ - size) {
238 stream.payload.clear();
239 stream.in_progress =
false;
243 extract_bytes(word0, word1, size, stream.payload);
245 emit(stream.payload, user);
247 stream.payload.clear();
248 stream.in_progress =
false;
259 stream.payload.clear();
260 stream.in_progress =
false;
269 for (
const auto& stream : streams_)
270 if (stream.in_progress)
277 return streams_[group & 0x0F].payload.size();
288 maximum_payload_bytes_ = worst_case_bytes;
289 for (
auto& stream : streams_) {
290 stream.payload.reserve(worst_case_bytes);
291 stream.scratch.reserve(6);
296 return maximum_payload_bytes_;
300 return last_drop_reason_;
307 for (
auto& stream : streams_) {
308 stream.payload.clear();
309 stream.in_progress =
false;
318 StreamState() =
default;
319 StreamState(
const StreamState& other) {
322 StreamState& operator=(
const StreamState& other) {
325 payload.reserve(other.payload.capacity());
326 scratch.reserve(other.scratch.capacity());
327 payload.assign(other.payload.begin(), other.payload.end());
328 scratch.assign(other.scratch.begin(), other.scratch.end());
332 StreamState(StreamState&& other) noexcept
333 : payload(std::move(other.payload)), scratch(std::move(other.scratch)),
334 in_progress(std::exchange(other.in_progress,
false)) {}
335 StreamState& operator=(StreamState&& other)
noexcept {
338 payload = std::move(
other.payload);
339 scratch = std::move(
other.scratch);
340 in_progress = std::exchange(
other.in_progress,
false);
343 std::vector<std::uint8_t> payload;
344 std::vector<std::uint8_t> scratch;
345 bool in_progress =
false;
348 static bool payload_is_7bit(std::uint32_t w0, std::uint32_t w1, std::uint32_t size)
noexcept {
349 const std::uint8_t bytes[6] = {
350 static_cast<std::uint8_t
>((w0 >> 8) & 0xFF),
351 static_cast<std::uint8_t
>(w0 & 0xFF),
352 static_cast<std::uint8_t
>((w1 >> 24) & 0xFF),
353 static_cast<std::uint8_t
>((w1 >> 16) & 0xFF),
354 static_cast<std::uint8_t
>((w1 >> 8) & 0xFF),
355 static_cast<std::uint8_t
>(w1 & 0xFF),
357 for (std::uint32_t i = 0;
i <
size; ++
i)
358 if ((bytes[i] & 0x80) != 0)
363 static void extract_bytes(std::uint32_t w0, std::uint32_t w1,
365 std::vector<std::uint8_t>& out) {
375 const std::uint8_t buf[6] = {
376 static_cast<std::uint8_t
>((w0 >> 8) & 0xFF),
377 static_cast<std::uint8_t
>((w0 >> 0) & 0xFF),
378 static_cast<std::uint8_t
>((w1 >> 24) & 0xFF),
379 static_cast<std::uint8_t
>((w1 >> 16) & 0xFF),
380 static_cast<std::uint8_t
>((w1 >> 8) & 0xFF),
381 static_cast<std::uint8_t
>((w1 >> 0) & 0xFF),
383 for (std::uint32_t i = 0;
i <
size; ++
i)
384 out.push_back(buf[i]);
387 std::array<StreamState, 16> streams_{};
399 std::vector<std::uint8_t>& out) {
400 struct Box { std::vector<std::uint8_t>* sink; };
402 return r.feed_packet(word0, word1,
403 [](
const std::vector<std::uint8_t>& payload,
void* user) {
404 auto* b =
static_cast<Box*
>(user);
Reassembler state machine for UMP Type-0x3 sysex7 packet streams.
Definition ump_sysex7_reassembler.hpp:102
UmpSysex7Reassembler(std::size_t maximum_payload_bytes=default_maximum_payload_bytes)
Definition ump_sysex7_reassembler.hpp:141
void reserve(std::size_t worst_case_bytes)
Reserve worst-case payload capacity to avoid allocations in the hot path.
Definition ump_sysex7_reassembler.hpp:286
std::size_t partial_size(std::uint8_t group=0) const noexcept
Current accumulated payload size in bytes (0 when idle).
Definition ump_sysex7_reassembler.hpp:276
void reset() noexcept
Drop any in-progress sysex without emitting.
Definition ump_sysex7_reassembler.hpp:306
DropReason
Why the last packet was dropped (or why a partial message was reset).
Definition ump_sysex7_reassembler.hpp:130
@ invalid_size
Definition ump_sysex7_reassembler.hpp:134
@ overflow
Definition ump_sysex7_reassembler.hpp:136
@ none
Definition ump_sysex7_reassembler.hpp:131
@ reserved_status
Definition ump_sysex7_reassembler.hpp:133
@ high_bit_payload
Definition ump_sysex7_reassembler.hpp:135
@ orphan
Definition ump_sysex7_reassembler.hpp:132
@ reset
Definition ump_sysex7_reassembler.hpp:138
@ nested_start
Definition ump_sysex7_reassembler.hpp:137
static constexpr std::size_t default_maximum_payload_bytes
Definition ump_sysex7_reassembler.hpp:104
Status
Classification of the most recently consumed packet.
Definition ump_sysex7_reassembler.hpp:107
@ dropped
Continue / end without a preceding start, OR a packet whose status nibble is neither 0x0/0x1/0x2/0x3.
Definition ump_sysex7_reassembler.hpp:123
@ continued
Continue packet (status nibble 0x2) appended to an armed accumulator.
Definition ump_sysex7_reassembler.hpp:116
@ ended
End packet (status nibble 0x3) closed an armed accumulator.
Definition ump_sysex7_reassembler.hpp:119
@ single_packet
Single-packet sysex (status nibble 0x0).
Definition ump_sysex7_reassembler.hpp:110
@ start
Start packet (status nibble 0x1).
Definition ump_sysex7_reassembler.hpp:113
std::size_t maximum_payload_bytes() const noexcept
Definition ump_sysex7_reassembler.hpp:295
Status feed_packet(std::uint32_t word0, std::uint32_t word1, Sysex7EmitFn emit, void *user) noexcept
Feed a single 2-word UMP packet through the state machine.
Definition ump_sysex7_reassembler.hpp:156
bool in_progress() const noexcept
True when an in-progress sysex is open (a 0x1 start packet has been seen and no 0x3 end packet has cl...
Definition ump_sysex7_reassembler.hpp:268
DropReason last_drop_reason() const noexcept
Definition ump_sysex7_reassembler.hpp:299
Definition custom_node_events.hpp:26
void(*)(const std::vector< std::uint8_t > &payload, void *user) Sysex7EmitFn
Callback shape: receives the complete UMP SysEx7 payload bytes, with no synthesised MIDI 1....
Definition ump_sysex7_reassembler.hpp:96
UmpSysex7Reassembler::Status feed_collect(UmpSysex7Reassembler &r, std::uint32_t word0, std::uint32_t word1, std::vector< std::uint8_t > &out)
Convenience: feed a packet and collect the emitted payload (if any) into out.
Definition ump_sysex7_reassembler.hpp:397
@ other
Definition udev_monitor.hpp:26
@ size
Definition config.hpp:216
@ i
Definition formant_filter_bank.hpp:20