Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
running_status.hpp
Go to the documentation of this file.
1#pragma once
2
3// MIDI 1.0 running-status byte-stream parser.
4//
5// Platform MIDI backends (ALSA raw MIDI, Win32 mmeapi MIM_LONGDATA, BLE
6// MIDI) deliver raw bytes — status bytes may be omitted when they would
7// repeat the previous channel-voice status ("running status"). The prior
8// ALSA MIDI parser had a simple state machine that didn't track running
9// status, so a note-on sequence like `90 3C 7F 3D 7F 3E 7F` would emit
10// only the first note. This parser handles running status, real-time
11// interleave, and system-common/sysex boundaries correctly.
12//
13// Pure C++ — consumed by platform backends + unit-tested without any
14// MIDI device attached.
15//
16// RT-safety contract:
17//
18// - Install callbacks and call reserve_sysex(N) before entering the MIDI
19// callback, where N is the largest SysEx payload body the port accepts
20// after F0 and before F7.
21// - After that preparation, feed() and reset() do not allocate while SysEx
22// payloads stay within the reserved size.
23// - Callback objects and callback-owned destination storage are caller-owned.
24// Keep them preconstructed and allocation-free when using this parser from
25// realtime code.
26
27#include <pulp/midi/message.hpp>
28
29#include <cstddef>
30#include <cstdint>
31#include <functional>
32#include <vector>
33
34namespace pulp::midi {
35
38using ShortMessageSink = std::function<void(const MidiEvent&)>;
39using SysexSink = std::function<void(const uint8_t* data, std::size_t size)>;
40
42public:
44
45 void on_short_message(ShortMessageSink sink) { short_sink_ = std::move(sink); }
46 void on_sysex(SysexSink sink) { sysex_sink_ = std::move(sink); }
47
50 void reserve_sysex(std::size_t worst_case_body_bytes);
51
54 void feed(const uint8_t* data, std::size_t size);
55
58 void reset();
59
60private:
61 static int expected_data_bytes(uint8_t status);
62 void emit_short(uint8_t status, uint8_t d1, uint8_t d2);
63
64 uint8_t running_status_ = 0;
65 uint8_t current_system_common_ = 0;
66 uint8_t data_[2] = {0, 0};
67 int data_count_ = 0;
68 int data_expected_ = 0;
69 bool in_sysex_ = false;
70 std::vector<uint8_t> sysex_buf_;
71
72 ShortMessageSink short_sink_;
73 SysexSink sysex_sink_;
74};
75
76} // namespace pulp::midi
void on_sysex(SysexSink sink)
Definition running_status.hpp:46
void on_short_message(ShortMessageSink sink)
Definition running_status.hpp:45
void reset()
Reset the parser — clears running status + any in-flight sysex.
void feed(const uint8_t *data, std::size_t size)
Feed a byte stream.
void reserve_sysex(std::size_t worst_case_body_bytes)
Prepare internal SysEx body storage for realtime use.
Definition custom_node_events.hpp:26
std::function< void(const MidiEvent &)> ShortMessageSink
Callback invoked for each complete short message.
Definition running_status.hpp:38
std::function< void(const uint8_t *data, std::size_t size)> SysexSink
Definition running_status.hpp:39
Timestamped MIDI event combining a choc::midi::ShortMessage with timing info.
Definition message.hpp:32