Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
ble_midi.hpp
Go to the documentation of this file.
1#pragma once
2
3// BLE MIDI — Bluetooth Low Energy MIDI 1.0 transport (Apple BLE-MIDI 1.0 spec,
4// also adopted by the MIDI Association in 2015). This header declares the
5// cross-platform discovery / connection / I/O surface that backends implement.
6//
7// Backends:
8// macOS / iOS: CoreBluetooth (CBCentralManager + CBPeripheral) bridged
9// into CoreMIDI via the well-known BLE-MIDI service UUID
10// 03B80E5A-EDE8-4B33-A751-6CE34EC4C700. On Apple the
11// CoreMIDI driver also exposes paired BLE peripherals as
12// regular MIDI endpoints; this surface adds explicit
13// scan-and-pair control for unpaired peripherals.
14// Linux: BlueZ D-Bus (org.bluez.GattCharacteristic1 notifications) +
15// ALSA virtual port for output.
16// Windows: WinRT BluetoothLEAdvertisementWatcher scan +
17// GATT FromBluetoothAddressAsync connect +
18// GattCharacteristic.ValueChanged notify, bridged into the
19// MidiSystem via the BleMidiPortRegistry.
20// Android: BluetoothLeScanner + BluetoothGatt notification
21// via the existing JNI bridge. Scaffold only.
22//
23// Spec reference (BLE-MIDI 1.0):
24// Service UUID: 03B80E5A-EDE8-4B33-A751-6CE34EC4C700
25// Characteristic UUID: 7772E5DB-3868-4112-A1A9-F2669D106BF3 (read/write/notify)
26//
27// Packet format (per 20-byte MTU minimum):
28// header = 0x80 | (timestamp_high & 0x3F) (top 6 bits of 13-bit ms)
29// tstmp = 0x80 | (timestamp_low & 0x7F) (bottom 7 bits)
30// [status, data1, data2, ...]
31// Running status, multi-message-per-packet, and split-sysex follow the
32// Apple spec — backends MUST reassemble before delivering to callbacks.
33//
34// Baseline behavior: discover BLE MIDI peripherals, connect to one, and
35// receive MIDI events (notes) via the standard MidiInputCallback path.
36// Pairing UI is delegated to the platform — the scanner surfaces peripherals
37// and the host app drives the pairing dialog (iOS CABTMIDICentralViewController,
38// macOS equivalent, or a custom widget).
39
40#include <pulp/midi/device.hpp>
41
42#include <chrono>
43#include <cstdint>
44#include <functional>
45#include <memory>
46#include <string>
47#include <vector>
48
49namespace pulp::midi {
50
56 std::string id;
57 std::string name;
59 int rssi = 0;
63 std::chrono::steady_clock::time_point last_seen{};
67 bool is_paired = false;
68};
69
80
95
99using BleMidiScanCallback = std::function<void(const BleMidiPeripheral&)>;
100
104 std::function<void(const std::string& peripheral_id,
106 BleMidiError error)>;
107
113public:
114 virtual ~BleMidiCentral() = default;
115
119 virtual bool is_available() const = 0;
120
127 virtual bool start_scan(BleMidiScanCallback callback) = 0;
128 virtual void stop_scan() = 0;
129 virtual bool is_scanning() const = 0;
130
135 virtual std::vector<BleMidiPeripheral> known_peripherals() const = 0;
136
141 virtual bool connect(const std::string& peripheral_id) = 0;
142 virtual void disconnect(const std::string& peripheral_id) = 0;
143
144 virtual void set_state_callback(BleMidiStateCallback callback) = 0;
145
151 virtual std::string midi_input_port_for(const std::string& peripheral_id) const = 0;
152 virtual std::string midi_output_port_for(const std::string& peripheral_id) const = 0;
153};
154
158std::unique_ptr<BleMidiCentral> create_ble_midi_central();
159
160// ── BLE-MIDI 1.0 packet codec ──────────────────────────────────────────────
161//
162// The codec is cross-platform and Skia-free — every backend reuses it.
163// Reassembles 13-bit timestamps, running status, and multi-message
164// packets per the BLE-MIDI 1.0 spec. Exposed for tests + for the BlueZ
165// / WinRT backends which receive raw GATT bytes and must produce
166// MidiEvents.
167
170 static constexpr const char* kService =
171 "03B80E5A-EDE8-4B33-A751-6CE34EC4C700";
172 static constexpr const char* kCharacteristic =
173 "7772E5DB-3868-4112-A1A9-F2669D106BF3";
174};
175
180public:
182 std::function<void(const std::vector<uint8_t>& bytes,
183 uint32_t timestamp_ms)>;
184
185 void set_message_callback(MessageCallback cb) { cb_ = std::move(cb); }
186
191 bool decode(const uint8_t* data, std::size_t size);
192
196 void reset();
197
198private:
199 MessageCallback cb_;
200 uint8_t last_status_ = 0;
203 std::vector<uint8_t> sysex_buffer_;
205 uint8_t last_header_ts_ = 0;
206};
207
212std::vector<uint8_t> encode_ble_midi_packet(const uint8_t* msg,
213 std::size_t size,
214 uint32_t timestamp_ms);
215
216} // namespace pulp::midi
Cross-platform BLE MIDI central.
Definition ble_midi.hpp:112
virtual ~BleMidiCentral()=default
virtual std::string midi_input_port_for(const std::string &peripheral_id) const =0
MIDI port-id that MidiSystem::enumerate_inputs() returns once the peripheral is Connected.
virtual bool connect(const std::string &peripheral_id)=0
Begin a connection.
virtual void disconnect(const std::string &peripheral_id)=0
virtual std::string midi_output_port_for(const std::string &peripheral_id) const =0
virtual bool start_scan(BleMidiScanCallback callback)=0
Start scanning.
virtual bool is_scanning() const =0
virtual void stop_scan()=0
virtual bool is_available() const =0
True if the running platform has a BLE MIDI backend compiled in AND the user/system has granted Bluet...
virtual void set_state_callback(BleMidiStateCallback callback)=0
virtual std::vector< BleMidiPeripheral > known_peripherals() const =0
Snapshot of peripherals currently known to the central.
Stateful decoder.
Definition ble_midi.hpp:179
void reset()
Reset running status + the in-flight sysex buffer.
void set_message_callback(MessageCallback cb)
Definition ble_midi.hpp:185
std::function< void(const std::vector< uint8_t > &bytes, uint32_t timestamp_ms)> MessageCallback
Definition ble_midi.hpp:181
bool decode(const uint8_t *data, std::size_t size)
Feed one notification packet.
Definition custom_node_events.hpp:26
BleMidiError
Backend-reported error code.
Definition ble_midi.hpp:85
@ ConnectFailed
Scan never produced the requested id.
Definition ble_midi.hpp:91
@ PermissionDenied
Definition ble_midi.hpp:87
@ PeripheralNotFound
Platform has no BLE MIDI backend wired.
Definition ble_midi.hpp:90
@ ServiceNotFound
CB / GATT connect attempt failed.
Definition ble_midi.hpp:92
@ None
Definition ble_midi.hpp:86
@ BluetoothOff
User declined Bluetooth permission.
Definition ble_midi.hpp:88
@ Unsupported
Adapter is off / unavailable.
Definition ble_midi.hpp:89
@ Timeout
Peripheral does not expose BLE-MIDI service.
Definition ble_midi.hpp:93
std::function< void(const std::string &peripheral_id, BleMidiConnectionState state, BleMidiError error)> BleMidiStateCallback
Callback for connection-state changes against a specific peripheral id.
Definition ble_midi.hpp:103
std::function< void(const BleMidiPeripheral &)> BleMidiScanCallback
Callback fired each time the scanner observes a peripheral that advertises the BLE-MIDI service UUID.
Definition ble_midi.hpp:99
BleMidiConnectionState
Connection state for an attempted BLE pairing.
Definition ble_midi.hpp:72
@ Connected
Definition ble_midi.hpp:76
@ Scanning
Definition ble_midi.hpp:74
@ Failed
Definition ble_midi.hpp:78
@ Connecting
Definition ble_midi.hpp:75
@ Idle
Definition ble_midi.hpp:73
@ Disconnected
Definition ble_midi.hpp:77
std::vector< uint8_t > encode_ble_midi_packet(const uint8_t *msg, std::size_t size, uint32_t timestamp_ms)
Encode a single MIDI message into a BLE-MIDI packet using timestamp timestamp_ms (truncated to 13 bit...
std::unique_ptr< BleMidiCentral > create_ble_midi_central()
Create the BLE MIDI central for the current platform.
SequencerStateChannel is the audio-safe transport for a plugin's structured NON-parameter state: the ...
Definition audio_device_manager.hpp:48
One BLE peripheral that advertises the BLE-MIDI service.
Definition ble_midi.hpp:55
std::string name
Definition ble_midi.hpp:57
bool is_paired
True if the OS already has a bond/pairing for this peripheral.
Definition ble_midi.hpp:67
std::chrono::steady_clock::time_point last_seen
Monotonic time the peripheral was last seen in a scan response.
Definition ble_midi.hpp:63
std::string id
Definition ble_midi.hpp:56
int rssi
Last-seen RSSI in dBm. 0 if the backend doesn't report it.
Definition ble_midi.hpp:59
The well-known service / characteristic UUIDs (16-byte big-endian).
Definition ble_midi.hpp:169
static constexpr const char * kCharacteristic
Definition ble_midi.hpp:172
static constexpr const char * kService
Definition ble_midi.hpp:170