The monophonic voice, in time order.
Chord sounding at a given beat, or null.
Key context for leading-tone detection.
Other sounding voices at a given beat.
One annotation per input note.
import { analyzeVoice, makeChord, majorKey } from '@libraz/libcantus';
const voice = [
{ id: 0, pitch: 60, startBeat: 0, durationBeat: 1 },
{ id: 1, pitch: 62, startBeat: 1, durationBeat: 1 },
{ id: 2, pitch: 64, startBeat: 2, durationBeat: 1 },
];
const cMajor = makeChord(0, 'maj');
const labels = analyzeVoice(voice, () => cMajor, majorKey(0), () => []);
labels; // one AnalyzedNote per input note, in the same order
Label every note of a voice with its theory roles.
Each note is classified against the chord sounding at its beat. Chord tones get a role label; non-chord tones are matched, in order, as suspensions (prepared by an identical consonant pitch and resolving by step), passing tones, neighbors, anticipations, and escape tones, then fall back to tension, avoid, or an unresolved-dissonance label. Leading-tone resolutions are noted additionally.
The voice is expected to be monophonic (one note at a time). Notes sharing an onset are treated as simultaneous cluster members, not melodic neighbors, so they receive no melodic labels (suspension, passing, neighbor, anticipation, escape) — only harmonic ones. Callers with truly polyphonic material should split it into monophonic sub-voices first, as
analyzeArrangementdoes.