Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
pulp::midi Namespace Reference

Namespaces

namespace  arpeggiator_detail
namespace  panic_detail
namespace  detail
namespace  routing_detail
namespace  step_player_detail
namespace  utility_detail

Classes

struct  ArpeggiatorGate
struct  ArpeggiatorSpec
struct  ArpeggiatorBlock
 Block timing for one sample-accurate arpeggiator call. More...
struct  ArpeggiatorCapacityContract
class  Arpeggiator
 Fixed-capacity, sample-accurate MIDI arpeggiator. More...
struct  BleMidiPeripheral
 One BLE peripheral that advertises the BLE-MIDI service. More...
class  BleMidiCentral
 Cross-platform BLE MIDI central. More...
struct  BleMidiUuids
 The well-known service / characteristic UUIDs (16-byte big-endian). More...
class  BleMidiPacketDecoder
 Stateful decoder. More...
class  BleMidiPortRegistry
 Singleton bridge between BLE-MIDI centrals and the platform MidiSystem. More...
class  MidiBuffer
 Collection of timestamped MIDI events within a single audio buffer period. More...
struct  ChordMemorySpec
class  ChordMemory
 Bounded chord memory: capture a chord once, then play it from single notes. More...
struct  ControllerMapRule
class  ControllerMapper
struct  ScaleAwareMpeSpec
class  ScaleAwareMpePitch
struct  MidiPortInfo
class  MidiInput
class  MidiOutput
class  MidiSystem
struct  HumanizeSpec
 Seeded timing and velocity jitter over note attacks. More...
class  Humanize
 MaximumPendingAttacks bounds how many attacks can sit in the jitter window at once. More...
class  MidiKeyboardState
 Tracks the state of all 128 MIDI keys across 16 channels. More...
struct  LatchSpec
class  Latch
 Bounded note-retention kernel over a MIDI event stream. More...
struct  MidiEvent
 Timestamped MIDI event combining a choc::midi::ShortMessage with timing info. More...
struct  TimestampedShortMessage
 Timestamped MIDI event in the collector queue. More...
struct  MidiMessageCollectorTelemetry
class  MidiMessageCollector
struct  MUID
 MIDI-CI MUID (Message Unique Identifier) — 28-bit random ID. More...
struct  CiDeviceInfo
 CI device identity (from discovery). More...
struct  ProfileId
 CI Profile identifier (5 bytes). More...
struct  ProfileState
 CI Profile state. More...
struct  FunctionBlockId
 UMP Function Block identifier (MIDI 2.0 §A) — a Function Block is a logical grouping of UMP groups that share a single set of CI / Profile state. More...
struct  FunctionBlockInfo
 Description of a single Function Block as advertised by an endpoint. More...
struct  ProfileDetails
 Profile Details Inquiry/Reply payload (CI 1.2 §7.6) — per-profile metadata the responder publishes when asked for "more about profile X". More...
class  CiDiscovery
 MIDI-CI Discovery manager — handles CI message exchange. More...
struct  PeChunk
 A single PE chunk, after Mcoded7 decode of header + payload. More...
class  PeReassembler
 Reassemble a stream of incoming PeChunks for the same request_id. More...
struct  PeSubscription
 One active subscription state — owned by PeSubscriptionManager. More...
class  PeSubscriptionManager
 Authoritative subscription registry on the responder side. More...
class  MidiControlBus
struct  TimedMidiEvent
struct  MidiTrack
struct  MidiFileData
struct  TimestampedMidiEvent
 A MIDI event with timestamp (in seconds or samples). More...
class  MidiMessageSequence
 Ordered sequence of MIDI events with editing operations. More...
class  MockMidiSystem
class  MonotonicMidiClock
 Monotonic timestamp source for MIDI input events, measured in seconds from a base captured when the port opened. More...
struct  MpeExpressionEvent
 A single per-note expression event. More...
class  MpeBuffer
 A time-ordered list of per-note expression events for one block. More...
struct  MpeMessageList
 Fixed-capacity, allocation-free list of events returned by the builders. More...
class  MpeSynthVoice
 Abstract base for a single MPE voice. More...
struct  MpeVoiceAllocatorTelemetry
struct  MpeVoiceAllocatorRuntimeBudgetReport
class  MpeGlideDetector
 Glide/legato detector — observes MPE note-on events and reports when a new note on the same channel overlaps an existing held note. More...
class  MpeVoiceAllocator
 Routes MPE expression events to a pool of voices of type Voice (which must derive from MpeSynthVoice). More...
struct  MpeNoteState
 Per-note state maintained by MpeVoiceTracker. More...
class  MpeVoiceTracker
 Tracks MPE voice state across channels. More...
struct  NoteDelaySpec
 MIDI note delay / echo. More...
class  NoteDelay
 MaximumHeldSources bounds how many authored notes can have armed echoes at once. More...
struct  NoteRepeatSpec
 Clock-divided note repeat / retrigger. More...
class  NoteRepeat
struct  NoteLengthSpec
class  NoteLengthShaper
struct  MonophonicSpec
struct  MonophonicPitchState
class  BasicMonophonicNoteSelector
struct  PanicScope
 Which channels and which message kinds a panic covers. More...
struct  PortListDiff
 Ports that appeared and ports that went away between two snapshots. More...
class  PortListTracker
 Remembers the last snapshot and reports what changed since. More...
class  PortChangePoller
 Calls on_change from a background thread whenever the combined input and output list changes. More...
struct  RawMidiParserState
struct  ChannelRouteSpec
class  ChannelRouter
struct  NoteRangeSpec
class  NoteRangeFilter
struct  KeyboardSplitSpec
class  KeyboardSplit
class  RpnParser
 Parses RPN (Registered Parameter Number) and NRPN (Non-Registered Parameter Number) messages from a stream of MIDI CC events. More...
class  RunningStatusParser
struct  StepPlayerStep
 The authored data at one lane and step coordinate. More...
struct  StepPlayerLaneSpec
struct  StepPlayerBlock
 Block timing for one sample-accurate step-player call. More...
struct  StepPlayerCapacityContract
struct  StepPlayerCoordinate
 A lane's authored coordinate at one grid tick. More...
class  StepPlayer
 Fixed-capacity, sample-accurate multi-lane linear step player. More...
struct  StrumSpec
 Spread a near-simultaneous cluster of notes across time. More...
class  Strum
struct  SynthesiserNote
 Per-note metadata maintained by the Synthesiser when a voice is activated. More...
struct  SynthesiserTelemetry
struct  SynthesiserRuntimeBudgetReport
class  SynthesiserVoice
 Abstract base for one synth voice. More...
struct  SynthesiserNoteOnPolicy
 Constant note-on metadata for MIDI-buffer dispatch. More...
class  Synthesiser
 Polyphonic synth that routes plain-MIDI events into a pool of Voice instances. More...
class  SysexAccumulator
class  SysexChunkAssembler
struct  TuningQueryResult
struct  TuningNoteResult
struct  TuningStatus
class  TuningProvider
class  EqualTemperamentTuningProvider
struct  UmpPacket
struct  MpeZone
struct  MpeConfig
struct  UmpEvent
 One UMP packet with a block-relative sample offset. More...
class  UmpBuffer
 A time-ordered list of UMP events for one audio block. More...
struct  Midi2ProgramChange
 Decoded fields of a MIDI 2.0 Channel Voice Program Change (status 0xC). More...
struct  UmpEndpointDirection
 Direction the endpoint exposes. More...
struct  UmpEndpointInfo
 Static description of an endpoint discovered on the system or registered virtually. More...
class  UmpEndpoint
 Polymorphic endpoint handle. More...
