Contrapunk Audio Chain Cutover
Pattern
When replacing a built-in audio component, keep the chain contract stable and switch only the block instantiation behind a Cargo feature. The router should continue sending the same SynthEvent/MIDI events unless a dedicated event bridge is explicitly part of the task.
Default Approach
- Preserve public commands and UI behavior first.
- Add a small constructor/helper that selects the first
AudioBlock:#[cfg(feature = "elixir-synth")]→ElixirSynthBlock#[cfg(not(feature = "elixir-synth"))]→ legacySynth
- Keep
BlockDescriptortype IDs honest:- legacy:
builtin.synth - Elixir:
builtin.elixir-synth
- legacy:
- Do not import root
src/fxintoelixir-core; share only neutral primitives throughcontrapunk-dsp. - Validate both feature configurations:
cargo check -p contrapunkcargo check -p contrapunk --features elixir-synth- relevant Tauri/chain tests when touched
A-Cut Gotchas
src-tauri/src/audio_clock.rsowns the cpal chain startup; this is where the first synth block is instantiated.src/chain/elixir_block.rsalready adaptselixir_core::Engineto theAudioBlocktrait behindelixir-synth.src-tauri/src/commands/engine.rsstill emitsSynthEventto the built-in synth channel; if Elixir is selected through the chain, ensure events still reach the chain or add a bridge explicitly.- Keep non-F32 cpal paths safe; they currently output silence and tick transport only.
- Avoid adding locks or allocation to the cpal callback.
Success Criteria
- Both feature-on and feature-off builds compile.
- Feature-off behavior remains legacy-compatible.
- Feature-on chain reports/uses
builtin.elixir-synthand renders notes through Elixir. - No new audio-thread allocation or blocking beyond existing chain behavior.
Source: contrapunk-audio/contrapunk — distributed by TomeVault.