The lead line, chord context callback, key, and generation knobs.
The counter line as note events sorted by onset; [] for an empty melody.
If an option names a value outside its range — including a weight override that is not a finite number, which is rejected before any candidate is scored rather than absorbed into the ranking.
import { generateCounterMelody, majorKey, parseChordSymbol } from '@libraz/libcantus';
const melody = [{ pitch: 72, startBeat: 0, durationBeat: 2 }];
const counter = generateCounterMelody({
melody,
chordAt: () => parseChordSymbol('C'),
key: majorKey(0),
}); // note events below the melody, in onset order
Generate a counter melody against a lead line.
Onsets are chosen per
rhythm:'follow'places one counter note on each melody onset;'complement'fills melody rests and sustains and reinforces an occasional strong beat, spaced at least a beat apart. Each onset's pitch is picked from the safe pitches in the counter register (chord tones and consonant tensions against the sounding melody note), rejecting candidates that would form a parallel perfect interval with the melody or sit on the wrong side of it, then ranked by the profile's weights — which is where the two styles part company:'strict'prefers contrary motion and penalizes similar,'pop'rewards a run of parallel thirds or sixths and is content with a pedal. Ties are broken by a seeded perturbation far below any real score difference (seeTIE_BREAK_JITTER), so the same seed always yields the same line while a different seed can only reshuffle candidates that were already equally good. Notes extend to the next counter onset (the last to the melody's end) at a velocity slightly under the melody.