struct  UmpSessionConfig
 Options for creating a session. More...
class  UmpSession
class  UmpSysex7Reassembler
 Reassembler state machine for UMP Type-0x3 sysex7 packet streams. More...
struct  VirtualUmpEndpointConfig
 Configuration for a newly-registered virtual endpoint. More...
class  VirtualUmpEndpoint
struct  MidiUtilityContract
struct  MidiUtilityProcessReport

Typedefs

using BleMidiScanCallback = std::function<void(const BleMidiPeripheral&)>
 Callback fired each time the scanner observes a peripheral that advertises the BLE-MIDI service UUID.
using BleMidiStateCallback
 Callback for connection-state changes against a specific peripheral id.
using ControllerCurve = float (*)(float normalized, float amount) noexcept
using MidiInputCallback = std::function<void(const MidiEvent& event)>
using MidiSysexCallback
using PeNotifyCallback
 Notify callback — fires when a Subscribe peer should be notified of a resource change.
using MpeNoteGeneration = std::uint64_t
 Stable identity assigned to one accepted MPE note-on generation.
using MonophonicNoteSelector = BasicMonophonicNoteSelector<>
using RawMidiShortCallback
using RawMidiSysexCallback
using ShortMessageSink = std::function<void(const MidiEvent&)>
 Callback invoked for each complete short message.
using SysexSink = std::function<void(const uint8_t* data, std::size_t size)>
using SysexEmitFn
 Callback shape: receives the complete sysex payload (including the leading 0xF0 and trailing 0xF7) plus a flag indicating whether the message was properly terminated (true) or aborted by a new status byte (false).
using UmpReceiveCallback
 Receive callback.
using Sysex7EmitFn
 Callback shape: receives the complete UMP SysEx7 payload bytes, with no synthesised MIDI 1.0 F0/F7 framing.

Enumerations

enum class  ArpeggiatorOrder : std::uint8_t {
  Up , Down , UpDown , AsPlayed ,
  Random , Chord
}
enum class  ArpeggiatorRepeatedNotePolicy : std::uint8_t { Retrigger , Tie }
enum class  ArpeggiatorBoundaryOrder : std::uint8_t { InputBeforeStep , StepBeforeInput }
enum class  ArpeggiatorTransportEvent : std::uint8_t {
  Continuous , Started , Stopped , Seeked ,
  LoopWrapped
}
enum class  BleMidiConnectionState {
  Idle , Scanning , Connecting , Connected ,
  Disconnected , Failed
}
 Connection state for an attempted BLE pairing. More...
enum class  BleMidiError {
  None , PermissionDenied , BluetoothOff , Unsupported ,
  PeripheralNotFound , ConnectFailed , ServiceNotFound , Timeout
}
 Backend-reported error code. More...
enum class  ChordMemoryMode : std::uint8_t { Single , PerKey , ScaleDegree }
 How a captured chord is addressed when a single note triggers it. More...
enum class  LatchMode : std::uint8_t { Off , Hold , Toggle }
 How a latch retains the notes a player releases. More...
enum class  CiMessageType : uint8_t {
  DiscoveryInquiry = 0x70 , DiscoveryReply = 0x71 , InvalidateMUID = 0x7E , ProfileSetOn = 0x22 ,
  ProfileSetOff = 0x23 , ProfileInquiry = 0x24 , ProfileReply = 0x25 , ProfileEnable = 0x26 ,
  ProfileDisable = 0x27 , ProfileDetailsInquiry = 0x28 , ProfileDetailsReply = 0x29 , ProfileSpecificData = 0x2F ,
  PropertyGetInquiry = 0x34 , PropertyGetReply = 0x35 , PropertySetInquiry = 0x36 , PropertySetReply = 0x37 ,
  PropertySubscribeInquiry = 0x38 , PropertySubscribeReply = 0x39 , PropertyNotify = 0x3F , ProcessInquiry = 0x40 ,
  ProcessReply = 0x41
}
 MIDI-CI message types (sub-IDs). More...
enum class  PeMessageType : uint8_t {
  GetInquiry = 0x34 , GetReply = 0x35 , SetInquiry = 0x36 , SetReply = 0x37 ,
  SubscribeInquiry = 0x38 , SubscribeReply = 0x39 , Notify = 0x3F
}
 PE sub-IDs (MIDI-CI sub-ID #2 byte). Mirrors CiMessageType for PE. More...
enum class  MpeVoiceStealMode { Oldest , LowestVelocity , LowestPitch , HighestPitch }
 Voice-steal policy when the pool is full. More...
enum class  NoteDelaySync : std::uint8_t { Division , Milliseconds }
enum class  MonophonicPriority : std::uint8_t { Low , High , Last }
enum class  PanicMessageKind : std::uint8_t {
  all_sound_off , all_notes_off , reset_all_controllers , sustain_off ,
  pitch_bend_centre
}
 One kind of message a panic can contain. More...
enum class  StepPlayerDirection : std::uint8_t { Forward , Reverse , PingPong , Random }
enum class  StepPlayerTransportEvent : std::uint8_t {
  Continuous , Started , Stopped , Seeked ,
  LoopWrapped
}
enum class  StepPlayerError : std::uint8_t {
  None , NotConfigured , InvalidLane , InvalidStepIndex ,
  InvalidVelocity , InvalidGate , InvalidProbability , InvalidRatchetCount ,
  InvalidPitchOffset , InvalidTimingOffset
}
enum class  StrumDirection : std::uint8_t {
  Up , Down , Alternate , AsPlayed ,
  Random
}
enum class  StrumShape : std::uint8_t { Linear , Accelerate , Decelerate }
 How the spacing is distributed across the spread. More...
enum class  StrumSpacingSync : std::uint8_t { Division , Milliseconds }
enum class  VoiceStealStrategy : uint8_t {
  Oldest , Quietest , Lowest , Highest ,
  Priority
}
 Voice-stealing strategy applied when note_on fires and no free voice is available. More...
enum class  UmpMessageType : uint8_t {
  Utility = 0x0 , System = 0x1 , Midi1ChannelVoice = 0x2 , DataSysEx = 0x3 ,
  Midi2ChannelVoice = 0x4 , Data128 = 0x5
}
enum class  Midi2Status : uint8_t {
  RegisteredPerNoteCC = 0x00 , AssignablePerNoteCC = 0x10 , RegisteredCC = 0x20 , AssignableCC = 0x30 ,
  RelativeRegisteredCC = 0x40 , RelativeAssignableCC = 0x50 , PerNotePitchBend = 0x60 , NoteOff = 0x80 ,
  NoteOn = 0x90 , PolyPressure = 0xA0 , ControlChange = 0xB0 , ProgramChange = 0xC0 ,
  ChannelPressure = 0xD0 , PitchBend = 0xE0 , PerNoteManagement = 0xF0
}
enum class  UtilityStatus : uint8_t {
  Noop = 0x0 , JrClock = 0x1 , JrTimestamp = 0x2 , DeltaClockstampTicksPerQuarter = 0x3 ,
  DeltaClockstamp = 0x4
}
 Utility message status codes (bits 20-23 of word 0 for type 0x0). More...
enum class  UmpOpenStatus { Ok , NotFound , OsBackendUnavailable , OsError }
 Outcome of creating an endpoint connection through the session. More...
enum class  MidiUtilityOverflowPolicy : std::uint8_t { DropUnstarted , FailOpenBalanced , RetainReleaseDebt }
enum class  MidiUtilitySameSampleOrder : std::uint8_t { InputStable , ReleaseBeforeAttack }
enum class  MidiUtilityTransportRequirement : std::uint8_t { None , MonotonicSamples , FlushOnDiscontinuity }

Functions

std::unique_ptr< BleMidiCentral > create_ble_midi_central ()
 Create the BLE MIDI central for the current platform.
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 bits per the spec).
void clear_midi_block (MidiBuffer &block) noexcept
bool midi_block_has_drops (const MidiBuffer &block) noexcept
bool copy_midi_sidecars (const MidiBuffer &src, MidiBuffer &dst) noexcept
bool copy_midi_block (const MidiBuffer &src, MidiBuffer &dst) noexcept
bool copy_mpe_block (const MpeBuffer &src, MpeBuffer &dst) noexcept
bool mpe_buffer_has_drops (const MpeBuffer &block) noexcept
float linear_controller_curve (float normalized, float) noexcept
std::unique_ptr< MidiSystem > create_midi_system ()
std::vector< uint8_t > mcoded7_encode (const uint8_t *data, std::size_t size)
 Encode raw 8-bit bytes into Mcoded7.
