Description
The DSP graph currently processes all blocks unconditionally during the audio callback, regardless of whether a voice is actively producing sound. This leads to unnecessary CPU usage when:
- No note is currently playing (voice is idle)
- A voice has completed its release phase and the envelope has reached the floor threshold
- The signal passing through a chain of blocks has dropped below an audible level
Proposed Solution
Implement a voice culling system that tracks voice activity state and skips processing for voices that aren't producing meaningful output. This optimization should:
- Track voice lifecycle state - Extend
VoiceState or EnvelopeBlock to expose whether a voice is idle, active, or releasing
- Provide culling hooks - Allow the graph or plugin to query whether processing should be skipped
- Support threshold-based culling - Optionally detect when output signals fall below a configurable floor (e.g., -120dB)
- Maintain real-time safety - No allocations or locks in the culling decision path
Current Implementation Context
VoiceState (bbx_dsp/src/voice.rs) tracks monophonic voice state with legato support
EnvelopeBlock (bbx_dsp/src/blocks/modulators/envelope.rs) has EnvelopeStage enum (Idle, Attack, Decay, Sustain, Release)
- Envelope uses
ENVELOPE_FLOOR constant (~-120dB) for floor detection
- Graph processes all blocks in topological order without activity checks
Design Considerations
- Should work with current monophonic design and future polyphonic voice pool
- Culling decision should be cheap (branch on stage/flag, not signal analysis)
- Consider exposing
is_idle() or should_cull() API on EnvelopeBlock
- May need to propagate "active" state through connected blocks in graph
Open questions
- Should culling be opt-in per block or automatic based on envelope state?
- How should culling interact with blocks that have internal state (filters with decay, reverb tails)?
- Should there be a configurable "tail time" after release to handle effects?
- When polyphony is added, should voice culling feed into voice stealing priority?
Acceptance criteria
- Voice activity state (idle/active/releasing) is queryable from EnvelopeBlock or VoiceState
- Processing can be conditionally skipped for idle voices without introducing artifacts
- No allocations or locks added to the audio thread for culling decisions
- CPU usage measurably decreases when voices are idle vs. always processing
- Existing examples and tests continue to work correctly
Tasks
TBD
Description
The DSP graph currently processes all blocks unconditionally during the audio callback, regardless of whether a voice is actively producing sound. This leads to unnecessary CPU usage when:
Proposed Solution
Implement a voice culling system that tracks voice activity state and skips processing for voices that aren't producing meaningful output. This optimization should:
VoiceStateorEnvelopeBlockto expose whether a voice is idle, active, or releasingCurrent Implementation Context
VoiceState(bbx_dsp/src/voice.rs) tracks monophonic voice state with legato supportEnvelopeBlock(bbx_dsp/src/blocks/modulators/envelope.rs) hasEnvelopeStageenum (Idle, Attack, Decay, Sustain, Release)ENVELOPE_FLOORconstant (~-120dB) for floor detectionDesign Considerations
is_idle()orshould_cull()API on EnvelopeBlockOpen questions
Acceptance criteria
Tasks
TBD