HOW IT WORKS.
Six stages, one audio thread, no allocations in the hot path.
CPAL captures guitar audio at 48kHz in 128-sample blocks, ring-buffered to the DSP thread. MIDI input from any controller also lands here.
let stream = cpal::default_host() .default_input_device()? .build_input_stream(&cfg, …)?;
McLeod autocorrelation + single-cycle refinement. Onset via spectral flux. Pitch voting smooths jitter. Sub-3ms per buffer.
let note = pitch_detection::
mcleod_pitch(
&buf, 48_000.0
)?; HarmonyEngine resolves the note against the current Scale and HarmonyMode. Stateful modes (Contrary, Strict CP) advance their internal history.
let line = engine .harmonize_note_on(note)?;
VoiceLeadingProcessor places each harmony pitch-class into Soprano / Alto / Tenor / Bass, cartesian-products the combinations, scores them, picks the best.
let chord = solver .revoice_chord(line, prev)?;
Jitter, velocity, swing, duration. A per-note record ties Note-Off to its Note-On so releases stay coherent.
let humanized = humanizer .humanize_note_on(note, vel);
Virtual MIDI (CoreMIDI / WinMM / ALSA) to your DAW, or straight into a CLAP plugin hosted inside Contrapunk via clack-host.
midi_out.send(NoteOn {
ch, note, vel
})?;