std::vector< uint8_t > mcoded7_encode (const std::vector< uint8_t > &data)
std::vector< uint8_t > mcoded7_decode (const uint8_t *data, std::size_t size)
 Decode an Mcoded7-encoded stream back to raw 8-bit bytes.
std::vector< uint8_t > mcoded7_decode (const std::vector< uint8_t > &data)
std::vector< uint8_t > pe_build_message (PeMessageType pe_type, uint8_t ci_version, MUID source, MUID destination, uint8_t request_id, std::string_view header_json, uint16_t total_chunks, uint16_t chunk_number, const uint8_t *payload, std::size_t payload_size)
 Builder for PE message wire bytes.
std::vector< uint8_t > pe_build_message (PeMessageType pe_type, uint8_t ci_version, MUID source, MUID destination, uint8_t request_id, std::string_view header_json, uint16_t total_chunks, uint16_t chunk_number, const std::vector< uint8_t > &payload)
std::optional< PeChunk > pe_parse_message (const uint8_t *data, std::size_t size)
 Parse a single PE SysEx envelope into a PeChunk.
std::vector< std::vector< uint8_t > > pe_split_into_chunks (PeMessageType pe_type, uint8_t ci_version, MUID source, MUID destination, uint8_t request_id, std::string_view header_json, const uint8_t *payload, std::size_t payload_size, std::size_t max_payload_bytes)
 Split a logical PE Get/Set into wire-ready chunks bounded by max_payload_bytes of decoded resource per chunk.
std::optional< std::vector< uint8_t > > pe_compress (const uint8_t *data, std::size_t size)
 Compress a PE payload using zlib (RFC 1950). Returns nullopt on failure.
std::optional< std::vector< uint8_t > > pe_compress (const std::vector< uint8_t > &payload)
std::optional< std::vector< uint8_t > > pe_decompress (const uint8_t *data, std::size_t size)
 Decompress a zlib-wrapped PE payload. Returns nullopt on failure.
std::optional< std::vector< uint8_t > > pe_decompress (const std::vector< uint8_t > &blob)
std::string pe_header_make (std::string_view resource, std::string_view command={}, int status=200)
 Builds a minimal PE JSON header.
bool pe_header_parse (std::string_view json, std::string *resource, std::string *command, int *status)
 Parse a PE JSON header.
std::optional< MidiFileData > read_midi_file (const std::string &path)
bool write_midi_file (const std::string &path, const MidiFileData &data)
void bind_tracker_to_buffer (MpeVoiceTracker &tracker, MpeBuffer &out, int32_t &current_sample_offset, bool *expression_event_dropped=nullptr)
 Convenience: install tracker callbacks that forward events to out with the given sample offset.
MpeMessageList< 3 > mpe_configuration_message (uint8_t manager_channel, uint8_t member_count)
 MPE Configuration Message: RPN 6 on a manager channel.
MpeMessageList< 4 > mpe_pitch_bend_range_message (uint8_t channel, float semitones)
 Pitch-bend range (RPN 0) for one channel: whole semitones in Data Entry MSB and the remaining hundredths in Data Entry LSB.
MpeMessageList< 6 > mpe_zone_layout_messages (const MpeConfig &config)
 Zone layout: one Configuration Message for each zone, lower first.
MpeMessageList< 64 > mpe_zone_pitch_bend_range_messages (const MpeZone &zone, float manager_semitones, float member_semitones)
 Pitch-bend ranges for a whole zone: the manager channel gets manager_semitones and every member channel gets member_semitones.
template<std::size_t N>
void send_mpe_messages (MidiOutput &output, const MpeMessageList< N > &list)
 Send every event in list to output, in order.
constexpr std::size_t panic_message_count (const PanicScope &scope)
 Number of events panic_messages() will write for scope.
std::size_t panic_messages (const PanicScope &scope, std::span< MidiEvent > out, std::int32_t sample_offset=0) noexcept
 Write the panic messages for scope into out, channel by channel.
template<typename Fn>
void for_each_held_note (const MidiKeyboardState &keys, std::uint16_t channel_mask, Fn &&fn) noexcept(noexcept(fn(std::uint8_t{}, std::uint8_t{})))
 Call fn(channel, note) for every key keys reports as held on the channels in channel_mask, in channel then note order.
std::size_t held_note_offs (const MidiKeyboardState &keys, std::span< MidiEvent > out, std::uint16_t channel_mask=0xFFFFu, std::int32_t sample_offset=0) noexcept
 Note-off events for exactly the notes keys reports as held, on the channels in channel_mask, in channel then note order.
std::size_t all_note_offs (std::span< MidiEvent > out, std::uint16_t channel_mask=0xFFFFu, std::int32_t sample_offset=0) noexcept
 Brute-force fallback: a note-off for every key on the channels in channel_mask, whether or not it was tracked as held.
std::optional< PanicMessageKind > classify_panic_message (const MidiEvent &event) noexcept
 Classify a message as one of the panic kinds, or nullopt for anything else.
PortListDiff diff_port_lists (const std::vector< MidiPortInfo > &before, const std::vector< MidiPortInfo > &after)
 Compare two snapshots.
void parse_raw_midi_bytes (const uint8_t *buf, std::size_t n, RawMidiParserState &state, const RawMidiShortCallback &on_short, const RawMidiSysexCallback &on_sysex)
bool is_valid_midi_note (int midi_note) noexcept
bool is_valid_midi_channel (int midi_channel) noexcept
int normalise_midi_channel (int midi_channel) noexcept
double equal_temperament_frequency (int midi_note, double a4_hz=440.0) noexcept
int equal_temperament_note_for_frequency (double frequency_hz, double a4_hz=440.0) noexcept
int ump_words_for_message_type (uint8_t message_type)
 Spec-complete UMP packet length, in 32-bit words, for a raw message-type nibble (bits 28-31 of word 0, values 0x0-0xF).
template<typename Visitor>
void walk_ump_packet (const uint32_t *words, uint32_t word_count, Visitor &&visit)
 Walk one UMP packet's contiguous word array, visiting each complete message in order.
UtilityStatus utility_status (const UmpPacket &p)
 Extract the utility status nibble from a type-0 UMP packet (bits 20-23 of word 0).
uint16_t jr_value_16 (const UmpPacket &p)
 Extract the 16-bit JR Clock / JR Timestamp value from a type-0 UMP packet (bits 0-15 of word 0).
double jr_value_seconds (uint16_t value)
double jr_value_microseconds (uint16_t value)
UmpPacket make_jr_clock (uint8_t group, uint16_t value)
 Build a JR Clock utility packet (status 0x1).
UmpPacket make_jr_timestamp (uint8_t group, uint16_t value)
 Build a JR Timestamp utility packet (status 0x2).
