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

Tracks MPE voice state across channels. More...

#include <mpe_voice_tracker.hpp>

Classes

struct  ZoneState

Public Member Functions

 MpeVoiceTracker (MpeConfig config=MpeConfig::standard_lower())
void set_config (const MpeConfig &cfg)
const MpeConfig & config () const
void set_member_bend_range (float semitones)
 Set the member-channel pitch-bend range in semitones (per-note bend) for every channel.
void set_manager_bend_range (float semitones)
 Set the manager-channel pitch-bend range in semitones (zone-wide bend) for both zones.
float member_bend_range () const
 Member-channel range for channel 1 (the first lower-zone member).
float member_bend_range (uint8_t channel) const
 Member-channel range for one specific channel (0-15).
float manager_bend_range () const
 Manager range of the lower zone.
float manager_bend_range (bool upper_zone) const
 Manager range of one zone.
void set_follow_configuration_messages (bool enabled)
 When enabled (the default), process(MidiEvent) follows two Registered Parameter Number messages as they arrive:
bool follows_configuration_messages () const
void set_assignable_timbre_index (std::optional< uint8_t > cc_index)
 Bind a MIDI 2.0 Assignable Per-Note Controller index (status 0x10) to per-note timbre.
std::optional< uint8_t > assignable_timbre_index () const
bool process (const MidiEvent &event)
 Process a single MIDI 1.0 event.
bool process (const UmpPacket &p)
 Process a MIDI 2.0 UMP Channel Voice packet.
std::size_t active_count () const
const MpeNoteState * find (uint8_t channel, uint8_t note) const
 Find the newest active note for channel/note, or nullptr.
std::size_t snapshot (MpeNoteState *out, std::size_t max) const
 Iterate all active notes.
bool note_generation_exhausted () const noexcept
 True once every representable nonzero note generation has been issued.
std::uint64_t refused_note_on_count () const noexcept
 Number of note-ons refused because the generation space was exhausted.
bool advance_note_generation (MpeNoteGeneration next) noexcept
 Advance the next generation without issuing it.
std::size_t pending_note_off_count () const noexcept
 Number of accepted physical note-offs waiting for a bounded lifecycle sink to make room.
bool flush_pending_note_offs ()
 Retry deferred note-offs in FIFO order.
const std::array< MpeNoteState, kMaxNotes > & slots () const
 Direct access to the underlying slots (active flag identifies live notes).
const ZoneState & lower_zone_state () const
const ZoneState & upper_zone_state () const
void reset () noexcept

Public Attributes

std::function< void(const MpeConfig &)> on_config_change
 Invoked after an incoming MPE Configuration Message changed config().
std::function< bool(const MpeNoteState *note_off, const MpeNoteState *note_on)> on_note_lifecycle
 Transactional note-lifecycle sink.
std::function< void(const MpeNoteState &)> on_note_on
std::function< void(const MpeNoteState &)> on_note_off
std::function< void(const MpeNoteState &)> on_pitch_bend
std::function< void(const MpeNoteState &)> on_pressure
std::function< void(const MpeNoteState &)> on_timbre

Static Public Attributes

static constexpr std::size_t kMaxNotes = 128
 Maximum concurrently active notes the tracker can hold.
static constexpr float kDefaultMemberBendSemitones = 48.0f
 Default member-channel pitch-bend range (MPE spec).
static constexpr float kDefaultManagerBendSemitones = 2.0f
 Default manager-channel pitch-bend range (MPE spec).

Detailed Description

Tracks MPE voice state across channels.

