let harmony = Engine.new(Palestrina);
fn detect_pitch(buf) -> Note
const MAX_VOICES: usize = 4;
voice_leading.reject_parallels()
struct GuitarInput { onset }
engine.harmonize(note, scale)
fn detect_pitch(
  buf: &[f32]
) -> Option<Note>
let voices =
  counterpoint::
    generate(line, mode);
if interval == Fifth
  && motion == Parallel
  { reject() }
#[inline(always)]
fn onset(
  frame: &Frame
) -> bool
block_size: 128
sample_rate: 48_000
latency_ms: 7.2
// Palestrina
// counterpoint
// rules.rs
OPEN SOURCE · GITHUB · DISCORD

PROJECT COMMUNITY

Read the source, report a bug, propose a feature, or contribute a pull request.

"

The Jazz mode voicings are better than I am. I mean that as a compliment to the code.

KE
@kenji_osaka
JAZZ GUITARIST
"

I make my species-counterpoint students check their homework against Palestrina mode. Beats my red pen.

LE
@lena.strings
COMPOSITION PROF
"

Random-below-no-2nds into a Rhodes plugin hosted in-app. Instant ECM record.

DR
@drifter_7
AMBIENT PRODUCER
"

Looked under the hood expecting hacks. Found a voice-leading solver with comments. Read the whole thing on a flight.

BO
@boris.k
DSP ENGINEER
"

We run two guitars into two instances, quantize to different modes. Feels like a third band member.

SU
@sunday_kids
POST-ROCK BAND
"

Parallel-fifths rejection on by default is a value statement. Ship more value statements.

MA
@mara.note
MUSIC THEORIST
◆ ◇ ◆ ◇ ◆RECENT COMMITS
  1. af83873feat(dsp): add Chapter 3 studio exercises (#195)Vibhav Bobade · 1m ago
  2. decf23afeat(dsp): add cumulative wavetable learning exercises (#192)Vibhav Bobade · 31m ago
  3. f16ee0efix(ui): keep scale keys piano-coloredVibhav Bobade · 1d ago
  4. c557bb5fix(harmony): apply spread changes to next notesVibhav Bobade · 1d ago
  5. 7134554fix(harmony): keep spread voicings in keyVibhav Bobade · 1d ago
  6. 4dfb985fix(routing): preserve input owners on stale harmony releaseVibhav Bobade · 1d ago
  7. 60335c4feat(determinism): add reset and close lifecycle gapsVibhav Bobade · 1d ago
  8. 66eb9f5chore(wasm): refresh bindings after rebaseVibhav Bobade · 1d ago
  9. f73a224refactor(harmony): keep held-owner replay explicitVibhav Bobade · 1d ago
  10. 85335fbfix(determinism): canonicalize performance lifecycleVibhav Bobade · 1d ago
  11. 992e776test(determinism): define end-to-end replay contractsVibhav Bobade · 1d ago
  12. f76ea67feat(routing): add all-to-synth overrideVibhav Bobade · 1d ago
  13. 1e41593fix(ui): address synth mixer review feedbackVibhav Bobade · 3d ago
  14. e715573fix(ui): keep inactive voice routes visibleVibhav Bobade · 3d ago
  15. c9996d8fix(ui): keep Elixir as the single synth mixerVibhav Bobade · 4d ago
  16. b56f051chore(deps): bump clap from 4.6.4 to 4.6.6 in the rust-minor-patch groupdependabot[bot] · 2d ago
  17. 9c38712fix(ui): anchor expression roll to bottom keyboardVibhav Bobade · 5d ago
  18. 87a13dcfix(presets): make cpk persistence safe and reloadableVibhav Bobade · 6d ago
  19. 8a2b0cafix(presets): resolve AppData path resolution and remount PresetManager UIPrashant Singh · 8d ago
  20. 3823a21fix: resolve workspace compilation errors and imports for tauri preset commandsPrashant Singh · 12d ago
AVAILABLE FORMATS

BROWSER, DESKTOP,
OR SOURCE.

Browser at app.contrapunk.com. macOS DMG from GitHub Releases. Build from source with cargo tauri dev.