uint16_t scale_7_to_16 (uint8_t v7)
 Scale a 7-bit value (0..127) to 16-bit (0..65535) per MIDI 2.0 spec.
uint8_t scale_16_to_7 (uint16_t v16)
 Scale 16-bit (0..65535) down to 7-bit (0..127).
uint32_t scale_14_to_32 (uint16_t v14)
 Scale a 14-bit value (0..16383, center 8192) to 32-bit (center 0x80000000) using MIDI 2.0 Min/Center/Max scaling so the midpoint is preserved exactly.
uint16_t scale_32_to_14 (uint32_t v32)
 Scale a 32-bit value back to 14-bit (0..16383), inverse of scale_14_to_32.
UmpPacket midi1_event_to_ump2 (const MidiEvent &ev, uint8_t group=0)
 Convert a single MIDI 1.0 event to a MIDI 2.0 Channel Voice UMP packet.
void midi1_to_ump (const MidiBuffer &src, UmpBuffer &dst, uint8_t group=0)
 Append a MIDI 1.0 buffer to a UmpBuffer as MIDI 2.0 UMP packets.
Midi2ProgramChange ump_program_change_fields (const UmpPacket &p)
 Decode a MIDI 2.0 Program Change packet's fields without converting it.
bool ump_to_midi1_event (const UmpPacket &p, MidiEvent &out)
 Convert a UMP Channel Voice packet into a MIDI 1.0 event when possible.
bool ump_is_bank_valid_program_change (const UmpPacket &p)
 True when p is a MIDI 2.0 Program Change carrying a valid bank, i.e.
void ump_to_midi1 (const UmpBuffer &src, MidiBuffer &dst)
 Flatten a UmpBuffer into a MidiBuffer.
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.

Variables

constexpr bool kHasMtsEspTuning = static_cast<bool>( 0 )
template<typename Serial>
constexpr bool monophonic_serial_capacity_supported
constexpr std::size_t kMaxPanicMessages = 16u * 5u
 Largest number of events panic_messages() can produce (16 channels x 5).
constexpr std::size_t kMaxNoteOffs = 16u * 128u
 Largest number of events held_note_offs() / all_note_offs() can produce.
constexpr std::size_t kRawMidiSysexLimit = 64 * 1024
constexpr bool kHasScalaTuning = static_cast<bool>( 0 )
constexpr int kUnknownMidiChannel = -1
constexpr double kJrTicksPerSecond = 31250.0
 One JR Clock tick is 1/31250 of a second (~32 microseconds).
constexpr uint8_t kBankSelectMsbCc = 0
 MIDI 1.0 controller numbers carrying Bank Select for a program change.
constexpr uint8_t kBankSelectLsbCc = 32

Typedef Documentation

◆ BleMidiScanCallback

using pulp::midi::BleMidiScanCallback = std::function<void(const BleMidiPeripheral&)>

Callback fired each time the scanner observes a peripheral that advertises the BLE-MIDI service UUID.

May be invoked from a backend thread; consumers post to the UI thread themselves.

◆ BleMidiStateCallback

Initial value:
std::function<void(const std::string& peripheral_id,
BleMidiError error)>
BleMidiError
Backend-reported error code.
Definition ble_midi.hpp:85
BleMidiConnectionState
Connection state for an attempted BLE pairing.
Definition ble_midi.hpp:72
SequencerStateChannel is the audio-safe transport for a plugin's structured NON-parameter state: the ...
Definition audio_device_manager.hpp:48

Callback for connection-state changes against a specific peripheral id.

Drives the pairing UI (Connecting → Connected | Failed).

◆ ControllerCurve

using pulp::midi::ControllerCurve = float (*)(float normalized, float amount) noexcept

◆ MidiInputCallback

using pulp::midi::MidiInputCallback = std::function<void(const MidiEvent& event)>

◆ MidiSysexCallback

Initial value:
std::function<void(const std::vector<uint8_t>& bytes,
double timestamp_sec)>

◆ MonophonicNoteSelector

◆ MpeNoteGeneration

using pulp::midi::MpeNoteGeneration = std::uint64_t

Stable identity assigned to one accepted MPE note-on generation.

Zero is permanently reserved as "no generation".

◆ PeNotifyCallback

Initial value:
std::function<void(MUID subscriber_muid,
std::string_view resource,
std::string_view header_json,
const std::vector<uint8_t>& payload)>
MIDI-CI MUID (Message Unique Identifier) — 28-bit random ID.
Definition midi_ci.hpp:52

Notify callback — fires when a Subscribe peer should be notified of a resource change.

subscriber_muid is the bound peer; header_json and payload echo the Notify PE message body. The transport layer (the caller of CiDiscovery) is responsible for actually building + sending the Notify wire bytes — this callback just tells you who.

◆ RawMidiShortCallback

Initial value:
std::function<void(uint8_t status, uint8_t d1, uint8_t d2)>

◆ RawMidiSysexCallback

Initial value:
std::function<void(const std::vector<uint8_t>& buf)>

◆ ShortMessageSink

using pulp::midi::ShortMessageSink = std::function<void(const MidiEvent&)>

Callback invoked for each complete short message.

SysEx payloads are delivered via a separate callback so callers can grow their own buffer.

◆ Sysex7EmitFn

Initial value:
void(*)(const std::vector<std::uint8_t>& payload,
void* user)

Callback shape: receives the complete UMP SysEx7 payload bytes, with no synthesised MIDI 1.0 F0/F7 framing.

UMP framing is implicit in the start/continue/end status nibble; callers crossing into MIDI 1.0 must add F0/F7 explicitly at that boundary.

The reassembler invokes this callback exactly once per logical sysex (either a single complete packet, or a start → continue* → end span), including a zero-byte payload. Orphaned continue/end packets (i.e. arrival without a preceding start) are dropped silently — corrupting the downstream sink with a partial payload is worse than dropping one malformed sysex.

◆ SysexEmitFn

Initial value:
std::function<void(const std::vector<std::uint8_t>& payload, bool complete)>

Callback shape: receives the complete sysex payload (including the leading 0xF0 and trailing 0xF7) plus a flag indicating whether the message was properly terminated (true) or aborted by a new status byte (false).

Callers typically ignore aborted messages or log them at debug level.

◆ SysexSink

using pulp::midi::SysexSink = std::function<void(const uint8_t* data, std::size_t size)>

◆ UmpReceiveCallback

Initial value:
std::function<void(const UmpPacket& packet,
double timestamp_sec)>
Definition ump.hpp:84

Receive callback.

Fired on an OS thread; must not block. The packet is delivered as already-parsed UmpPacket so the caller doesn't have to repeat the per-type word-count switch.

Enumeration Type Documentation

◆ ArpeggiatorBoundaryOrder

enum class pulp::midi::ArpeggiatorBoundaryOrder : std::uint8_t
strong
Enumerator
InputBeforeStep 
StepBeforeInput 

◆ ArpeggiatorOrder

enum class pulp::midi::ArpeggiatorOrder : std::uint8_t
strong
Enumerator
Up 
Down 
UpDown 
AsPlayed 
Random 
Chord 

◆ ArpeggiatorRepeatedNotePolicy

enum class pulp::midi::ArpeggiatorRepeatedNotePolicy : std::uint8_t
strong
Enumerator
Retrigger 
Tie 

◆ ArpeggiatorTransportEvent

enum class pulp::midi::ArpeggiatorTransportEvent : std::uint8_t
strong
Enumerator
Continuous 
Started 
Stopped 
Seeked 
LoopWrapped 

◆ BleMidiConnectionState

Connection state for an attempted BLE pairing.