Not thread-safe; intended to be owned by a single consumer (usually the plugin's process() path or its format-adapter preprocessor).

Constructor & Destructor Documentation

◆ MpeVoiceTracker()

pulp::midi::MpeVoiceTracker::MpeVoiceTracker ( MpeConfig config = MpeConfig::standard_lower())
inlineexplicit

Member Function Documentation

◆ active_count()

std::size_t pulp::midi::MpeVoiceTracker::active_count ( ) const

◆ advance_note_generation()

bool pulp::midi::MpeVoiceTracker::advance_note_generation ( MpeNoteGeneration next)
noexcept

Advance the next generation without issuing it.

This control-thread operation succeeds only while no notes are active and only moves the cursor forward; zero, rewind, and exhausted-tracker requests fail. Primarily useful for deterministic lifecycle tests and state handoff.

◆ assignable_timbre_index()

std::optional< uint8_t > pulp::midi::MpeVoiceTracker::assignable_timbre_index ( ) const
inline

◆ config()

const MpeConfig & pulp::midi::MpeVoiceTracker::config ( ) const
inline

◆ find()

const MpeNoteState * pulp::midi::MpeVoiceTracker::find ( uint8_t channel,
uint8_t note ) const

Find the newest active note for channel/note, or nullptr.

◆ flush_pending_note_offs()

bool pulp::midi::MpeVoiceTracker::flush_pending_note_offs ( )

Retry deferred note-offs in FIFO order.

Returns true when the queue is empty. A sidecar calls this once, at sample offset zero, before ingesting each new block. Accepted releases invoke on_note_off exactly once.

◆ follows_configuration_messages()

bool pulp::midi::MpeVoiceTracker::follows_configuration_messages ( ) const
inline

◆ lower_zone_state()

const ZoneState & pulp::midi::MpeVoiceTracker::lower_zone_state ( ) const
inline

◆ manager_bend_range() [1/2]

float pulp::midi::MpeVoiceTracker::manager_bend_range ( ) const
inline

Manager range of the lower zone.

◆ manager_bend_range() [2/2]

float pulp::midi::MpeVoiceTracker::manager_bend_range ( bool upper_zone) const
inline

Manager range of one zone.

◆ member_bend_range() [1/2]

float pulp::midi::MpeVoiceTracker::member_bend_range ( ) const
inline

Member-channel range for channel 1 (the first lower-zone member).

◆ member_bend_range() [2/2]

float pulp::midi::MpeVoiceTracker::member_bend_range ( uint8_t channel) const
inline

Member-channel range for one specific channel (0-15).

◆ note_generation_exhausted()

bool pulp::midi::MpeVoiceTracker::note_generation_exhausted ( ) const
inlinenoexcept

True once every representable nonzero note generation has been issued.

Further note-ons are consumed by process() but refused without changing note state or invoking on_note_on. Exhaustion is permanent for this tracker instance, including across reset() and set_config().

◆ pending_note_off_count()

std::size_t pulp::midi::MpeVoiceTracker::pending_note_off_count ( ) const
inlinenoexcept

Number of accepted physical note-offs waiting for a bounded lifecycle sink to make room.

While nonzero, fresh note-ons and retriggers are consumed without mutation so the fixed queue cannot be overrun.

◆ process() [1/2]

bool pulp::midi::MpeVoiceTracker::process ( const MidiEvent & event)

Process a single MIDI 1.0 event.

Returns true if the event belonged to a known MPE zone (manager or member), false otherwise. A false return indicates the host should treat the event as a plain MIDI message (no MPE routing).

◆ process() [2/2]

bool pulp::midi::MpeVoiceTracker::process ( const UmpPacket & p)

Process a MIDI 2.0 UMP Channel Voice packet.

Returns true when the packet belongs to a known MPE zone (and was consumed), false otherwise. MIDI 2.0 per-note pitch bend (status 0x60) is routed directly to the matching note rather than going via member-channel state, preserving the full per-note resolution UMP carries.

Non-Channel-Voice UMP types (Utility, System, SysEx, Data128) are ignored and return false.

◆ refused_note_on_count()

std::uint64_t pulp::midi::MpeVoiceTracker::refused_note_on_count ( ) const
inlinenoexcept

Number of note-ons refused because the generation space was exhausted.

Saturates at the maximum representable value.

◆ reset()

void pulp::midi::MpeVoiceTracker::reset ( )
noexcept

◆ set_assignable_timbre_index()

void pulp::midi::MpeVoiceTracker::set_assignable_timbre_index ( std::optional< uint8_t > cc_index)
inline

Bind a MIDI 2.0 Assignable Per-Note Controller index (status 0x10) to per-note timbre.

The assignable PNC space is host- configured per the UMP spec; this hook lets a Pulp plugin advertise which assignable index it accepts as timbre. Pass std::nullopt to disable assignable-PNC routing entirely (registered PNC 74, status 0x00, continues to route to timbre regardless). Range: 0-127.

◆ set_config()

void pulp::midi::MpeVoiceTracker::set_config ( const MpeConfig & cfg)

◆ set_follow_configuration_messages()

void pulp::midi::MpeVoiceTracker::set_follow_configuration_messages ( bool enabled)
inline

When enabled (the default), process(MidiEvent) follows two Registered Parameter Number messages as they arrive:

  • RPN 6 on channel 1 or 16 (MPE Configuration Message): sets the member count of the lower or upper zone and updates config(). The newly configured zone wins when the zones would overlap; the other zone gives up the channels it can no longer keep. The affected channels return to the default bend ranges (48 member, 2 manager) and their cached expression state is cleared. Notes left on channels that no longer belong to a zone are released.
  • RPN 0 (pitch-bend range, semitones + cents) on any channel: sets that channel's range. On a zone manager channel it sets that zone's manager range; on any other channel it sets the member range for that channel only. The data value is applied when Data Entry MSB (CC 6) arrives and refined by Data Entry LSB (CC 38). Parameter selection is tracked per channel, and NRPN selection (CC 99/98) cancels RPN selection on that channel so NRPN data entry can never be read as RPN.

◆ set_manager_bend_range()

void pulp::midi::MpeVoiceTracker::set_manager_bend_range ( float semitones)

Set the manager-channel pitch-bend range in semitones (zone-wide bend) for both zones.

Non-positive values are ignored.

◆ set_member_bend_range()

void pulp::midi::MpeVoiceTracker::set_member_bend_range ( float semitones)

Set the member-channel pitch-bend range in semitones (per-note bend) for every channel.

Non-positive values are ignored.

◆ slots()

const std::array< MpeNoteState, kMaxNotes > & pulp::midi::MpeVoiceTracker::slots ( ) const
inline

Direct access to the underlying slots (active flag identifies live notes).

Stable indices across calls.

◆ snapshot()

std::size_t pulp::midi::MpeVoiceTracker::snapshot ( MpeNoteState * out,
std::size_t max ) const

Iterate all active notes.

Writes up to max records to out and returns the count written.

◆ upper_zone_state()

const ZoneState & pulp::midi::MpeVoiceTracker::upper_zone_state ( ) const
inline

Member Data Documentation

◆ kDefaultManagerBendSemitones

float pulp::midi::MpeVoiceTracker::kDefaultManagerBendSemitones = 2.0f
staticconstexpr

Default manager-channel pitch-bend range (MPE spec).

◆ kDefaultMemberBendSemitones

float pulp::midi::MpeVoiceTracker::kDefaultMemberBendSemitones = 48.0f
staticconstexpr

Default member-channel pitch-bend range (MPE spec).

◆ kMaxNotes

std::size_t pulp::midi::MpeVoiceTracker::kMaxNotes = 128
staticconstexpr

Maximum concurrently active notes the tracker can hold.

◆ on_config_change

std::function<void(const MpeConfig&)> pulp::midi::MpeVoiceTracker::on_config_change

Invoked after an incoming MPE Configuration Message changed config().

◆ on_note_lifecycle

std::function<bool(const MpeNoteState* note_off, const MpeNoteState* note_on)> pulp::midi::MpeVoiceTracker::on_note_lifecycle

Transactional note-lifecycle sink.

note_off and note_on are null when that half is absent; a retrigger supplies both, in retirement-then- start order. Return false when the complete lifecycle cannot be represented. Rejection leaves a retrigger slot and generation cursor unchanged and suppresses the observer callbacks below.

The pointers are valid only for the duration of the call. This hook is intended for bounded realtime sinks such as MpeBuffer; ordinary observers should continue to use on_note_on / on_note_off.

◆ on_note_off

std::function<void(const MpeNoteState&)> pulp::midi::MpeVoiceTracker::on_note_off

◆ on_note_on

std::function<void(const MpeNoteState&)> pulp::midi::MpeVoiceTracker::on_note_on

◆ on_pitch_bend

std::function<void(const MpeNoteState&)> pulp::midi::MpeVoiceTracker::on_pitch_bend

◆ on_pressure

std::function<void(const MpeNoteState&)> pulp::midi::MpeVoiceTracker::on_pressure

◆ on_timbre

std::function<void(const MpeNoteState&)> pulp::midi::MpeVoiceTracker::on_timbre

The documentation for this class was generated from the following file: