Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock > Class Template Reference

Fixed-capacity, sample-accurate multi-lane linear step player. More...

#include <step_player.hpp>

Classes

struct  Spec

Public Member Functions

constexpr StepPlayer (Spec spec={}) noexcept
constexpr bool valid () const noexcept
constexpr const Spec & spec () const noexcept
constexpr StepPlayerError set_step (std::size_t lane, std::size_t index, StepPlayerStep step) noexcept
constexpr const StepPlayerStep * get_step (std::size_t lane, std::size_t index) const noexcept
constexpr void clear_steps () noexcept
 Authored-step edits are control-thread work; playback state is untouched so an in-flight note still receives its owned release.
std::size_t sounding_note_count () const noexcept
MidiUtilityProcessReport process (const MidiBuffer &input, MidiBuffer &output, const StepPlayerBlock &block) noexcept
MidiUtilityProcessReport flush_transport (MidiBuffer &output) noexcept
 Release every owned note and resync lane clocks.
MidiUtilityProcessReport reset (MidiBuffer &output) noexcept
 Release every owned note and discard all playback state.
std::optional< StepPlayerCoordinate > coordinate_at (std::size_t lane, timebase::TickPosition tick) const noexcept
 The coordinate a lane's grid selects at tick.
std::optional< StepPlayerCoordinate > upcoming_coordinate (std::size_t lane) const noexcept
 The coordinate a lane will fire next, or std::nullopt before the lane's clock has been placed by a transport block.

Static Public Member Functions

static constexpr StepPlayerCapacityContract contract () noexcept
static constexpr bool valid_lane_spec (const StepPlayerLaneSpec &lane) noexcept
static constexpr bool valid_spec (const Spec &spec) noexcept
static constexpr StepPlayerError validate_step (const StepPlayerStep &step) noexcept

Static Public Attributes

static constexpr std::size_t kMaximumCells = MaxLanes * MaxSteps
static constexpr std::size_t kMaximumSoundingNotes = MaxLanes * (MaxRatchetHits + 1)
 A lane can hold one ratchet burst plus one note kept overlapping by a slide, so the ledger never needs more than this to avoid stranding.
static constexpr std::int32_t kMaximumTimingOffsetTicks
 A micro-timing nudge is bounded to one quarter note: far wider than any swing or groove needs, and narrow enough that a value which would reorder the grid wholesale is refused rather than silently honoured.

Detailed Description

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
class pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >

Fixed-capacity, sample-accurate multi-lane linear step player.

Each lane is an independent loop of up to MaxSteps authored steps over one shared transport: its own length, step duration, direction walk, and choke group. Lanes with different lengths or divisions realign at the common multiple of their grid ordinals, so polymeter falls out of the exact tick arithmetic rather than a dedicated mechanism.

The player is a generator: input passes through unchanged and generated notes are merged in sample order. Every emitted attack owns exactly one later release in the fixed sounding ledger; an attack that cannot secure a ledger slot is suppressed rather than stranded, and a release that cannot be emitted is retained as debt and retried. Step selection, probability, and random walks are pure functions of the grid coordinate, so the event stream is identical for any block partition of the same transport span. A transport discontinuity is a flush plus a resync: every owned note is released and every lane clock realigns to the grid at the new position.

Constructor & Destructor Documentation

◆ StepPlayer()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::StepPlayer ( Spec spec = {})
inlineexplicitconstexprnoexcept

Member Function Documentation

◆ clear_steps()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
void pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::clear_steps ( )
inlineconstexprnoexcept

Authored-step edits are control-thread work; playback state is untouched so an in-flight note still receives its owned release.

◆ contract()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
constexpr StepPlayerCapacityContract pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::contract ( )
inlinestaticconstexprnoexcept

◆ coordinate_at()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
std::optional< StepPlayerCoordinate > pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::coordinate_at ( std::size_t lane,
timebase::TickPosition tick ) const
inlinenoexcept

The coordinate a lane's grid selects at tick.

Pure in the lane's authored spec and the tick: it consults no playback state and advances nothing, so a caller may ask ahead of process() which cell is about to speak. std::nullopt means the lane does not exist or carries no steps.

◆ flush_transport()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
MidiUtilityProcessReport pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::flush_transport ( MidiBuffer & output)
inlinenoexcept

Release every owned note and resync lane clocks.

Use this for a transport stop, seek, or loop boundary handled outside process().

◆ get_step()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
const StepPlayerStep * pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::get_step ( std::size_t lane,
std::size_t index ) const
inlineconstexprnoexcept

◆ process()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
MidiUtilityProcessReport pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::process ( const MidiBuffer & input,
MidiBuffer & output,
const StepPlayerBlock & block )
inlinenoexcept

◆ reset()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
MidiUtilityProcessReport pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::reset ( MidiBuffer & output)
inlinenoexcept

Release every owned note and discard all playback state.

◆ set_step()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
StepPlayerError pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::set_step ( std::size_t lane,
std::size_t index,
StepPlayerStep step )
inlineconstexprnoexcept

◆ sounding_note_count()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
std::size_t pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::sounding_note_count ( ) const
inlinenoexcept

◆ spec()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
const Spec & pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::spec ( ) const
inlineconstexprnoexcept

◆ upcoming_coordinate()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
std::optional< StepPlayerCoordinate > pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::upcoming_coordinate ( std::size_t lane) const
inlinenoexcept

The coordinate a lane will fire next, or std::nullopt before the lane's clock has been placed by a transport block.

◆ valid()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
bool pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::valid ( ) const
inlineconstexprnoexcept

◆ valid_lane_spec()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
constexpr bool pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::valid_lane_spec ( const StepPlayerLaneSpec & lane)
inlinestaticconstexprnoexcept

◆ valid_spec()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
constexpr bool pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::valid_spec ( const Spec & spec)
inlinestaticconstexprnoexcept

◆ validate_step()

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
constexpr StepPlayerError pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::validate_step ( const StepPlayerStep & step)
inlinestaticconstexprnoexcept

Member Data Documentation

◆ kMaximumCells

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
std::size_t pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::kMaximumCells = MaxLanes * MaxSteps
staticconstexpr

◆ kMaximumSoundingNotes

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
std::size_t pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::kMaximumSoundingNotes = MaxLanes * (MaxRatchetHits + 1)
staticconstexpr

A lane can hold one ratchet burst plus one note kept overlapping by a slide, so the ledger never needs more than this to avoid stranding.

◆ kMaximumTimingOffsetTicks

template<std::size_t MaxLanes = 8, std::size_t MaxSteps = 32, std::size_t MaxRatchetHits = 8, std::size_t MaximumStepsPerBlock = 128>
std::int32_t pulp::midi::StepPlayer< MaxLanes, MaxSteps, MaxRatchetHits, MaximumStepsPerBlock >::kMaximumTimingOffsetTicks
staticconstexpr
Initial value:
=
static_cast<std::int32_t>(timebase::kTicksPerQuarter)
constexpr std::int64_t kTicksPerQuarter
Definition tick.hpp:54

A micro-timing nudge is bounded to one quarter note: far wider than any swing or groove needs, and narrow enough that a value which would reorder the grid wholesale is refused rather than silently honoured.

The field is 32-bit deliberately. A quarter is 705'600 ticks, so a 16-bit offset would cap at 32'767 — under a fifth of a single sixteenth step, which cannot express swing at all.


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