Reported on the callback registered with BleMidiCentral::set_state_callback().

Enumerator
Idle 
Scanning 
Connecting 
Connected 
Disconnected 
Failed 

◆ BleMidiError

enum class pulp::midi::BleMidiError
strong

Backend-reported error code.

Backends translate platform errors (CBError, BlueZ D-Bus errors, WinRT HRESULT, etc.) into this neutral enum so cross-platform tests can assert without including per-OS headers.

Enumerator
None 
PermissionDenied 
BluetoothOff 

User declined Bluetooth permission.

Unsupported 

Adapter is off / unavailable.

PeripheralNotFound 

Platform has no BLE MIDI backend wired.

ConnectFailed 

Scan never produced the requested id.

ServiceNotFound 

CB / GATT connect attempt failed.

Timeout 

Peripheral does not expose BLE-MIDI service.

◆ ChordMemoryMode

enum class pulp::midi::ChordMemoryMode : std::uint8_t
strong

How a captured chord is addressed when a single note triggers it.

Enumerator
Single 

One captured chord answers every trigger, transposed to the trigger.

PerKey 

Each trigger pitch class owns its own captured chord.

ScaleDegree 

The captured shape is stored as scale degrees, so a trigger anywhere in the scale produces the diatonic chord rather than a parallel transposition.

◆ CiMessageType

enum class pulp::midi::CiMessageType : uint8_t
strong

MIDI-CI message types (sub-IDs).

Note: Pulp uses 0x24 / 0x25 / 0x26 / 0x27 for the Profile Inquiry family (existing contract; preserved for wire compatibility). The CI 1.2 Profile Details / Profile Specific Data / Set Profile On|Off values follow that block at 0x28 / 0x29 / 0x2F / 0x22 / 0x23. New values are appended only — old code paths keep their numeric IDs.

Enumerator
DiscoveryInquiry 
DiscoveryReply 
InvalidateMUID 
ProfileSetOn 

Set Profile On (CI 1.2 §7.4).

ProfileSetOff 

Set Profile Off (CI 1.2 §7.5).

ProfileInquiry 
ProfileReply 
ProfileEnable 

Pulp legacy Profile Enabled report.

ProfileDisable 

Pulp legacy Profile Disabled report.

ProfileDetailsInquiry 

Profile Details Inquiry (CI 1.2 §7.6).

ProfileDetailsReply 

Profile Details Reply (CI 1.2 §7.6).

ProfileSpecificData 

Profile-Specific Data (CI 1.2 §7.7).

PropertyGetInquiry 
PropertyGetReply 
PropertySetInquiry 
PropertySetReply 
PropertySubscribeInquiry 
PropertySubscribeReply 
PropertyNotify 
ProcessInquiry 
ProcessReply 

◆ LatchMode

enum class pulp::midi::LatchMode : std::uint8_t
strong

How a latch retains the notes a player releases.

Off is exact passthrough and owns no note. Hold retains every note of a phrase until the player lifts every key and starts the next phrase, which is the behaviour an arpeggiator hold pedal provides. Toggle makes each press flip one key's retention, so a second press on a latched key releases it.

Enumerator
Off 
Hold 
Toggle 

◆ Midi2Status

enum class pulp::midi::Midi2Status : uint8_t
strong
Enumerator
RegisteredPerNoteCC 
AssignablePerNoteCC 
RegisteredCC 
AssignableCC 
RelativeRegisteredCC 
RelativeAssignableCC 
PerNotePitchBend 
NoteOff 
NoteOn 
PolyPressure 
ControlChange 
ProgramChange 
ChannelPressure 
PitchBend 
PerNoteManagement 

◆ MidiUtilityOverflowPolicy

enum class pulp::midi::MidiUtilityOverflowPolicy : std::uint8_t
strong
Enumerator
DropUnstarted 
FailOpenBalanced 
RetainReleaseDebt 

◆ MidiUtilitySameSampleOrder

enum class pulp::midi::MidiUtilitySameSampleOrder : std::uint8_t
strong
Enumerator
InputStable 
ReleaseBeforeAttack 

◆ MidiUtilityTransportRequirement

enum class pulp::midi::MidiUtilityTransportRequirement : std::uint8_t
strong
Enumerator
None 
MonotonicSamples 
FlushOnDiscontinuity 

◆ MonophonicPriority

enum class pulp::midi::MonophonicPriority : std::uint8_t
strong
Enumerator
Low 
High 
Last 

◆ MpeVoiceStealMode

enum class pulp::midi::MpeVoiceStealMode
strong

Voice-steal policy when the pool is full.

Enumerator
Oldest 

Steal the voice that started first.

LowestVelocity 

Steal the quietest voice.

LowestPitch 

Steal the lowest-note voice.

HighestPitch 

Steal the highest-note voice.

◆ NoteDelaySync

enum class pulp::midi::NoteDelaySync : std::uint8_t
strong
Enumerator
Division 
Milliseconds 

◆ PanicMessageKind

enum class pulp::midi::PanicMessageKind : std::uint8_t
strong

One kind of message a panic can contain.

Enumerator
all_sound_off 

CC 120 = 0, immediate stop.

all_notes_off 

CC 123 = 0, release.

reset_all_controllers 

CC 121 = 0.

sustain_off 

CC 64 = 0.

pitch_bend_centre 

pitch bend 8192

◆ PeMessageType

enum class pulp::midi::PeMessageType : uint8_t
strong

