The tracks to analyse.
Analysis options; see ArrangementOptions.
The inferred harmony, per-track annotations, cadences, and conflicts.
import { analyzeArrangement } from '@libraz/libcantus';
const melody = [
{ pitch: 60, startBeat: 0, durationBeat: 2 },
{ pitch: 67, startBeat: 2, durationBeat: 2 },
];
const { key, conflicts } = analyzeArrangement([{ role: 'melody', notes: melody }]);
conflicts; // notes clashing with the inferred harmony, worst severity first
Analyse a whole arrangement against a single inferred harmony.
The chord timeline and key are inferred from the pooled notes of every track (see chordTimelineFromNotes); pooling all voices is robust even when roles are absent or a track doubles the harmony, so it is preferred over deriving the harmony from a subset. Zero- and negative-length notes never sound, so they are dropped at ingest and appear in neither the annotations nor the conflicts.
Voice analysis assumes one monophonic voice at a time, so each track is first partitioned into monophonic sub-voices (a polyphonic block-chord track fans out into one sub-voice per simultaneous note; a monophonic track is unaffected). Each sub-voice is labelled note-by-note against the harmony with analyzeVoice, seeing every other sub-voice — including siblings within the same track — as its accompaniment. Every note is then re-checked with evaluateSafety at its onset and again at each chord change it sustains across, so a held note that clashes with a later chord is caught: evaluations that are not NoteSafety.Safe become conflicts, one per clashing beat, sorted worst severity first and then by beat.