The notes to analyze (any number of tracks, flattened).
Analysis options; see ChordTimelineOptions.
The inferred timeline, the key regions it ran against, the key that prevails across them, and per-segment confidences.
import { chordTimelineFromNotes } from '@libraz/libcantus';
const notes = [
{ pitch: 60, startBeat: 0, durationBeat: 2 }, // C
{ pitch: 64, startBeat: 0, durationBeat: 2 }, // E
{ pitch: 67, startBeat: 0, durationBeat: 2 }, // G
];
const { timeline, prevailingKey } = chordTimelineFromNotes(notes);
timeline.at(0); // the chord inferred over beat 0, or null
prevailingKey; // the key held longest across the piece
Infer a chord timeline from raw multi-track notes.
Chord boundaries are searched for rather than assumed: the span is examined in slots of one main pulse, subdivided as finely as
minChordBeatsasks for, and the change points that best explain the notes are chosen, trading each slot's harmonic fit against a cost per chord change that a strong beat discounts. A bar holding two chords therefore yields two segments and a bar holding one yields one, without the caller having to know the harmonic rhythm in advance. Passsegmentation: 'grid'to cut a segment everyharmonicRhythmbeats instead.Each settled segment's chord is inferred from a pitch-class weight histogram of the notes overlapping it (weight = overlap duration x velocity x metric-accent bonus for onsets in the segment). Adjacent segments carrying the identical chord are merged; a stretch with no notes produces no segment, so
at(beat)returns null there. Each segment carries a confidence in [0, 1] — the fraction of its weight explained by chord tones, reduced when the match is inexact, and duration-weighted across anything merged into it.Notes with a zero or negative duration never sound, so they are dropped at ingest: they contribute to neither the key inference, the span, nor any window's histogram.
Bar lines, downbeats and the metric accents the boundary search discounts by all follow the signature in force at the beat in question, so a piece that changes meter is read in the meter it is actually in. The first downbeat is beat 0, so a pickup is written at negative beats and the search still finds the bar lines where the score has them.