import { S as Score, f as Part, M as Measure, N as NoteEntry, V as VoiceGroup, q as StaffGroup, r as NoteWithPosition, s as Chord, t as NoteIteratorItem, h as MeasureEntry, x as VoiceToStaffMap, C as Clef, G as StaffRange, H as PositionQueryOptions, I as VerticalSlice, J as VoiceLine, E as EntryWithContext, y as NoteWithContext, O as AdjacentNotes, z as DirectionWithContext, a as DirectionEntry, Q as DirectionKind, R as DynamicWithContext, U as TempoWithContext, W as PedalWithContext, X as WedgeWithContext, Y as OctaveShiftWithContext, Z as TiedNoteGroup, _ as SlurSpan, $ as TupletGroup, a0 as BeamGroup, a1 as NotationType, a2 as HarmonyWithContext, ae as HarmonyEntry, a3 as LyricWithContext, a4 as AssembledLyrics, a6 as RepeatInfo, a5 as BarlineWithContext, a7 as EndingInfo, a8 as KeyChangeInfo, a9 as TimeChangeInfo, aa as ClefChangeInfo, ab as StructuralChanges, g as MeasureAttributes, P as Pitch } from '../types-CkeI8vw6.js'; /** * Playback Timeline * * Where {@link generatePlaybackSequence} gives the ORDER measures are played * (with repeats/voltas/jumps expanded), this gives the same expanded playback * WITH absolute times: a mapping between elapsed seconds and conceptual musical * position (measure + beat). * * This is a pure analysis of the score's playback interpretation (tempo + * repeat expansion). It is the one thing a downstream audio-to-score aligner * integration cannot recompute from the MusicXML alone — everything else about * the score (pitches, parts, voices, durations) the consumer already has and * can derive from a musical position. The seconds here are tempo-derived and * therefore equal to the playback time of the MIDI produced by `exportMidi`, * which shares this exact timeline computation. */ /** Options controlling the rendered timeline. */ interface TimingMapOptions { /** * Ticks per quarter note (default: 480). Only affects internal tick rounding; * `midiSec`/`quarterPos` are otherwise independent of it. */ ticksPerQuarterNote?: number; /** Default tempo in BPM when the score carries no tempo marking (default: 120). */ defaultTempo?: number; } /** A single point on the timeline: a time ↔ musical-position correspondence. */ interface TimingBreakpoint { /** Elapsed playback time, in seconds (equals the MIDI time of `exportMidi`). */ midiSec: number; /** * Cumulative quarter notes from the start of (repeat-expanded) playback. * This is the monotone axis to interpolate against: within a constant-tempo * segment `midiSec` is linear in `quarterPos`. */ quarterPos: number; /** Printed measure number (MusicXML ``), e.g. "12". */ measureNumber: string; /** Quarter-note offset within the measure (0 at the measure start). */ beatInMeasure: number; /** * Which repeat iteration produced this measure (1-based; 0 = not in a * repeat). The same measure appears multiple times when repeated. */ repeatIteration: number; } /** * Maps the playback timeline to conceptual musical positions. * * Pair it with an audio aligner that returns `audioSec ↔ midiSec` to follow a * recording on the score: * `audioSec → (aligner) → midiSec → (this, interpolate quarterPos) → (measure, beat)`. */ interface TimingSidecar { /** Sidecar schema version. */ version: string; /** Total duration of the playback, in seconds. */ durationSec: number; /** Ticks per quarter note used internally. */ ticksPerQuarterNote: number; /** Breakpoints, sorted ascending and monotone by `midiSec`. */ breakpoints: TimingBreakpoint[]; } /** * Generate the playback timeline (MIDI seconds ↔ conceptual musical position) * for a score, with repeats/voltas/jumps expanded. * * @param score - The score to analyze * @param options - Tempo/resolution options (use the same values as any MIDI * export the timeline is paired with) * @returns The timing sidecar */ declare function generatePlaybackTimeline(score: Score, options?: TimingMapOptions): TimingSidecar; /** * Playback Sequence Generator * * Analyzes repeat structures, voltas (endings), and jump instructions * (D.C., D.S., Coda, Segno, Fine, To Coda) in a score to produce the * correct playback order of measures. * * This is the structural expansion that turns the written score into the * order a performer would actually play it. The MIDI exporter consumes * this so that repeats, 1st/2nd endings, and da capo / dal segno jumps are * reflected in the rendered audio. * * The algorithm is ported from the validated playback engine in the * Octava project and adapted to the typed musicxml-io Score model. */ /** * Volta (ending bracket) information */ interface VoltaInfo { measureIndex: number; numbers: number[]; type: 'start' | 'stop' | 'discontinue'; } /** * Jump instruction information */ interface JumpInfo { measureIndex: number; type: 'dc' | 'dc_al_fine' | 'dc_al_coda' | 'ds' | 'ds_al_fine' | 'ds_al_coda'; } /** * Parsed playback control structures from a score */ interface PlaybackControls { /** measure indices that carry a forward repeat (||:) */ repeatStarts: number[]; /** backward repeats (:||) with their repeat counts */ repeatEnds: { measureIndex: number; times: number; }[]; /** volta brackets in document order */ voltas: VoltaInfo[]; /** jump instructions (D.C., D.S., and their al fine/al coda variants) */ jumps: JumpInfo[]; /** measure index of the segno symbol */ segnoIndex: number | null; /** measure index of the coda symbol */ codaIndex: number | null; /** measure index of the Fine marking */ fineIndex: number | null; /** measure index of the "To Coda" marking */ toCodaIndex: number | null; } /** * A measure to play, with the repeat-iteration context it was reached in. */ interface PlaybackMeasure { /** Index of the measure in the original (written) score */ measureIndex: number; /** Which repeat iteration produced this entry (1-based; 0 = not in a repeat) */ repeatIteration: number; } /** * Extract repeat / volta / jump control structures from the structural part. * * Repeats and voltas apply to the whole system, so by convention we scan a * single part (the first by default). Jump instructions and segno/coda/fine * markers are read from both entries (words text + segno/coda * symbols) and standalone entries. */ declare function extractPlaybackControls(part: Part): PlaybackControls; /** * Generate the playback sequence: the list of measures in the order they * should be performed, with repeats expanded and voltas / jumps resolved. * * @param score - the score to analyze * @param options.partIndex - which part to read structure from (default: 0) */ declare function generatePlaybackSequence(score: Score, options?: { partIndex?: number; }): PlaybackMeasure[]; /** * Whether a score contains any repeat / volta / jump structures that would * make its playback order differ from its written order. */ declare function hasPlaybackControls(score: Score, options?: { partIndex?: number; }): boolean; interface VoiceFilter { voice?: string; staff?: number; } /** * Get all notes for a specific voice (and optionally staff) */ declare function getNotesForVoice(measure: Measure, filter: VoiceFilter): NoteEntry[]; /** * Get all notes for a specific staff (regardless of voice) */ declare function getNotesForStaff(measure: Measure, filter: { staff: number; }): NoteEntry[]; /** * Group notes by voice (and staff) */ declare function groupByVoice(measure: Measure): VoiceGroup[]; /** * Group notes by staff */ declare function groupByStaff(measure: Measure): StaffGroup[]; /** * Calculate absolute position of a note within a measure * Position is in divisions from the start of the measure */ declare function getAbsolutePosition(note: NoteEntry, measure: Measure): number; /** * Add absolute position to all notes in a measure */ declare function withAbsolutePositions(measure: Measure): NoteWithPosition[]; /** * Get chords (groups of simultaneously sounding notes) */ declare function getChords(measure: Measure, filter?: VoiceFilter): Chord[]; /** * Iterate over all notes in a score */ declare function iterateNotes(score: Score): Generator; /** * Get all notes from a score as an array */ declare function getAllNotes(score: Score): NoteIteratorItem[]; /** * Get unique voices used in a measure */ declare function getVoices(measure: Measure): string[]; /** * Get unique staves used in a measure */ declare function getStaves(measure: Measure): number[]; /** * Check if a measure contains any notes */ declare function hasNotes(measure: Measure): boolean; /** * Check if a measure is a rest (no pitched notes) */ declare function isRestMeasure(measure: Measure): boolean; /** * Options for normalized position calculation */ interface NormalizedPositionOptions { baseDivisions: number; currentDivisions?: number; } /** * Get a normalized position of a note using a common base divisions * This is useful when comparing positions across measures with different divisions */ declare function getNormalizedPosition(note: NoteEntry, measure: Measure, options: NormalizedPositionOptions): number; /** * Get normalized duration of a note using a common base divisions */ declare function getNormalizedDuration(note: NoteEntry, options: NormalizedPositionOptions): number; /** * Get all entries for a specific staff (including notes, directions, etc.) */ declare function getEntriesForStaff(measure: Measure, staff: number): MeasureEntry[]; /** * Build a Voice to Staff mapping from a measure * Uses explicitly specified staff values to infer staff for voices */ declare function buildVoiceToStaffMap(measure: Measure): VoiceToStaffMap; /** * Build a Voice to Staff mapping from all measures in a part */ declare function buildVoiceToStaffMapForPart(part: Part): VoiceToStaffMap; /** * Infer staff number for an entry using voice-to-staff mapping * Returns 1 as default if unable to infer (per MusicXML spec) */ declare function inferStaff(entry: NoteEntry, voiceToStaffMap: VoiceToStaffMap): number; /** * Get effective staff for an entry (explicit or inferred) */ declare function getEffectiveStaff(entry: NoteEntry, measure: Measure): number; /** * Get the clef for a specific staff at a given measure * Searches backwards from the specified measure to find the most recent clef */ declare function getClefForStaff(score: Score, options: { partIndex: number; measureIndex: number; staff: number; }): Clef | undefined; /** * Get all voices used within a specific staff */ declare function getVoicesForStaff(measure: Measure, staff: number): string[]; /** * Get the range of staff numbers used in a part */ declare function getStaffRange(score: Score, partIndex: number): StaffRange; /** * Get all entries at a specific position in a measure */ declare function getEntriesAtPosition(measure: Measure, position: number, options?: PositionQueryOptions): MeasureEntry[]; /** * Get all notes at a specific position in a measure */ declare function getNotesAtPosition(measure: Measure, position: number, options?: PositionQueryOptions): NoteEntry[]; /** * Get all entries within a position range */ declare function getEntriesInRange(measure: Measure, range: { start: number; end: number; }, options?: PositionQueryOptions): MeasureEntry[]; /** * Get all notes within a position range */ declare function getNotesInRange(measure: Measure, range: { start: number; end: number; }, options?: PositionQueryOptions): NoteEntry[]; /** * Get a vertical slice of all notes at a specific position across all parts */ declare function getVerticalSlice(score: Score, options: { measureIndex: number; position: number; }): VerticalSlice; /** * Get a continuous voice line across all measures */ declare function getVoiceLine(score: Score, options: { partIndex: number; voice: string; staff?: number; }): VoiceLine; /** * Get a voice line within a measure range */ declare function getVoiceLineInRange(score: Score, options: { partIndex: number; voice: string; startMeasure: number; endMeasure: number; staff?: number; }): VoiceLine; /** * Iterate over all entries in a score (not just notes) */ declare function iterateEntries(score: Score): Generator; /** * Get the next note in the same voice */ declare function getNextNote(score: Score, context: NoteWithContext): NoteWithContext | null; /** * Get the previous note in the same voice */ declare function getPrevNote(score: Score, context: NoteWithContext): NoteWithContext | null; /** * Get both previous and next notes */ declare function getAdjacentNotes(score: Score, context: NoteWithContext): AdjacentNotes; /** * Get all directions from a score or specific part/measure */ declare function getDirections(score: Score, options?: { partIndex?: number; measureIndex?: number; }): DirectionWithContext[]; /** * Get directions at a specific position in a measure */ declare function getDirectionsAtPosition(measure: Measure, position: number): DirectionEntry[]; /** * Find directions by type (kind) */ declare function findDirectionsByType(score: Score, kind: DirectionKind): DirectionWithContext[]; /** * Get all dynamics markings from a score */ declare function getDynamics(score: Score, options?: { partIndex?: number; }): DynamicWithContext[]; /** * Get all tempo markings from a score */ declare function getTempoMarkings(score: Score): TempoWithContext[]; /** * Get all pedal markings from a score */ declare function getPedalMarkings(score: Score, options?: { partIndex?: number; }): PedalWithContext[]; /** * Get all wedges (crescendo/diminuendo) from a score */ declare function getWedges(score: Score, options?: { partIndex?: number; }): WedgeWithContext[]; /** * Get all octave shifts from a score */ declare function getOctaveShifts(score: Score, options?: { partIndex?: number; }): OctaveShiftWithContext[]; /** * Get all groups of tied notes in a score * Each group represents notes connected by ties */ declare function getTiedNoteGroups(score: Score, options?: { partIndex?: number; }): TiedNoteGroup[]; /** * Get all slur spans in a score * Each span represents a slur from start to stop */ declare function getSlurSpans(score: Score, options?: { partIndex?: number; }): SlurSpan[]; /** * Get all tuplet groups in a score */ declare function getTupletGroups(score: Score, options?: { partIndex?: number; }): TupletGroup[]; /** * Get all beam groups in a measure * Returns groups of notes connected by beams */ declare function getBeamGroups(measure: Measure): BeamGroup[]; /** * Find all notes with a specific notation type */ declare function findNotesWithNotation(score: Score, notationType: NotationType, options?: { partIndex?: number; }): NoteWithContext[]; /** * Get all harmonies from a score */ declare function getHarmonies(score: Score, options?: { partIndex?: number; }): HarmonyWithContext[]; /** * Get harmony at a specific position in a measure */ declare function getHarmonyAtPosition(measure: Measure, position: number): HarmonyEntry | undefined; /** * Get chord progression (all harmonies in order) * Returns a simplified representation of the chord progression */ declare function getChordProgression(score: Score, options?: { partIndex?: number; }): { root: string; kind: string; bass?: string; measureIndex: number; position: number; }[]; /** * Get all lyrics from a score */ declare function getLyrics(score: Score, options?: { partIndex?: number; verse?: number; }): LyricWithContext[]; /** * Get assembled lyric text for a specific verse * Joins syllables with proper hyphenation */ declare function getLyricText(score: Score, options?: { partIndex?: number; verse?: number; }): AssembledLyrics[]; /** * Get the number of verses in a score */ declare function getVerseCount(score: Score, options?: { partIndex?: number; }): number; /** * Get repeat structure from a score * Returns forward and backward repeat markers */ declare function getRepeatStructure(score: Score, options?: { partIndex?: number; }): RepeatInfo[]; /** * Find barlines matching criteria */ declare function findBarlines(score: Score, options?: { partIndex?: number; style?: string; repeat?: boolean; }): BarlineWithContext[]; /** * Get all endings (volta brackets) from a score */ declare function getEndings(score: Score, options?: { partIndex?: number; }): EndingInfo[]; /** * Get all key signature changes in a score */ declare function getKeyChanges(score: Score, options?: { partIndex?: number; }): KeyChangeInfo[]; /** * Get all time signature changes in a score */ declare function getTimeChanges(score: Score, options?: { partIndex?: number; }): TimeChangeInfo[]; /** * Get all clef changes in a score */ declare function getClefChanges(score: Score, options?: { partIndex?: number; staff?: number; }): ClefChangeInfo[]; /** * Get all structural changes in a score (key, time, clef) */ declare function getStructuralChanges(score: Score, options?: { partIndex?: number; }): StructuralChanges; /** * Get a part by index */ declare function getPartByIndex(score: Score, index: number): Part | undefined; /** * Get the number of parts in a score */ declare function getPartCount(score: Score): number; /** * Get all part IDs from a score */ declare function getPartIds(score: Score): string[]; /** * Get a specific measure from the score */ declare function getMeasure(score: Score, options: { part: number; measure: string | number; }): Measure | undefined; /** * Get measure by index */ declare function getMeasureByIndex(score: Score, options: { part: number; measureIndex: number; }): Measure | undefined; /** * Get the total number of measures in a score */ declare function getMeasureCount(score: Score): number; /** * Get the divisions value at a specific measure * Searches backwards from the specified measure to find the most recent divisions */ declare function getDivisions(score: Score, options: { part: number; measure: string | number; }): number; /** * Get the current attributes at a specific measure * Merges all attribute changes from measure 1 to the specified measure */ declare function getAttributesAtMeasure(score: Score, options: { part: number; measure: string | number; }): MeasureAttributes; /** * Pitch range filter */ interface PitchRange { min?: Pitch; max?: Pitch; } /** * Find notes filter */ interface FindNotesFilter { pitchRange?: PitchRange; voice?: string; staff?: number; noteType?: string; hasTie?: boolean; } /** * Find notes matching specific criteria */ declare function findNotes(score: Score, filter: FindNotesFilter): NoteEntry[]; /** * Get the total duration of the score in divisions */ declare function getDuration(score: Score): number; /** * Get part by ID */ declare function getPartById(score: Score, id: string): Part | undefined; /** * Get part index by ID */ declare function getPartIndex(score: Score, id: string): number; /** * Check if the score has multiple staves (e.g., piano grand staff) */ declare function hasMultipleStaves(score: Score, partIndex?: number): boolean; /** * Get the number of staves for a part */ declare function getStaveCount(score: Score, partIndex?: number): number; /** * Round-trip metrics for measuring preservation fidelity */ interface RoundtripMetrics { notesOriginal: number; notesPreserved: number; measuresOriginal: number; measuresPreserved: number; partsOriginal: number; partsPreserved: number; preservationRate: number; } /** * Compare two scores and calculate round-trip preservation metrics */ declare function measureRoundtrip(original: Score, exported: Score): RoundtripMetrics; /** * Count total notes in a score */ declare function countNotes(score: Score): number; /** * Compare two scores for structural equality */ declare function scoresEqual(a: Score, b: Score): boolean; export { type FindNotesFilter, type NormalizedPositionOptions, type PitchRange, type PlaybackControls, type PlaybackMeasure, type RoundtripMetrics, type TimingBreakpoint, type TimingMapOptions, type TimingSidecar, type VoiceFilter, buildVoiceToStaffMap, buildVoiceToStaffMapForPart, countNotes, extractPlaybackControls, findBarlines, findDirectionsByType, findNotes, findNotesWithNotation, generatePlaybackSequence, generatePlaybackTimeline, getAbsolutePosition, getAdjacentNotes, getAllNotes, getAttributesAtMeasure, getBeamGroups, getChordProgression, getChords, getClefChanges, getClefForStaff, getDirections, getDirectionsAtPosition, getDivisions, getDuration, getDynamics, getEffectiveStaff, getEndings, getEntriesAtPosition, getEntriesForStaff, getEntriesInRange, getHarmonies, getHarmonyAtPosition, getKeyChanges, getLyricText, getLyrics, getMeasure, getMeasureByIndex, getMeasureCount, getNextNote, getNormalizedDuration, getNormalizedPosition, getNotesAtPosition, getNotesForStaff, getNotesForVoice, getNotesInRange, getOctaveShifts, getPartById, getPartByIndex, getPartCount, getPartIds, getPartIndex, getPedalMarkings, getPrevNote, getRepeatStructure, getSlurSpans, getStaffRange, getStaveCount, getStaves, getStructuralChanges, getTempoMarkings, getTiedNoteGroups, getTimeChanges, getTupletGroups, getVerseCount, getVerticalSlice, getVoiceLine, getVoiceLineInRange, getVoices, getVoicesForStaff, getWedges, groupByStaff, groupByVoice, hasMultipleStaves, hasNotes, hasPlaybackControls, inferStaff, isRestMeasure, iterateEntries, iterateNotes, measureRoundtrip, scoresEqual, withAbsolutePositions };