The sounding notes, in time order.
Where the last note stops sounding.
Measured from beat 0 rather than from the first onset, so a score that opens with a pickup reports the beat its music ends on and not its length. An empty score ends at 0.
The meter map the notes are read against.
The tempo map the notes are read against.
A copy of the underlying plain data.
StaticofBuild a score from note events.
A bare time signature becomes a one-entry meter map taking effect at beat 0, and a bare bpm a one-entry tempo map at beat 0; a score given neither is read in 4/4 at 120 bpm, which is what every meter-aware function in the library assumes when its caller names nothing.
The sounding notes, in any order.
Optionalopts: ScoreOptions
The meter, tempo, and key to read them against.
The score.
StaticemptyAn empty score, for a track that has not been written yet.
Optionalopts: ScoreOptionsStaticfromRead note events whose times are in ticks rather than beats.
Onsets and durations are converted; nothing else about the events changes. A negative tick count is a pickup and stays one, since beat 0 is the first downbeat at either resolution.
The events, with tick-valued onsets and durations.
Ticks per quarter note.
Optionalopts: ScoreOptions
The meter, tempo, and key to read them against.
The score, in beats.
StaticfromStaticfromThe stretch of the score between two beats.
A note is kept when its onset falls in [fromBeat, toBeat); the notes keep
their absolute onsets, so the slice stays in the score's own time and a
slice taken across a meter or tempo change is still read against it.
First beat of the stretch, inclusive.
End of the stretch, exclusive.
The score holding those notes.
The score moved along the timeline.
The meter and tempo maps stay where they are: they describe the bars and the pulse the music is played against, and moving the notes over them is how a phrase is heard a bar later. A negative shift moves music before the first downbeat, which is where a pickup lives.
How far to move, in quarter-note beats; negative moves earlier.
The moved score.
The score moved by a number of semitones.
A carried key moves with the notes, so the score that comes back is read in the key it now sounds in and every analysis taken from it — its timeline, its numerals, its phrases — is taken against that key rather than against the one it was written in.
The signed semitone offset.
The transposed score.
The score moved by a spelled interval, keeping the spelling.
Note events carry a MIDI pitch and no spelling of their own, so the interval contributes its size and its direction; a carried key is what keeps the spelling, and it moves with the notes — by the interval itself, so an augmented fourth and a diminished fifth land on differently spelled keys.
An interval name (e.g. 'A4', '-m3'), plain interval
data, or an Interval; a descending interval moves down.
The transposed score.
The notes pulled onto a grid.
Onsets and durations are both snapped to the nearest multiple of the grid, counted from beat 0 in both directions, so a pickup is quantized against the same grid the downbeat is. A duration that would round to nothing keeps one grid unit, since a note quantized out of existence is a note lost.
The grid unit in quarter-note beats (0.25 for sixteenths).
The quantized score.
The notes pushed off the grid the way a player would.
The metric accents are read from the score's opening signature, since
HumanizeOptions names one signature rather than a map; pass ts
to read a later stretch of a score that changes meter.
Optionalopts: HumanizeOptions
Jitter amounts, accent depth and the seed; see HumanizeOptions.
The humanized score.
The notes with ornaments added.
Strong positions are read from the score's opening signature, since OrnamentOptions names one signature rather than a map.
Optionalopts: OrnamentOptions
The ornament style, how much is decorated, and the seed; see OrnamentOptions.
The ornamented score.
The notes placed on a groove template's slots.
The template's per-bar grid is laid out against the score's opening signature, which is the signature the template must have been extracted under; a template carrying a different one is refused rather than drifting.
The groove template, from extractGrooveTemplate.
The score with the template's feel imposed.
The feel of the score's own timing, as a template another score can take on.
The other direction of Score.groove: a played-in take is measured slot by slot for how far ahead of or behind the grid it sits and how hard it is struck, and the reading is handed back as a template. Only the subdivision is named, because the score already holds the meter the grid is laid out against — its opening signature, which is the one the template records and the one an apply-time meter is checked against.
Optionalsubdivision: number
Grid steps per quarter-note beat; a sixteenth-note grid by default, which is where swing, push and drag are audible.
The template these notes describe.
The harmony the notes spell out, in time.
The score's meter map and its key, when it carries one, are read against unless the options name others.
Optionalopts: ChordTimelineOptions
Analysis options; see ChordTimelineOptions.
The inferred timeline, carrying the key regions the analysis found and how sure it is of each segment.
The key held longest across the score, when one can be read.
A score carrying a key answers with it. Otherwise the key regions are searched for and the one holding for the most beats wins, so a piece that digresses is named by the key it keeps coming back to rather than by the key it opens on. A score with nothing sounding has no key at all.
The prevailing key, or undefined when nothing sounds.
Every key region the score passes through, in time order.
A score that was told its key answers with that key across the whole of it,
as Score.key and Score.timeline do: the caller has already
answered the question, and searching for a modulation away from a stated
key would make the option read as a hint where it was given as an
instruction. Only totalBeats is read from the options there, since the
rest tune a search that is not run.
Optionalopts: KeyTimelineOptions
Analysis options; see KeyTimelineOptions.
The regions; empty when nothing sounds.
Every key the score could be in, ranked, read as one key across the whole of it.
Score.keys divides the score into the regions it passes through; this ranks the readings of the score entire, so a caller can see what the winner beat and by how much. Each note counts for its duration times its velocity — the measure chord inference already weighs by — so the sustained harmony that establishes a key outweighs a run of ornaments over it, which is what makes a score the right thing to ask rather than a bare histogram of its pitches.
Optionalopts: Omit<DetectKeyOptions, "weights">
Which profile to rank with, whether the church modes take part, whether each candidate explains itself, and the budget; the weights come from the notes themselves, so they are not on offer.
The candidates, best first; empty when nothing sounds.
The phrases the notes fall into.
The cadences are read from the score's own harmony, so the chord timeline does not have to be built and passed in by hand.
Optionalopts: PhraseOptions
Analysis options; see PhraseOptions.
The phrases, in time order.
The cadences that close the score's phrases, ranked by how much structure each one closes.
Score.phrases says where the phrases end; this says which of those endings matter. Two chords alone cannot tell the close of a piece from a passing confirmation halfway through a hyperbar, and a phrase reading can, because it knows what the cadence closes and where that sits. Phrases no cadence closes are left out.
Optionalopts: PhraseOptions
How the phrases are read; see PhraseOptions.
The cadences, heaviest first; ties keep the earlier one.
The sections the score divides into.
Optionalopts: FormSectionOptions
Analysis options; see FormSectionOptions.
The sections, in time order.
The bar-level pulse above the meter.
The cadences come from the score's own harmony unless the caller passes their own, so this reads the same grouping Score.phrases builds its phrases on rather than a second one that ignores where the music closes.
Optionalopts: HypermeterOptions
Analysis options; see HypermeterOptions.
The grouping, its downbeats, a confidence, and a rationale.
The motifs the melody repeats.
The plain analysis record comes back rather than a Motif, as it
does from every other reader here that is not Score.timeline or
Score.key: a Motif holds a bare cell of notes, so wrapping the
record would drop the intervals, the rhythmic profile, every later
occurrence and the rationale — the findings the extraction was asked for.
A record is a cell, so Motif.fromData(found) reaches the
transformations without anything being rebuilt by hand.
A melody is one line, so a score holding several is read as its top voice: notes struck together are one event and the highest of them stands for it. A piano part read whole therefore answers for the voice a listener follows rather than for a line assembled from the insides of its chords. Split the score into lines first to have each of them answered for.
Optionalopts: ExtractMotifsOptions
Cell-length bounds and the recurrence threshold; see ExtractMotifsOptions.
The motifs found, longest first.
import { Motif, Score } from '@libraz/libcantus';
const score = Score.of([
{ pitch: 60, startBeat: 0, durationBeat: 1 },
{ pitch: 62, startBeat: 1, durationBeat: 1 },
{ pitch: 64, startBeat: 2, durationBeat: 1 },
{ pitch: 60, startBeat: 3, durationBeat: 1 },
{ pitch: 62, startBeat: 4, durationBeat: 1 },
{ pitch: 64, startBeat: 5, durationBeat: 1 },
]);
const found = score.motifs()[0];
found?.occurrences.length; // 2
found === undefined ? undefined : Motif.fromData(found).notes.length; // 3
The shape the melody traces.
A contour is the shape of one line, so a score holding several is read as its top voice: notes struck together are one event and the highest of them stands for it, which keeps a chord from being measured as a climb through its own notes. Split the score into lines first to have each of them answered for; Score.voices is what reads the polyphony as polyphony.
Each note's role in the harmony sounding under it.
The chord under each note and the key it is read in both come from the score: the harmony from its own chord timeline, and the key from the argument, then the carried key, then the regions the analysis found. A score with no readable harmony — nothing sounding, or too little to name a chord — reads every note against C major and no chord at all, which labels the notes as the non-chord tones they are rather than failing.
A score holds a whole piece rather than a single line, so the notes are read as the polyphony they are: each note is classified in its own voice, against everything else sounding under it. That is what a suspension needs — a dissonance is dissonant against something — and it is what keeps a note of one voice from being heard as the passing tone of another.
One annotation per note, in the score's own time order.
The notes carrying the ids the voice analysis reports them back under.
Score.voices answers about the notes; this hands back the notes themselves with the handle each answer names, so a caller pairing an annotation or a conflict with the note it describes has something to pair it with. The id is the note's position in the score's own time order, which is not necessarily the order the notes arrived in.
One voice note per note, in the score's own time order.
Whether an instrument can play the score, and what makes it hard.
The third layer asks how much time separates two notes, and the score answers it from its whole tempo map: a passage under an accelerando is judged at the tempo actually in force across it, the same integral Score.secondsAt reports. The beats the issues name are the score's own.
The instrument to play it on: a plain InstrumentProfile or an Instrument.
Difficulty, the issues found, and each note's placement.
An index for repeated lookups over the notes.
Optionalopts: NoteEventIndexOptions
How the events are read; see NoteEventIndexOptions.
The index.
Where a beat falls in seconds, read through the tempo map.
Every tempo segment the span crosses is integrated, so a beat after a tempo change is not the change's tempo applied to the whole span. A beat inside a pickup reports negative elapsed time, at the opening tempo.
Position in quarter-note beats.
Elapsed seconds from the tempo map's origin.
Where a beat falls in bars, read through the meter map.
Position in quarter-note beats.
The 0-based bar and the quarter-note offset inside it; a pickup reports bar -1.
The time signature sounding at a beat.
Position in quarter-note beats.
The signature in force there.
The score's beats as MIDI ticks, for a writer that counts in them.
Ticks per quarter note.
The notes with tick-valued onsets and durations, in time order.
Whether another score holds the same notes in the same context.
The comparison is made through the other score's public data, so two scores built by different copies of the module still compare.
The score to compare.
True when the notes, the meter, the tempo and the read key match.
The plain form of the score, for JSON.stringify.
Sounding notes with the context that gives them meaning: a meter map, a tempo map, and optionally the key they are read in.
This is the library's entry point for music that already exists. A DAW plug-in, a transcription tool, or a MIDI importer holds
NoteEvent[]before it holds anything else, and every question worth asking of it — what chords are these, where do the phrases end, what key is this — took a separate function and a hand-wired chain of intermediate results. AScorecarries the notes and their context together, so each question is one call and the answers stay in the class API.The notes are held in time order, and their onsets are unbounded below: beat 0 is the first downbeat, so a pickup sounds at a negative beat and is kept there rather than being shifted onto the grid.
Example