PE sub-IDs (MIDI-CI sub-ID #2 byte). Mirrors CiMessageType for PE.

Enumerator
GetInquiry 
GetReply 
SetInquiry 
SetReply 
SubscribeInquiry 
SubscribeReply 
Notify 

◆ StepPlayerDirection

enum class pulp::midi::StepPlayerDirection : std::uint8_t
strong
Enumerator
Forward 
Reverse 
PingPong 
Random 

◆ StepPlayerError

enum class pulp::midi::StepPlayerError : std::uint8_t
strong
Enumerator
None 
NotConfigured 
InvalidLane 
InvalidStepIndex 
InvalidVelocity 
InvalidGate 
InvalidProbability 
InvalidRatchetCount 
InvalidPitchOffset 
InvalidTimingOffset 

◆ StepPlayerTransportEvent

enum class pulp::midi::StepPlayerTransportEvent : std::uint8_t
strong
Enumerator
Continuous 
Started 
Stopped 
Seeked 
LoopWrapped 

◆ StrumDirection

enum class pulp::midi::StrumDirection : std::uint8_t
strong
Enumerator
Up 
Down 
Alternate 

Flip between up and down on each successive cluster.

AsPlayed 

Keep the order the notes arrived in.

Random 

Seeded shuffle, stable for a given cluster coordinate.

◆ StrumShape

enum class pulp::midi::StrumShape : std::uint8_t
strong

How the spacing is distributed across the spread.

Enumerator
Linear 
Accelerate 

Notes bunch at the start and open out.

Decelerate 

Notes open wide and close up.

◆ StrumSpacingSync

enum class pulp::midi::StrumSpacingSync : std::uint8_t
strong
Enumerator
Division 
Milliseconds 

◆ UmpMessageType

enum class pulp::midi::UmpMessageType : uint8_t
strong
Enumerator
Utility 
System 
Midi1ChannelVoice 
DataSysEx 
Midi2ChannelVoice 
Data128 

◆ UmpOpenStatus

enum class pulp::midi::UmpOpenStatus
strong

Outcome of creating an endpoint connection through the session.

Returned from open_endpoint() so callers can distinguish "no such endpoint" from "OS rejected the connection".

Enumerator
Ok 
NotFound 
OsBackendUnavailable 
OsError 

◆ UtilityStatus

enum class pulp::midi::UtilityStatus : uint8_t
strong

Utility message status codes (bits 20-23 of word 0 for type 0x0).

JR Clock / JR Timestamp carry a 16-bit value in bits 0-15 of word 0 measured in 1/31250-second units (~32 µs resolution). They let hosts schedule UMP events with sub-block timing accuracy. JR Clock is the "current time" reference; JR Timestamp is "this packet's event happens at this offset". See the MIDI 2.0 UMP spec § Utility.

Enumerator
Noop 
JrClock 
JrTimestamp 
DeltaClockstampTicksPerQuarter 
DeltaClockstamp 

◆ VoiceStealStrategy

enum class pulp::midi::VoiceStealStrategy : uint8_t
strong

Voice-stealing strategy applied when note_on fires and no free voice is available.

Enumerator
Oldest 

Steal the oldest active voice (smallest note_id).

Quietest 

Steal the voice currently reporting the lowest peak_level().

Voices that don't override peak_level() all report 0, so this degrades to a deterministic earliest-found scan — useful for quiet-tail envelopes.

Lowest 

Steal the voice with the lowest MIDI note number.

Highest 

Steal the voice with the highest MIDI note number.

Priority 

Steal the voice with the lowest priority.

Ties broken by oldest-first.

Function Documentation

◆ all_note_offs()

std::size_t pulp::midi::all_note_offs ( std::span< MidiEvent > out,
std::uint16_t channel_mask = 0xFFFFu,
std::int32_t sample_offset = 0 )
inlinenoexcept

Brute-force fallback: a note-off for every key on the channels in channel_mask, whether or not it was tracked as held.

For hosts and formats that bypass the tracker. Up to kMaxNoteOffs events.

◆ bind_tracker_to_buffer()

void pulp::midi::bind_tracker_to_buffer ( MpeVoiceTracker & tracker,
MpeBuffer & out,
int32_t & current_sample_offset,
bool * expression_event_dropped = nullptr )
inline

Convenience: install tracker callbacks that forward events to out with the given sample offset.

When provided, expression_event_dropped is set if a bounded expression append fails so the owner can reconcile tracker and consumer state. Call once on the host thread during setup.

◆ classify_panic_message()

std::optional< PanicMessageKind > pulp::midi::classify_panic_message ( const MidiEvent & event)
inlinenoexcept

Classify a message as one of the panic kinds, or nullopt for anything else.

The value byte must be the one panic_messages() sends (0 for the CCs, centre for pitch bend): CC 123 with a nonzero value is not an All Notes Off.

◆ clear_midi_block()

void pulp::midi::clear_midi_block ( MidiBuffer & block)
inlinenoexcept

◆ copy_midi_block()

bool pulp::midi::copy_midi_block ( const MidiBuffer & src,
MidiBuffer & dst )
inlinenoexcept

◆ copy_midi_sidecars()

bool pulp::midi::copy_midi_sidecars ( const MidiBuffer & src,
MidiBuffer & dst )
inlinenoexcept

◆ copy_mpe_block()

bool pulp::midi::copy_mpe_block ( const MpeBuffer & src,
MpeBuffer & dst )
inlinenoexcept

◆ create_ble_midi_central()

std::unique_ptr< BleMidiCentral > pulp::midi::create_ble_midi_central ( )

Create the BLE MIDI central for the current platform.

Returns a scaffold central on platforms without a real backend; is_available() returns false on those builds so callers can degrade gracefully.

◆ create_midi_system()

std::unique_ptr< MidiSystem > pulp::midi::create_midi_system ( )

◆ diff_port_lists()

PortListDiff pulp::midi::diff_port_lists ( const std::vector< MidiPortInfo > & before,
const std::vector< MidiPortInfo > & after )
inline

Compare two snapshots.

A port is identified by id, name and direction together. Backends that number ports by position (so removing one shifts the ids of the rest) therefore report the shifted ports as removed and added, which is the truth for anyone holding the old ids. Duplicate entries are matched one for one.

◆ encode_ble_midi_packet()

std::vector< uint8_t > pulp::midi::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 bits per the spec).

Returns the encoded bytes; appends header + timestamp + message. Sysex callers should chunk to MTU - 1 bytes per call.

◆ equal_temperament_frequency()

double pulp::midi::equal_temperament_frequency ( int midi_note,
double a4_hz = 440.0 )
noexcept

◆ equal_temperament_note_for_frequency()

int pulp::midi::equal_temperament_note_for_frequency ( double frequency_hz,
double a4_hz = 440.0 )
noexcept

◆ feed_collect()

UmpSysex7Reassembler::Status pulp::midi::feed_collect ( UmpSysex7Reassembler & r,
std::uint32_t word0,
std::uint32_t word1,
std::vector< std::uint8_t > & out )
inline

Convenience: feed a packet and collect the emitted payload (if any) into out.

Returns the same Status as feed_packet. Useful for hosts that want a value-returning API rather than a callback. Not RT-safe (the lambda capture allocates) — for hot-path use, prefer the function-pointer feed_packet overload.

◆ for_each_held_note()

template<typename Fn>
void pulp::midi::for_each_held_note ( const MidiKeyboardState & keys,
std::uint16_t channel_mask,
Fn && fn )
inlinenoexcept

Call fn(channel, note) for every key keys reports as held on the channels in channel_mask, in channel then note order.

The allocation-free way to walk the held set when a caller writes straight into its own buffer instead of an intermediate span.

◆ held_note_offs()

std::size_t pulp::midi::held_note_offs ( const MidiKeyboardState & keys,
std::span< MidiEvent > out,
std::uint16_t channel_mask = 0xFFFFu,
std::int32_t sample_offset = 0 )
inlinenoexcept

Note-off events for exactly the notes keys reports as held, on the channels in channel_mask, in channel then note order.

This is the precise form: nothing is sent for a key that is not down. Returns the count written (truncated at a whole event if out is too small).

◆ is_valid_midi_channel()

bool pulp::midi::is_valid_midi_channel ( int midi_channel)
noexcept

◆ is_valid_midi_note()

bool pulp::midi::is_valid_midi_note ( int midi_note)
noexcept

◆ jr_value_16()

uint16_t pulp::midi::jr_value_16 ( const UmpPacket & p)
inline

Extract the 16-bit JR Clock / JR Timestamp value from a type-0 UMP packet (bits 0-15 of word 0).

Caller must check utility_status() first to know which kind of value this is. Returns 0 for non-JR utility messages.

◆ jr_value_microseconds()

double pulp::midi::jr_value_microseconds ( uint16_t value)
inline

◆ jr_value_seconds()

double pulp::midi::jr_value_seconds ( uint16_t value)
inline

◆ linear_controller_curve()

float pulp::midi::linear_controller_curve ( float normalized,
float  )
inlinenoexcept

◆ make_jr_clock()

UmpPacket pulp::midi::make_jr_clock ( uint8_t group,
uint16_t value )
inline

Build a JR Clock utility packet (status 0x1).

◆ make_jr_timestamp()

UmpPacket pulp::midi::make_jr_timestamp ( uint8_t group,
uint16_t value )
inline

Build a JR Timestamp utility packet (status 0x2).

◆ mcoded7_decode() [1/2]

std::vector< uint8_t > pulp::midi::mcoded7_decode ( const std::vector< uint8_t > & data)
inline

◆ mcoded7_decode() [2/2]

std::vector< uint8_t > pulp::midi::mcoded7_decode ( const uint8_t * data,
std::size_t size )

Decode an Mcoded7-encoded stream back to raw 8-bit bytes.

