Skip to content

resolve_note_ids_for_highlight

Resolve analysis locations or spans to MEI note IDs for notation highlighting.

Overview

This tool separates score-highlighting ID lookup from melodic n-gram extraction. Use it whenever another tool returns a location you want to highlight, including melodic n-grams, harmonic interval rows, cadence rows, and visualisation occurrences.

Parameters

Parameter Type Required Description
filename str Yes Name of the MEI file
spans list[dict] Yes Analysis locations or ranges to resolve

Each span may include:

Field Type Description
start_measure float Measure number from CRIM output
start_beat float Beat within the measure
start_q / start_offset float Quarter-note offset from the start of the piece
staff / part / column str Optional CRIM part label, such as "1"
voice_pair str Optional pair such as "1,2" from harmonic output
duration float Optional duration from the start location
end_q / end_offset float Optional exclusive end offset
note_count int Optional number of consecutive note events to collect

Returns

{
    "filename": "Bach_BWV_0772.mei",
    "spans": [
        {
            "index": 0,
            "matched_parts": ["1"],
            "note_ids": ["nz7y0rb", "n1ecjh8t"],
            "start_measure": 1.0,
            "start_beat": 1.25,
            "column": "1",
        }
    ],
}

The original fields from each requested span are preserved, and the resolver adds index, matched_parts, and note_ids.

How It Works

Note events are timed via CRIM Intervals' own music21 machinery (numberParts + detailIndex over _getM21ObjsNoTies()) — the same (measure, beat, offset) coordinate system every other interval tool produces. Each span is matched against this table rather than against an independently-computed timeline, so results always line up with the locations returned by get_melodic_ngrams, get_harmonic_intervals, get_cadences, and the visualisation tools.

For each matched music21 note or chord, note_ids are read from .id, which music21's MEI importer sets to the real xml:id when one is present. Rests contribute no IDs. A chord contributes the IDs of all of its constituent notes, not just one.

Known limitation: CRIM Intervals' own _getPartSeries() collapses a chord to its highest note before this resolver ever sees it, which detaches the other pitches from their position in the score. Those notes are dropped from every CRIM-based tool, not just highlighting — see Note-ID Resolution Review and Fix for details.

Examples

Resolve a cadence or harmonic interval row:

resolve_note_ids_for_highlight(
    "Morley_1595_01_Go_ye_my_canzonettes.mei",
    [{"start_measure": 12.0, "start_beat": 1.0, "voice_pair": "1,2"}],
)

Resolve a melodic n-gram span:

resolve_note_ids_for_highlight(
    "Bach_BWV_0772.mei",
    [
        {
            "column": "1",
            "start_measure": 1.0,
            "start_beat": 1.25,
            "start_offset": 0.25,
            "note_count": 5,
        }
    ],
)

Resolve a visualisation occurrence with an offset range:

resolve_note_ids_for_highlight(
    "Bach_BWV_0772.mei",
    [{"staff": "1", "start_q": 0.0, "end_q": 2.0}],
)