Returns an empty vector if the input is malformed (contains a byte with bit 7 set anywhere it must not, or a final group's high-bit byte references more low bytes than were actually transmitted).

◆ mcoded7_encode() [1/2]

std::vector< uint8_t > pulp::midi::mcoded7_encode ( const std::vector< uint8_t > & data)
inline

◆ mcoded7_encode() [2/2]

std::vector< uint8_t > pulp::midi::mcoded7_encode ( const uint8_t * data,
std::size_t size )

Encode raw 8-bit bytes into Mcoded7.

Output is ceil(input/7) * 8 - extra where extra trims trailing slots when the final group is short. The encoded stream contains only values <= 0x7F and is safe to embed in MIDI SysEx.

◆ midi1_event_to_ump2()

UmpPacket pulp::midi::midi1_event_to_ump2 ( const MidiEvent & ev,
uint8_t group = 0 )
inline

Convert a single MIDI 1.0 event to a MIDI 2.0 Channel Voice UMP packet.

Uses message type 0x4 so the host sees full MIDI 2.0 resolution. Group defaults to 0.

◆ midi1_to_ump()

void pulp::midi::midi1_to_ump ( const MidiBuffer & src,
UmpBuffer & dst,
uint8_t group = 0 )
inline

Append a MIDI 1.0 buffer to a UmpBuffer as MIDI 2.0 UMP packets.

◆ midi_block_has_drops()

bool pulp::midi::midi_block_has_drops ( const MidiBuffer & block)
inlinenoexcept

◆ mpe_buffer_has_drops()

bool pulp::midi::mpe_buffer_has_drops ( const MpeBuffer & block)
inlinenoexcept

◆ mpe_configuration_message()

MpeMessageList< 3 > pulp::midi::mpe_configuration_message ( uint8_t manager_channel,
uint8_t member_count )
inline

MPE Configuration Message: RPN 6 on a manager channel.

manager_channel is 0 for the lower zone or 15 for the upper zone; member_count is 0-15 and 0 disables the zone.

◆ mpe_pitch_bend_range_message()

MpeMessageList< 4 > pulp::midi::mpe_pitch_bend_range_message ( uint8_t channel,
float semitones )
inline

Pitch-bend range (RPN 0) for one channel: whole semitones in Data Entry MSB and the remaining hundredths in Data Entry LSB.

Ranges above 127 semitones are clamped and negative ranges become zero.

◆ mpe_zone_layout_messages()

MpeMessageList< 6 > pulp::midi::mpe_zone_layout_messages ( const MpeConfig & config)
inline

Zone layout: one Configuration Message for each zone, lower first.

A zone with no members is sent with a count of zero so a device that had it enabled turns it off.

◆ mpe_zone_pitch_bend_range_messages()

MpeMessageList< 64 > pulp::midi::mpe_zone_pitch_bend_range_messages ( const MpeZone & zone,
float manager_semitones,
float member_semitones )
inline

Pitch-bend ranges for a whole zone: the manager channel gets manager_semitones and every member channel gets member_semitones.

Send after the zone layout, because a Configuration Message resets ranges.

◆ normalise_midi_channel()

int pulp::midi::normalise_midi_channel ( int midi_channel)
noexcept

◆ panic_message_count()

std::size_t pulp::midi::panic_message_count ( const PanicScope & scope)
inlineconstexpr

Number of events panic_messages() will write for scope.

◆ panic_messages()

std::size_t pulp::midi::panic_messages ( const PanicScope & scope,
std::span< MidiEvent > out,
std::int32_t sample_offset = 0 )
inlinenoexcept

Write the panic messages for scope into out, channel by channel.

Within a channel the order is: sustain off, All Notes Off, All Sound Off, Reset All Controllers, pitch bend centre. Sustain goes first so a pedal-held note is released before the notes message arrives; the controller reset and bend centre come last so they are not undone by anything earlier.

Every event carries sample_offset. Returns the number of events written, which is panic_message_count(scope) when out is large enough; if out is too small the output is cut at a whole-event boundary and the return value is the count actually written (compare with panic_message_count() to detect truncation).

◆ parse_raw_midi_bytes()

void pulp::midi::parse_raw_midi_bytes ( const uint8_t * buf,
std::size_t n,
RawMidiParserState & state,
const RawMidiShortCallback & on_short,
const RawMidiSysexCallback & on_sysex )
inline

◆ pe_build_message() [1/2]

std::vector< uint8_t > pulp::midi::pe_build_message ( PeMessageType pe_type,
uint8_t ci_version,
MUID source,
MUID destination,
uint8_t request_id,
std::string_view header_json,
uint16_t total_chunks,
uint16_t chunk_number,
const std::vector< uint8_t > & payload )
inline

◆ pe_build_message() [2/2]

std::vector< uint8_t > pulp::midi::pe_build_message ( PeMessageType pe_type,
uint8_t ci_version,
MUID source,
MUID destination,
uint8_t request_id,
std::string_view header_json,
uint16_t total_chunks,
uint16_t chunk_number,
const uint8_t * payload,
std::size_t payload_size )

Builder for PE message wire bytes.

pe_type is one of the PE CiMessageType sub-IDs. header_json is plain UTF-8 JSON — the caller owns its schema (resource/, command/, status/, etc.).

Returns a complete SysEx envelope: F0 7E 7F 0D <sub-id> version src(4) dst(4) request_id hdr_len(2) hdr(Mcoded7) total(2) chunk_num(2) pay_len(2) pay(Mcoded7) F7.

NOT RT-safe — allocates the returned vector and the intermediate Mcoded7 encode buffers. SysEx is a main-thread protocol.

◆ pe_compress() [1/2]

std::optional< std::vector< uint8_t > > pulp::midi::pe_compress ( const std::vector< uint8_t > & payload)
inline

◆ pe_compress() [2/2]

std::optional< std::vector< uint8_t > > pulp::midi::pe_compress ( const uint8_t * data,
std::size_t size )

Compress a PE payload using zlib (RFC 1950). Returns nullopt on failure.

◆ pe_decompress() [1/2]

std::optional< std::vector< uint8_t > > pulp::midi::pe_decompress ( const std::vector< uint8_t > & blob)
inline

◆ pe_decompress() [2/2]

std::optional< std::vector< uint8_t > > pulp::midi::pe_decompress ( const uint8_t * data,
std::size_t size )

Decompress a zlib-wrapped PE payload. Returns nullopt on failure.

◆ pe_header_make()

std::string pulp::midi::pe_header_make ( std::string_view resource,
std::string_view command = {},
int status = 200 )

Builds a minimal PE JSON header.

Plenty of producers will want to build their own, but this covers the common "resource + status" case and keeps tests from depending on a JSON library at the surface.

command is one of "start" / "end" / "partial" / "full" / "notify". Empty command is omitted.

NOT RT-safe — returns a heap-backed std::string built via std::ostringstream.

◆ pe_header_parse()

bool pulp::midi::pe_header_parse ( std::string_view json,
std::string * resource,
std::string * command,
int * status )

Parse a PE JSON header.

Returns true on success and fills the out params; returns false if the header is not valid JSON. Only the fields used by the PE framing are extracted; everything else is considered application data and ignored here.

NOT RT-safe — populates out-param std::strings (allocations).

◆ pe_parse_message()

std::optional< PeChunk > pulp::midi::pe_parse_message ( const uint8_t * data,
std::size_t size )

Parse a single PE SysEx envelope into a PeChunk.

Returns nullopt if the buffer is not a well-formed PE message or fails Mcoded7 decode. Caller is responsible for matching request_id and reassembling chunks.

NOT RT-safe — allocates the PeChunk's header_json and payload vectors, plus a transient Mcoded7 decode buffer.

◆ pe_split_into_chunks()

std::vector< std::vector< uint8_t > > pulp::midi::pe_split_into_chunks ( PeMessageType pe_type,
uint8_t ci_version,
MUID source,
MUID destination,
uint8_t request_id,
std::string_view header_json,
const uint8_t * payload,
std::size_t payload_size,
std::size_t max_payload_bytes )

Split a logical PE Get/Set into wire-ready chunks bounded by max_payload_bytes of decoded resource per chunk.

The header is repeated verbatim in each chunk per the spec.

NOT RT-safe — allocates the outer vector and N inner SysEx buffers.

◆ read_midi_file()

std::optional< MidiFileData > pulp::midi::read_midi_file ( const std::string & path)

◆ scale_14_to_32()

uint32_t pulp::midi::scale_14_to_32 ( uint16_t v14)
inline

Scale a 14-bit value (0..16383, center 8192) to 32-bit (center 0x80000000) using MIDI 2.0 Min/Center/Max scaling so the midpoint is preserved exactly.

◆ scale_16_to_7()

uint8_t pulp::midi::scale_16_to_7 ( uint16_t v16)
inline

Scale 16-bit (0..65535) down to 7-bit (0..127).

◆ scale_32_to_14()

uint16_t pulp::midi::scale_32_to_14 ( uint32_t v32)
inline

Scale a 32-bit value back to 14-bit (0..16383), inverse of scale_14_to_32.

◆ scale_7_to_16()

uint16_t pulp::midi::scale_7_to_16 ( uint8_t v7)
inline

Scale a 7-bit value (0..127) to 16-bit (0..65535) per MIDI 2.0 spec.

Zero stays zero; 127 maps to 0xFFFF (no dead zone at the top).

◆ send_mpe_messages()

template<std::size_t N>
void pulp::midi::send_mpe_messages ( MidiOutput & output,
const MpeMessageList< N > & list )

Send every event in list to output, in order.

◆ ump_is_bank_valid_program_change()

bool pulp::midi::ump_is_bank_valid_program_change ( const UmpPacket & p)
inline

True when p is a MIDI 2.0 Program Change carrying a valid bank, i.e.

one whose faithful MIDI 1.0 rendering needs more than a single event.

◆ ump_program_change_fields()

Midi2ProgramChange pulp::midi::ump_program_change_fields ( const UmpPacket & p)
inline

Decode a MIDI 2.0 Program Change packet's fields without converting it.

Pure: depends only on p. Callers that need the complete MIDI 1.0 rendering of a bank-valid program change (CC 0, CC 32, then the program change) can build it from this, which ump_to_midi1_event alone cannot express through its single-event out-parameter.

◆ ump_to_midi1()

void pulp::midi::ump_to_midi1 ( const UmpBuffer & src,
MidiBuffer & dst )
inline

Flatten a UmpBuffer into a MidiBuffer.

Packets that have no MIDI 1.0 equivalent are skipped; if you care about them, use the MPE sidecar.

One packet is not always one event: a MIDI 2.0 program change whose Bank Valid option flag is set expands to the three MIDI 1.0 messages that carry the same meaning — CC 0 (bank MSB), CC 32 (bank LSB), then the program change — all at the source packet's sample offset and in that order, because a MIDI 1.0 receiver latches the bank bytes and applies them on the following program change.

◆ ump_to_midi1_event()

bool pulp::midi::ump_to_midi1_event ( const UmpPacket & p,
MidiEvent & out )
inline

Convert a UMP Channel Voice packet into a MIDI 1.0 event when possible.

Returns true on success and writes to out. Returns false for packets that have no MIDI 1.0 equivalent (e.g. per-note pitch bend, per-note CC), which callers typically route via the MPE sidecar instead.

Emits at most one event. The one message whose faithful MIDI 1.0 rendering needs more than that is a bank-valid MIDI 2.0 program change (CC 0, CC 32, program change); here it converts to the program change alone. Use ump_program_change_fields to read the bank, or ump_to_midi1, which emits the complete sequence.

◆ ump_words_for_message_type()

int pulp::midi::ump_words_for_message_type ( uint8_t message_type)
inline

Spec-complete UMP packet length, in 32-bit words, for a raw message-type nibble (bits 28-31 of word 0, values 0x0-0xF).

Covers every message type the MIDI 2.0 UMP spec defines — including the reserved ranges and the 128-bit UMP-Stream type (0xF) — so a cursor walking a packet stream advances past an unrecognized message by its true length instead of re-reading its trailing words as a fresh header:

0x0,0x1,0x2,0x6,0x7 -> 1 word (32-bit) 0x3,0x4,0x8,0x9,0xA -> 2 words (64-bit) 0xB,0xC -> 3 words (96-bit) 0x5,0xD,0xE,0xF -> 4 words (128-bit)

◆ utility_status()

UtilityStatus pulp::midi::utility_status ( const UmpPacket & p)
inline

Extract the utility status nibble from a type-0 UMP packet (bits 20-23 of word 0).

◆ walk_ump_packet()

template<typename Visitor>
void pulp::midi::walk_ump_packet ( const uint32_t * words,
uint32_t word_count,
Visitor && visit )
inline

Walk one UMP packet's contiguous word array, visiting each complete message in order.

visit(mt, msg_words, msg_word_count) fires once per message, where mt is the message-type nibble, msg_words points at the message's first word, and msg_word_count is its spec length. The cursor advances by that spec length (see ump_words_for_message_type), so a multi-word message's trailing words are never re-read as a fresh header. A message whose declared length runs past word_count (a truncated packet) stops the walk without visiting that message, so the visitor may safely read all msg_word_count words. Pure and allocation-free — safe on the audio thread.

◆ write_midi_file()

bool pulp::midi::write_midi_file ( const std::string & path,
const MidiFileData & data )

Variable Documentation

◆ kBankSelectLsbCc

uint8_t pulp::midi::kBankSelectLsbCc = 32
inlineconstexpr

◆ kBankSelectMsbCc

uint8_t pulp::midi::kBankSelectMsbCc = 0
inlineconstexpr

MIDI 1.0 controller numbers carrying Bank Select for a program change.

◆ kHasMtsEspTuning

bool pulp::midi::kHasMtsEspTuning = static_cast<bool>( 0 )
inlineconstexpr

◆ kHasScalaTuning

bool pulp::midi::kHasScalaTuning = static_cast<bool>( 0 )
inlineconstexpr

◆ kJrTicksPerSecond

double pulp::midi::kJrTicksPerSecond = 31250.0
constexpr

One JR Clock tick is 1/31250 of a second (~32 microseconds).

Convenience accessors convert a 16-bit JR value to a time delta.

◆ kMaxNoteOffs

std::size_t pulp::midi::kMaxNoteOffs = 16u * 128u
inlineconstexpr

Largest number of events held_note_offs() / all_note_offs() can produce.

◆ kMaxPanicMessages

std::size_t pulp::midi::kMaxPanicMessages = 16u * 5u
inlineconstexpr

Largest number of events panic_messages() can produce (16 channels x 5).

◆ kRawMidiSysexLimit

std::size_t pulp::midi::kRawMidiSysexLimit = 64 * 1024
inlineconstexpr

◆ kUnknownMidiChannel

int pulp::midi::kUnknownMidiChannel = -1
inlineconstexpr

◆ monophonic_serial_capacity_supported

template<typename Serial>
bool pulp::midi::monophonic_serial_capacity_supported
inlineconstexpr
Initial value:
= std::is_unsigned_v<Serial> &&
std::numeric_limits<Serial>::max() >
static_cast<std::uint64_t>(16 *
128)