@libraz/libcantus 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/analyze/index.cjs +7 -7
  2. package/dist/analyze/index.d.cts +8 -8
  3. package/dist/analyze/index.d.ts +8 -8
  4. package/dist/analyze/index.js +6 -6
  5. package/dist/{chunk-TT6PKMPR.cjs → chunk-35NBV2FT.cjs} +16 -16
  6. package/dist/{chunk-TT6PKMPR.cjs.map → chunk-35NBV2FT.cjs.map} +1 -1
  7. package/dist/{chunk-YPB3A3EY.cjs → chunk-3R3QVKY5.cjs} +314 -285
  8. package/dist/chunk-3R3QVKY5.cjs.map +1 -0
  9. package/dist/{chunk-RVS4G4MR.cjs → chunk-76ZBTRG2.cjs} +198 -198
  10. package/dist/{chunk-RVS4G4MR.cjs.map → chunk-76ZBTRG2.cjs.map} +1 -1
  11. package/dist/{chunk-X6RJAHII.js → chunk-7KG3RTYS.js} +2 -2
  12. package/dist/{chunk-PQL7E5F6.js → chunk-AGOVW7U6.js} +1 -1
  13. package/dist/{chunk-PQL7E5F6.js.map → chunk-AGOVW7U6.js.map} +1 -1
  14. package/dist/{chunk-J772DL3B.cjs → chunk-CGBADIO5.cjs} +53 -53
  15. package/dist/{chunk-J772DL3B.cjs.map → chunk-CGBADIO5.cjs.map} +1 -1
  16. package/dist/{chunk-GDKE6RQW.cjs → chunk-DSMJRXCZ.cjs} +402 -401
  17. package/dist/chunk-DSMJRXCZ.cjs.map +1 -0
  18. package/dist/{chunk-DKICR7AN.js → chunk-FBH75DAY.js} +13 -12
  19. package/dist/{chunk-DKICR7AN.js.map → chunk-FBH75DAY.js.map} +1 -1
  20. package/dist/{chunk-VLE5R7PF.js → chunk-H2KJHBW4.js} +4 -4
  21. package/dist/{chunk-XVMJO3QF.cjs → chunk-HPVSH7SF.cjs} +237 -215
  22. package/dist/chunk-HPVSH7SF.cjs.map +1 -0
  23. package/dist/{chunk-QCXYKSZS.cjs → chunk-HWHGOVO6.cjs} +19 -19
  24. package/dist/{chunk-QCXYKSZS.cjs.map → chunk-HWHGOVO6.cjs.map} +1 -1
  25. package/dist/{chunk-HSY2VQXZ.js → chunk-HYJJ2V2I.js} +32 -10
  26. package/dist/chunk-HYJJ2V2I.js.map +1 -0
  27. package/dist/{chunk-5KW7DNCJ.cjs → chunk-J7FHVZII.cjs} +445 -445
  28. package/dist/{chunk-5KW7DNCJ.cjs.map → chunk-J7FHVZII.cjs.map} +1 -1
  29. package/dist/{chunk-AWEMD747.js → chunk-M5RYMAFI.js} +3 -3
  30. package/dist/{chunk-H66ARFU5.js → chunk-NB25P3EH.js} +3 -3
  31. package/dist/{chunk-57WB2EDF.js → chunk-NFJ4P6ZZ.js} +3 -3
  32. package/dist/{chunk-5JJ6EHEZ.cjs → chunk-NM7UAMC3.cjs} +62 -62
  33. package/dist/{chunk-5JJ6EHEZ.cjs.map → chunk-NM7UAMC3.cjs.map} +1 -1
  34. package/dist/{chunk-R7B5N2JK.cjs → chunk-PM4XJE2Y.cjs} +47 -47
  35. package/dist/{chunk-R7B5N2JK.cjs.map → chunk-PM4XJE2Y.cjs.map} +1 -1
  36. package/dist/{chunk-7GOKQJTQ.js → chunk-PUKJP2Z5.js} +2 -2
  37. package/dist/{chunk-R5VUXQMP.js → chunk-Q6LFSAKV.js} +7 -7
  38. package/dist/{chunk-FPBZFNCD.js → chunk-RFIBF2RR.js} +47 -18
  39. package/dist/{chunk-FPBZFNCD.js.map → chunk-RFIBF2RR.js.map} +1 -1
  40. package/dist/{chunk-XKSO2V5I.cjs → chunk-S5BKZZGM.cjs} +1 -1
  41. package/dist/{chunk-XKSO2V5I.cjs.map → chunk-S5BKZZGM.cjs.map} +1 -1
  42. package/dist/{chunk-Q6R3QAZD.cjs → chunk-SGLEQVJH.cjs} +332 -332
  43. package/dist/{chunk-Q6R3QAZD.cjs.map → chunk-SGLEQVJH.cjs.map} +1 -1
  44. package/dist/{chunk-HOHFYGKW.js → chunk-UMWZ4ZET.js} +2 -2
  45. package/dist/{coerce-wwdELNYS.d.ts → coerce-DP0vYu-x.d.ts} +77 -1
  46. package/dist/{coerce-CJlgULrQ.d.cts → coerce-DRERzdve.d.cts} +77 -1
  47. package/dist/core/index.cjs +5 -5
  48. package/dist/core/index.d.cts +2 -2
  49. package/dist/core/index.d.ts +2 -2
  50. package/dist/core/index.js +4 -4
  51. package/dist/generate/index.cjs +8 -8
  52. package/dist/generate/index.d.cts +8 -8
  53. package/dist/generate/index.d.ts +8 -8
  54. package/dist/generate/index.js +7 -7
  55. package/dist/{index-BaoKVVhE.d.cts → index-B9QLziDe.d.cts} +2 -2
  56. package/dist/{index-B3WCj_Hm.d.cts → index-BBWNZS0f.d.cts} +2 -2
  57. package/dist/{index-CnHQgqNh.d.ts → index-BHIUyjhZ.d.ts} +2 -2
  58. package/dist/{index-DpC-mDlV.d.ts → index-BJgP97N1.d.ts} +4 -4
  59. package/dist/{index-CBCP84rv.d.cts → index-BL5N2Oyz.d.cts} +1 -1
  60. package/dist/{index-D_hzxD8w.d.cts → index-BMweUlm5.d.cts} +3 -0
  61. package/dist/{index-BzLNkmA7.d.cts → index-BNSM-DSk.d.cts} +4 -4
  62. package/dist/{index-BUtIf2go.d.ts → index-BRMbPkHE.d.ts} +25 -5
  63. package/dist/{index-BpWOhkkE.d.ts → index-CJBBiAmU.d.ts} +2 -2
  64. package/dist/{index-Ci3xyows.d.ts → index-CL4b4gUQ.d.ts} +3 -0
  65. package/dist/{index-DTsKaPaC.d.cts → index-DGCNKAUr.d.cts} +25 -5
  66. package/dist/{index-CnIdP4EZ.d.ts → index-Dw2DP7vm.d.ts} +1 -1
  67. package/dist/index.cjs +13 -13
  68. package/dist/index.d.cts +8 -8
  69. package/dist/index.d.ts +8 -8
  70. package/dist/index.js +12 -12
  71. package/dist/{masks-O73Rhj5d.d.ts → masks-Cc7-MzzY.d.ts} +1 -1
  72. package/dist/{masks-C8TWLFIi.d.cts → masks-pADfTOiD.d.cts} +1 -1
  73. package/dist/model/index.cjs +12 -12
  74. package/dist/model/index.d.cts +16 -15
  75. package/dist/model/index.d.ts +16 -15
  76. package/dist/model/index.js +11 -11
  77. package/dist/theory/index.cjs +5 -5
  78. package/dist/theory/index.d.cts +7 -7
  79. package/dist/theory/index.d.ts +7 -7
  80. package/dist/theory/index.js +4 -4
  81. package/package.json +6 -5
  82. package/dist/chunk-GDKE6RQW.cjs.map +0 -1
  83. package/dist/chunk-HSY2VQXZ.js.map +0 -1
  84. package/dist/chunk-XVMJO3QF.cjs.map +0 -1
  85. package/dist/chunk-YPB3A3EY.cjs.map +0 -1
  86. /package/dist/{chunk-X6RJAHII.js.map → chunk-7KG3RTYS.js.map} +0 -0
  87. /package/dist/{chunk-VLE5R7PF.js.map → chunk-H2KJHBW4.js.map} +0 -0
  88. /package/dist/{chunk-AWEMD747.js.map → chunk-M5RYMAFI.js.map} +0 -0
  89. /package/dist/{chunk-H66ARFU5.js.map → chunk-NB25P3EH.js.map} +0 -0
  90. /package/dist/{chunk-57WB2EDF.js.map → chunk-NFJ4P6ZZ.js.map} +0 -0
  91. /package/dist/{chunk-7GOKQJTQ.js.map → chunk-PUKJP2Z5.js.map} +0 -0
  92. /package/dist/{chunk-R5VUXQMP.js.map → chunk-Q6LFSAKV.js.map} +0 -0
  93. /package/dist/{chunk-HOHFYGKW.js.map → chunk-UMWZ4ZET.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/analyze/arrange/internal.ts","../src/analyze/arrange/tracks.ts","../src/analyze/arrange/session.ts","../src/analyze/arrange/tension.ts","../src/analyze/form/hypermeter.ts","../src/analyze/melody/internal.ts","../src/analyze/melody/contour.ts","../src/analyze/melody/motifs.ts","../src/analyze/melody/relation.ts","../src/analyze/melody/similarity.ts","../src/analyze/form/phrase.ts","../src/analyze/form/section.ts","../src/analyze/reduction/index.ts"],"sourcesContent":["/**\n * Shared helpers for the arrangement-analysis modules: pooling every track's\n * sounding notes and partitioning a track into monophonic sub-voices, plus the\n * small geometric primitives both the track-role and tension passes rely on.\n */\n\nimport { InvalidInputError } from '../../core/errors/index.js';\nimport type { NoteEvent } from '../../core/types.js';\nimport type { NoteEventAssertOptions } from '../../core/validation/index.js';\nimport {\n assertArray,\n assertFiniteNumber,\n assertNoteEvents,\n assertOneOf,\n describeRejected,\n} from '../../core/validation/index.js';\nimport { PROFILE_WEIGHTS, type SafetyProfile } from '../../theory/safety/index.js';\nimport { resolveKey } from '../../theory/scale/index.js';\nimport { adjacent, BEAT_EPS, hasEnded, sameInstant } from '../adjacency.js';\nimport type { KeyRegion } from '../keys/index.js';\nimport { assertChordTimeline, type ChordTimeline } from '../timeline/index.js';\nimport type { IdentifiedVoiceNote } from '../voice/index.js';\nimport type { ArrangementTrack, TrackRole } from './tracks.js';\n\n/**\n * Largest interval, in semitones, a sub-voice will span between consecutive\n * notes. Beyond two octaves the connection is not a melodic step or leap but an\n * artefact of lane packing, so a new sub-voice is opened instead.\n */\nconst MAX_LANE_LEAP = 24;\n\n/** A note with the pitch that immediately preceded it in the same sub-voice. */\nexport type PreparedNote = {\n /** The id the note carries in the analysis. */\n id: number;\n /** The note's index in its track's own note array. */\n originalIndex?: number;\n pitch: number;\n prevPitch?: number;\n startBeat: number;\n endBeat: number;\n};\n\n/** One monophonic sub-voice of a track: ordered notes plus sounding spans. */\nexport type PreparedVoice = {\n /** The sub-voice's notes in time order, each with a stable id. */\n voice: IdentifiedVoiceNote[];\n /** The same notes as sounding spans, each carrying its predecessor's pitch. */\n sounding: PreparedNote[];\n /**\n * The span sounding at a beat, or undefined. A sub-voice is monophonic by\n * construction, so at most one span covers any beat and the lookup is a\n * single binary search rather than a scan.\n */\n at: (beat: number) => PreparedNote | undefined;\n};\n\n/** A track prepared for analysis: resolved metadata plus monophonic sub-voices. */\nexport type PreparedTrack = {\n name: string;\n role: TrackRole;\n /** The track's index in the caller's input array. */\n trackIndex: number;\n /**\n * The track's notes partitioned into monophonic sub-voices. Voice analysis\n * assumes one note at a time, so a polyphonic track (e.g. a 'harmony' track\n * playing block chords) is split here; a monophonic track yields exactly one\n * sub-voice.\n */\n voices: PreparedVoice[];\n};\n\n/** Whether a sounding span `[start, end)` covers a beat. */\nexport function covers(note: PreparedNote, beat: number): boolean {\n return beat >= note.startBeat - BEAT_EPS && beat < note.endBeat - BEAT_EPS;\n}\n\n/** Whether a track's role means it carries no harmonic content. */\nexport function isPercussion(role: TrackRole | undefined): boolean {\n return role === 'drums';\n}\n\n/**\n * The roles that exist, written once as a table the compiler checks against\n * {@link TrackRole}: a role added to the union without a column here fails to\n * compile rather than becoming a name the check below quietly rejects.\n */\nconst TRACK_ROLE_TABLE: Readonly<Record<TrackRole, true>> = {\n melody: true,\n harmony: true,\n bass: true,\n drums: true,\n other: true,\n};\n\n/** The roles that exist, read off the table rather than listed again here. */\nconst TRACK_ROLES = Object.keys(TRACK_ROLE_TABLE) as TrackRole[];\n\n/**\n * The role a track plays, checked against the roles that exist.\n *\n * A name outside the table is an input error rather than a quiet fall back to\n * `'other'`: a drum track labelled `'percussion'` — the word a MIDI import\n * naturally writes — would be read as pitched material and pooled into the key\n * and the chords, and the role would be reported back as a value\n * {@link TrackRole} says cannot occur.\n *\n * @param role The track's role, if any.\n * @param name What the track is called in an error message.\n * @returns The role, or `'other'` when the track carries none.\n * @throws If the role names no known role.\n */\nexport function trackRoleOf(role: TrackRole | undefined, name: string): TrackRole {\n return role === undefined ? 'other' : assertOneOf(role, TRACK_ROLES, name);\n}\n\n/** The safety profile an arrangement is judged under when the caller names none. */\nconst DEFAULT_PROFILE: SafetyProfile = 'pop';\n\n/** The profiles that exist, read off the table rather than listed again here. */\nconst SAFETY_PROFILES = Object.keys(PROFILE_WEIGHTS) as SafetyProfile[];\n\n/**\n * The safety profile an arrangement is judged under, checked against the\n * profiles that exist.\n *\n * A name outside the table is an input error rather than a quiet fall back to\n * `pop`, which would read as a verdict about the music instead of as the\n * misspelling it is. Both entry points resolve it here so they cannot disagree\n * about which names are taken.\n *\n * @param profile The caller's profile, if any.\n * @returns The profile to judge under.\n * @throws If the profile names no known profile.\n */\nexport function arrangementProfile(profile: SafetyProfile | undefined): SafetyProfile {\n return assertOneOf(profile ?? DEFAULT_PROFILE, SAFETY_PROFILES, 'arrangement profile');\n}\n\n/**\n * Validate the note array of every track, naming the track that is wrong.\n *\n * A missing or non-array `notes` is rejected here rather than becoming a raw\n * `TypeError` further down, where the message no longer says which track. Every\n * entry point that takes arrangement tracks checks them this way, so the same\n * malformed input is refused with the same message whichever one is called.\n *\n * @param tracks The tracks as the caller passed them.\n * @param options The note-event assertion options this entry point works under.\n * @returns How many notes the tracks hold in total.\n * @throws If a track's `notes` is not an array, or holds a malformed event.\n */\nexport function assertTrackNotes(\n tracks: readonly ArrangementTrack[],\n options: NoteEventAssertOptions,\n): number {\n assertArray(tracks, 'arrangement tracks');\n let noteCount = 0;\n for (let index = 0; index < tracks.length; index += 1) {\n const notes = tracks[index]?.notes;\n if (!Array.isArray(notes)) {\n throw new InvalidInputError(\n `tracks[${index}].notes must be an array; received ${typeof notes}`,\n );\n }\n assertNoteEvents(notes, `tracks[${index}].notes`, options);\n noteCount += notes.length;\n }\n return noteCount;\n}\n\n/**\n * Reject a caller's chord timeline that is not one, when one was given at all.\n *\n * The option is what {@link assertChordTimeline} reads, with the difference\n * that leaving it out is how a caller asks for the timeline to be inferred.\n *\n * @param timeline The timeline as the caller passed it, or nothing.\n * @param name What the option is called in an error message.\n * @throws If the value is not a timeline of chord segments.\n */\nexport function assertGivenTimeline(timeline: ChordTimeline | undefined, name: string): void {\n if (timeline !== undefined) {\n assertChordTimeline(timeline, name);\n }\n}\n\n/**\n * Reject key regions that are not regions, or that name no key.\n *\n * The sibling of {@link assertGivenTimeline}, for the option that answers the\n * other half of the question: a region missing its `key` became a raw\n * `TypeError` inside the key reduction, several layers from the option it came\n * from. The key itself is resolved here rather than described, since resolving\n * it is what every reader of the region goes on to do.\n *\n * @param keys The regions as the caller passed them, or nothing.\n * @param name What the option is called in an error message.\n * @throws If the value is not an array of key regions.\n */\nexport function assertGivenKeys(keys: readonly KeyRegion[] | undefined, name: string): void {\n if (keys === undefined) {\n return;\n }\n if (!Array.isArray(keys)) {\n throw new InvalidInputError(\n `${name} must be an array of key regions; received ${describeRejected(keys)}`,\n );\n }\n for (let index = 0; index < keys.length; index += 1) {\n const region = keys[index];\n if (typeof region !== 'object' || region === null) {\n throw new InvalidInputError(\n `${name}[${index}] must be a key region; received ${describeRejected(region)}`,\n );\n }\n assertFiniteNumber(region.startBeat, `${name}[${index}].startBeat`);\n assertFiniteNumber(region.endBeat, `${name}[${index}].endBeat`);\n if (\n region.key === undefined ||\n region.key === null ||\n (typeof region.key !== 'string' && typeof region.key !== 'object')\n ) {\n throw new InvalidInputError(\n `${name}[${index}].key must name a key; received ${describeRejected(region.key)}`,\n );\n }\n resolveKey(region.key);\n }\n}\n\n/**\n * The tracks the harmony is inferred from, as a set of indices checked against\n * the tracks that exist.\n *\n * An index naming no track cannot be honoured, and matching nothing quietly\n * turns a stale index — the ordinary result of deleting a track — into an\n * analysis with no harmony at all, reported as though the arrangement were at\n * fault. A percussion track is refused for the same reason: {@link poolNotes}\n * drops it, so naming one is naming no harmony, and the analysis that comes back\n * blames the music for clashing with a harmony nothing states. The session's\n * `trackIndex` is checked the same way.\n *\n * @param harmonyTracks The caller's indices, if any.\n * @param tracks The tracks the indices are read against.\n * @param name What the indices belong to, for the error message.\n * @returns The indices as a set, or undefined when the caller named none.\n * @throws If an index is not finite, names no track, or names a track whose\n * notes carry no harmony.\n */\nexport function harmonyTrackSet(\n harmonyTracks: readonly number[] | undefined,\n tracks: readonly ArrangementTrack[],\n name = 'harmonyTracks',\n): ReadonlySet<number> | undefined {\n if (harmonyTracks === undefined) {\n return undefined;\n }\n const only = new Set<number>();\n for (let index = 0; index < harmonyTracks.length; index += 1) {\n const value = harmonyTracks[index];\n if (value === undefined || !Number.isFinite(value)) {\n throw new InvalidInputError(`${name}[${index}] must be a track index; received ${value}`);\n }\n const track = Math.trunc(value);\n if (track < 0 || track >= tracks.length) {\n throw new InvalidInputError(\n `${name}[${index}] ${track} is outside the arrangement's ${tracks.length} tracks`,\n );\n }\n if (isPercussion(tracks[track]?.role)) {\n throw new InvalidInputError(\n `${name}[${index}] ${track} names a percussion track, whose pitches state no harmony`,\n );\n }\n only.add(track);\n }\n return only;\n}\n\n/**\n * Every sounding note of every pitched track pooled into one flat list. Zero-\n * and negative-length notes never sound, so they are dropped here (matching the\n * chord-inference ingest in the timeline module), and percussion tracks are\n * skipped entirely: their pitches are instrument selections, not harmony.\n */\nexport function poolNotes(tracks: ArrangementTrack[], only?: ReadonlySet<number>): NoteEvent[] {\n const notes: NoteEvent[] = [];\n for (let index = 0; index < tracks.length; index += 1) {\n const track = tracks[index];\n if (track === undefined || isPercussion(track.role)) {\n continue;\n }\n if (only !== undefined && !only.has(index)) {\n continue;\n }\n for (const note of track.notes) {\n if (note.durationBeat > 0) {\n notes.push(note);\n }\n }\n }\n return notes;\n}\n\n/**\n * Split a track's ordered notes into monophonic sub-voices.\n *\n * Each note joins the free sub-voice — one whose last note has ended — whose\n * last pitch is nearest its own, so a track whose voices cross keeps each voice\n * on its own lane instead of handing a low note to whichever lane happened to\n * be created first. Ties go to the lane that ended latest, which is the one the\n * note actually follows. A connection wider than {@link MAX_LANE_LEAP} is not a\n * melodic one, so it opens a new sub-voice instead.\n *\n * The predecessor pitch this produces feeds leap and parallel detection, so a\n * mis-assigned lane invents voice-leading faults that are not in the music.\n */\nfunction splitIntoSubVoices(ordered: IdentifiedVoiceNote[]): IdentifiedVoiceNote[][] {\n const lanes: IdentifiedVoiceNote[][] = [];\n const attachNearest = (note: IdentifiedVoiceNote, excluded = new Set<number>()): number => {\n let best = -1;\n let bestDistance = Number.POSITIVE_INFINITY;\n let bestEnd = Number.NEGATIVE_INFINITY;\n for (let lane = 0; lane < lanes.length; lane += 1) {\n if (excluded.has(lane)) {\n continue;\n }\n const last = lanes[lane]?.[(lanes[lane]?.length ?? 0) - 1];\n if (last === undefined) {\n continue;\n }\n const end = last.startBeat + last.durationBeat;\n if (!hasEnded(end, note.startBeat)) {\n continue; // still sounding, so the lane is not free\n }\n const distance = Math.abs(last.pitch - note.pitch);\n if (distance > MAX_LANE_LEAP) {\n continue;\n }\n if (distance < bestDistance || (distance === bestDistance && end > bestEnd)) {\n best = lane;\n bestDistance = distance;\n bestEnd = end;\n }\n }\n if (best >= 0) {\n lanes[best]?.push(note);\n }\n return best;\n };\n\n for (let start = 0; start < ordered.length; ) {\n const onset = ordered[start]?.startBeat ?? 0;\n let end = start + 1;\n while (end < ordered.length && sameInstant(ordered[end]?.startBeat ?? 0, onset)) {\n end += 1;\n }\n const block = ordered.slice(start, end);\n // Notes struck together form a vertical slice, not a sequence of greedy\n // nearest-neighbour decisions, so their lanes are chosen as a whole.\n if (block.length > 1) {\n const freeLanes = lanes\n .map((lane, index) => ({ index, last: lane[lane.length - 1] }))\n .filter(\n (entry): entry is { index: number; last: IdentifiedVoiceNote } =>\n entry.last !== undefined &&\n hasEnded(entry.last.startBeat + entry.last.durationBeat, onset),\n )\n .sort((a, b) => a.last.pitch - b.last.pitch);\n const assignment = pairWithFreeLanes(block, freeLanes);\n // Every lane the slice takes is settled before a note is placed: a note\n // left for {@link attachNearest} must not be handed a lane the slice\n // already spoke for, and neither may two of them share one.\n const taken = new Set<number>(\n assignment.filter((lane): lane is number => lane !== undefined),\n );\n for (let index = 0; index < block.length; index += 1) {\n const note = block[index];\n if (note === undefined) {\n continue;\n }\n const lane = assignment[index];\n if (lane !== undefined) {\n lanes[lane]?.push(note);\n continue;\n }\n const attached = attachNearest(note, taken);\n if (attached < 0) {\n lanes.push([note]);\n } else {\n taken.add(attached);\n }\n }\n } else {\n const note = block[0];\n if (note !== undefined && attachNearest(note) < 0) {\n lanes.push([note]);\n }\n }\n start = end;\n }\n return lanes;\n}\n\n/**\n * Choose a free lane for each note of one simultaneous block.\n *\n * The whole slice is assigned at once, by the rule {@link splitIntoSubVoices}\n * follows note by note: each note joins the free lane whose last pitch is\n * nearest its own, with the block's low-to-high order preserved so no two\n * assignments cross. Pairing by position instead — the lowest note to the lowest\n * free lane — hands a thinned chord's notes to lanes they never sounded on: a\n * C-E-G that becomes E-G gives the E the C's lane and invents the leap that\n * follows from it.\n *\n * A connection wider than {@link MAX_LANE_LEAP} is not a melodic one, so leaving\n * a note for a lane of its own costs exactly that: it is taken whenever no\n * remaining lane is closer than the widest connection a lane admits.\n *\n * @param block The notes struck together, low to high.\n * @param freeLanes The lanes free at that onset, by their last pitch, low to high.\n * @returns One entry per block note: the lane it joins, or undefined when it\n * joins none of them.\n */\nfunction pairWithFreeLanes(\n block: readonly IdentifiedVoiceNote[],\n freeLanes: readonly { index: number; last: IdentifiedVoiceNote }[],\n): (number | undefined)[] {\n const noteCount = block.length;\n const laneCount = freeLanes.length;\n const assignment = new Array<number | undefined>(noteCount).fill(undefined);\n if (laneCount === 0) {\n return assignment;\n }\n // Cost of the cheapest order-preserving assignment of the first i notes over\n // the first j lanes, and the step it ends with: 0 leaves lane j-1 unused, 1\n // pairs note i-1 with it, 2 leaves note i-1 without a lane.\n const width = laneCount + 1;\n const cost = new Float64Array((noteCount + 1) * width);\n const step = new Uint8Array((noteCount + 1) * width);\n for (let i = 1; i <= noteCount; i += 1) {\n cost[i * width] = i * MAX_LANE_LEAP;\n step[i * width] = 2;\n }\n for (let i = 1; i <= noteCount; i += 1) {\n for (let j = 1; j <= laneCount; j += 1) {\n let best = cost[i * width + j - 1] ?? 0;\n let choice = 0;\n const unpaired = (cost[(i - 1) * width + j] ?? 0) + MAX_LANE_LEAP;\n if (unpaired < best) {\n best = unpaired;\n choice = 2;\n }\n const note = block[i - 1];\n const lane = freeLanes[j - 1];\n if (note !== undefined && lane !== undefined) {\n const distance = Math.abs(lane.last.pitch - note.pitch);\n const paired = (cost[(i - 1) * width + j - 1] ?? 0) + distance;\n if (distance <= MAX_LANE_LEAP && paired < best) {\n best = paired;\n choice = 1;\n }\n }\n cost[i * width + j] = best;\n step[i * width + j] = choice;\n }\n }\n for (let i = noteCount, j = laneCount; i > 0; ) {\n if (j === 0 || step[i * width + j] === 2) {\n i -= 1;\n } else if (step[i * width + j] === 1) {\n assignment[i - 1] = freeLanes[j - 1]?.index;\n i -= 1;\n j -= 1;\n } else {\n j -= 1;\n }\n }\n return assignment;\n}\n\n/** Index of the last span starting at or before a beat, or -1. */\nfunction lastStartingAtOrBefore(sounding: PreparedNote[], beat: number): number {\n let low = 0;\n let high = sounding.length;\n while (low < high) {\n const middle = Math.floor((low + high) / 2);\n if ((sounding[middle]?.startBeat ?? Number.POSITIVE_INFINITY) <= beat + BEAT_EPS) {\n low = middle + 1;\n } else {\n high = middle;\n }\n }\n return low - 1;\n}\n\n/**\n * Resolve each track's metadata and derive its monophonic sub-voices.\n *\n * Notes are sorted by onset, then pitch, so ordering is deterministic, and ids\n * are assigned in that order; each note keeps the index it had in the caller's\n * array so annotations can be mapped back. Zero- and negative-length notes never\n * sound and are dropped at ingest. Each track is then partitioned into\n * monophonic sub-voices (see {@link splitIntoSubVoices}); the predecessor pitch\n * used for suspension and parallel detection is the previous note of the same\n * sub-voice, never a simultaneous chord member.\n */\nexport function prepareTracks(tracks: ArrangementTrack[]): PreparedTrack[] {\n let nextId = 0;\n return tracks.map((track, index) => {\n const ordered: IdentifiedVoiceNote[] = track.notes\n .map((note, originalIndex) => ({ note, originalIndex }))\n .filter(({ note }) => note.durationBeat > 0)\n .sort((a, b) => a.note.startBeat - b.note.startBeat || a.note.pitch - b.note.pitch)\n .map(({ note, originalIndex }) => ({ ...note, id: nextId++, originalIndex }));\n const voices: PreparedVoice[] = splitIntoSubVoices(ordered).map((voice) => {\n const sounding: PreparedNote[] = voice.map((note, i) => {\n const prev = i > 0 ? voice[i - 1] : undefined;\n const prevEnd = prev === undefined ? undefined : prev.startBeat + prev.durationBeat;\n const contiguous = prevEnd !== undefined && adjacent(prevEnd, note.startBeat);\n return {\n id: note.id,\n originalIndex: note.originalIndex,\n pitch: note.pitch,\n prevPitch: contiguous ? prev?.pitch : undefined,\n startBeat: note.startBeat,\n endBeat: note.startBeat + note.durationBeat,\n };\n });\n return {\n voice,\n sounding,\n at: (beat: number) => {\n const candidate = sounding[lastStartingAtOrBefore(sounding, beat)];\n return candidate !== undefined && covers(candidate, beat) ? candidate : undefined;\n },\n };\n });\n return {\n name: track.name ?? `track ${index + 1}`,\n role: trackRoleOf(track.role, `tracks[${index}].role`),\n trackIndex: index,\n voices,\n };\n });\n}\n","/**\n * Arrangement track-role analysis: a single harmony is inferred from every\n * track's notes pooled together, each track is then analysed against that shared\n * harmony, and notes that clash with the sounding chord are collected as\n * conflicts.\n */\n\nimport type { MeterLike, MeterMap } from '../../core/meter/index.js';\nimport { beatsPerBarAt, isStrongBeat, resolveMeters } from '../../core/meter/index.js';\nimport type { NoteEvent } from '../../core/types.js';\nimport type { NoteEventAssertOptions } from '../../core/validation/index.js';\nimport {\n assertArray,\n assertFunction,\n assertGenerationBudget,\n assertInteger,\n assertNoteEvents,\n assertRange,\n} from '../../core/validation/index.js';\nimport type { Chord } from '../../theory/chord/index.js';\nimport {\n evaluateSafety,\n NoteSafety,\n type SafetyProfile,\n type VoiceSnapshot,\n} from '../../theory/safety/index.js';\nimport type { KeyLike } from '../../theory/scale/index.js';\nimport { majorKey, type ResolvedKey, resolveKey, scaleOf } from '../../theory/scale/index.js';\nimport { BEAT_EPS, sameInstant } from '../adjacency.js';\nimport { gridForNotes } from '../grid.js';\nimport {\n attachPivots,\n type KeyRegion,\n keyLookup,\n keyTimelineFromNotes,\n prevailingKeyOf,\n} from '../keys/index.js';\nimport {\n analyzeTimeline,\n type CadenceHit,\n type ChordTimeline,\n type ChordTimelineResult,\n type DirtySlots,\n detectCadences,\n type TimelineEvidence,\n} from '../timeline/index.js';\nimport {\n type AnalyzedNote,\n analyzeVoice,\n type KeyContext,\n type TheoryLabel,\n type VoiceNote,\n} from '../voice/index.js';\nimport {\n arrangementProfile,\n assertGivenKeys,\n assertGivenTimeline,\n assertTrackNotes,\n harmonyTrackSet,\n isPercussion,\n type PreparedNote,\n type PreparedTrack,\n poolNotes,\n prepareTracks,\n} from './internal.js';\n\n/**\n * The musical role a track plays in the arrangement.\n *\n * Only `drums` changes the analysis: its pitches select instruments rather than\n * naming harmony, so such a track is excluded from chord and key inference and\n * from every voice-leading comparison, and is reported back with no\n * annotations. `harmony` is meaningful through\n * {@link ArrangementOptions.harmonyTracks}, which names the tracks the chords\n * are inferred from. The rest are labels carried through to\n * {@link TrackAnalysis.role} for the caller's own use; nothing is inferred, so\n * a track left unlabelled reports `'other'` rather than a guess.\n *\n * @category Arrangement & Analysis\n */\nexport type TrackRole = 'melody' | 'harmony' | 'bass' | 'drums' | 'other';\n\n/**\n * One input track: its notes plus optional name and role.\n *\n * @category Arrangement & Analysis\n */\nexport type ArrangementTrack = {\n name?: string;\n role?: TrackRole;\n notes: readonly NoteEvent[];\n};\n\n/**\n * A track after analysis: its resolved name, role, and per-note annotations.\n *\n * @category Arrangement & Analysis\n */\nexport type TrackAnalysis = {\n name: string;\n role: TrackRole;\n /** One {@link AnalyzedNote} per note, labelled against the inferred chords. */\n notes: AnalyzedNote[];\n};\n\n/**\n * A note sounding with a {@link Conflict} that raises a pairwise flag against it.\n *\n * @category Arrangement & Analysis\n */\nexport type ConflictPartner = {\n /** The partner's track index in the caller's input array. */\n trackIndex: number;\n /** The partner note's index in that track's own note array. */\n originalIndex?: number;\n /** The id the partner note carries in {@link TrackAnalysis.notes}. */\n noteId: number;\n /** The pairwise flags this note raises, as {@link SafetyPartner.reasons}. */\n reasons: number;\n};\n\n/**\n * A note that clashes with the harmony sounding beneath it.\n *\n * @category Arrangement & Analysis\n */\nexport type Conflict = {\n beat: number;\n trackName: string;\n /** The clashing track's index in the caller's input array. */\n trackIndex: number;\n /** The note's index in that track's own note array. */\n originalIndex?: number;\n /** The id the note carries in {@link TrackAnalysis.notes}. */\n noteId: number;\n pitch: number;\n /**\n * The note's theory labels at its onset, so a caller can tell a genuine clash\n * from a passing tone, a neighbour, or a prepared suspension — all of which\n * are ordinary non-chord tones that still evaluate as unsafe.\n */\n labels: TheoryLabel[];\n /** The clash severity: {@link NoteSafety.Warning} or {@link NoteSafety.Dissonant}. */\n safety: NoteSafety;\n /** {@link SafetyResult.reasons} bitmask. */\n reasons: number;\n /** Preferred stepwise resolution, when the safety evaluator provides one. */\n resolveTo?: number;\n /** Nearby fully safe replacement pitches, nearest first. */\n suggestions?: number[];\n /**\n * The notes behind the pairwise flags in `reasons` — a parallel fifth, a\n * crossing, a vertical clash — when any are set.\n */\n partners?: ConflictPartner[];\n rationale?: string;\n};\n\n/**\n * Override an inferred result's key regions with ones the caller already had.\n *\n * The chords stay as inferred: they were found against the caller's regions\n * only if the caller also passed them down, and quietly re-deriving them here\n * would make the same options produce different chords depending on which pass\n * ran first.\n */\nfunction withGivenKeys(\n result: ChordTimelineResult,\n given: KeyRegion[] | undefined,\n): ChordTimelineResult {\n if (given === undefined) {\n return result;\n }\n return { ...result, keys: given, prevailingKey: prevailingKeyOf(given) ?? result.prevailingKey };\n}\n\n/**\n * Build the same result shape from a timeline the caller already had.\n *\n * The timeline was not inferred from these notes, so it carries no measured\n * confidence — reporting 1 would turn every confidence gate into an\n * unconditional pass. The key regions still come from the notes, because the\n * caller supplied chords, not an answer about the key; they are read over the\n * span the notes occupy, against the whole meter map, and annotated with their\n * pivots, exactly as the inferring path reads and annotates its own.\n */\nfunction callerTimeline(\n timeline: ChordTimeline,\n key: KeyLike | undefined,\n given: KeyRegion[] | undefined,\n pooled: NoteEvent[],\n meters: MeterMap,\n budget: number | undefined,\n): {\n timeline: ChordTimeline;\n keys: KeyRegion[];\n prevailingKey: ResolvedKey;\n segmentConfidence: number[];\n} {\n // The span is the one the notes sound over, not the one the chords cover: a\n // chart of the opening bars is a partial answer about the harmony and no\n // answer at all about where the piece ends, so stopping the key search at its\n // last chord would read every note after it against the opening key.\n const chartEnd = timeline.segments.reduce((end, segment) => Math.max(end, segment.endBeat), 0);\n const soundingEnd = pooled.reduce(\n (end, note) => Math.max(end, note.startBeat + note.durationBeat),\n 0,\n );\n const totalBeats = Math.max(chartEnd, soundingEnd);\n // ...and it begins where the music begins, read from the one derivation of an\n // analysed span's origin that the inferring path and the tension curve read\n // too: an excerpt lifted from bar 9 holds no key over the eight bars of\n // silence a hardcoded 0 would report it as covering.\n const { startBeat: musicStart } = gridForNotes(pooled, beatsPerBarAt(0, meters));\n // Caller-supplied regions are the caller's answer and are passed through as\n // they are; the ones derived here are annotated like any other, since the\n // chords a pivot is read from are in hand either way.\n const keys =\n given ??\n attachPivots(\n key !== undefined\n ? [{ startBeat: musicStart, endBeat: totalBeats, key: resolveKey(key), confidence: 1 }]\n : keyTimelineFromNotes(pooled, { meters, totalBeats, budget }),\n timeline.segments,\n );\n return {\n timeline,\n keys,\n prevailingKey: prevailingKeyOf(keys) ?? resolveKey(majorKey(0)),\n segmentConfidence: timeline.segments.map(() => 0),\n };\n}\n\n/**\n * The full result of {@link analyzeArrangement}.\n *\n * @category Arrangement & Analysis\n */\nexport type ArrangementAnalysis = {\n /**\n * The key regions the analysis ran against, in time order. Roman numerals,\n * harmonic function and note safety are all judged against the region\n * covering the beat, so a piece that modulates is not read against the key it\n * started in for every bar after it leaves.\n */\n keys: KeyRegion[];\n /**\n * The key held longest across {@link ArrangementAnalysis.keys}, whole: the\n * pitch classes with the spelling and the scale form the regions carry.\n */\n prevailingKey: ResolvedKey;\n timeline: ChordTimeline;\n segmentConfidence: number[];\n cadences: CadenceHit[];\n tracks: TrackAnalysis[];\n /** Notes clashing with the sounding harmony, worst severity first. */\n conflicts: Conflict[];\n};\n\n/**\n * Options controlling {@link analyzeArrangement} and {@link tensionCurve}.\n *\n * @category Arrangement & Analysis\n */\nexport type ArrangementOptions = {\n /**\n * A single key held across the whole arrangement. Omit it, and `keys` with\n * it, to have the key searched for over time so a modulating piece is read\n * against the key actually in force.\n *\n * Taken in whatever form the caller holds a key, and kept whole: the region\n * built from it carries the spelling and the scale form it was named with.\n */\n key?: KeyLike;\n /**\n * Key regions to judge against, when a previous pass already worked them out.\n * Takes precedence over `key`; supplying both is answering the same question\n * twice, and the more specific answer wins.\n */\n keys?: KeyRegion[];\n /**\n * A chord timeline to analyse against, instead of inferring one from the\n * notes. Supply it to analyse a hand-written progression, or to reuse the\n * timeline of an earlier call rather than re-running chord inference.\n */\n timeline?: ChordTimeline;\n /**\n * Indices of the tracks the harmony is inferred from. Defaults to every\n * pitched track. Percussion tracks are excluded either way. An index naming\n * no track is an input error rather than one that matches nothing.\n */\n harmonyTracks?: number[];\n /**\n * Lowest severity reported in `conflicts`.\n *\n * @defaultValue {@link NoteSafety.Warning}\n */\n minSeverity?: NoteSafety;\n /**\n * A single time signature held across the whole arrangement, as sugar for a\n * one-element `meters`; defaults to 4/4. Giving both is an input error.\n *\n * @defaultValue `4/4`\n */\n ts?: MeterLike;\n /**\n * The meter as it changes over the arrangement. Bar lines, downbeats and\n * metric weight all follow the signature in force at the beat in question.\n *\n * @defaultValue 4/4 throughout\n */\n meters?: MeterLike;\n /**\n * Length of the pickup in beats, when the piece starts with one.\n *\n * The first downbeat is beat 0, so an upbeat is written at negative beats.\n * Declaring its length rejects a note that starts before the pickup does;\n * leave it unset to accept any finite onset.\n *\n * @defaultValue no declared pickup\n */\n pickupBeats?: number;\n /**\n * Expected chord length in beats; defaults to the length of the opening bar.\n *\n * A prior rather than a grid: the arrangement analysis infers its harmony\n * with dynamic segmentation, where the longer a chord is expected to last the\n * more evidence a change needs before the search will place one. A chord may\n * still begin at any position the notes argue for. Supply `timeline` to\n * analyse against boundaries you have fixed yourself.\n *\n * @defaultValue the length of the opening bar\n */\n harmonicRhythm?: number;\n /**\n * Safety profile used for conflict detection; defaults to `pop`.\n *\n * @defaultValue `'pop'`\n */\n profile?: SafetyProfile;\n /**\n * Upper bound on the work this call may do — note counts, windows, and\n * candidate counts are each checked against it before anything is allocated.\n *\n * Raise it to analyse a piece larger than the default allows; the default is\n * {@link DEFAULT_GENERATION_BUDGET}, chosen so a runaway input fails fast\n * rather than blocking the thread.\n *\n * @defaultValue {@link DEFAULT_GENERATION_BUDGET}\n */\n budget?: number;\n};\n\n/**\n * Sounding pitches of every sub-voice at a beat, excluding one sub-voice.\n *\n * Only the sub-voice under analysis is excluded — sibling sub-voices of the\n * same track are included, so dissonant clusters inside a single polyphonic\n * track are still detected.\n */\ntype SoundingVoice = {\n track: number;\n voice: number;\n /** The track's index in the caller's input array. */\n trackIndex: number;\n note: PreparedNote;\n snapshot: VoiceSnapshot;\n};\n\n/** Build one complete arrangement snapshot for an evaluation beat. */\nfunction soundingVoicesAt(prepared: PreparedTrack[], beat: number): SoundingVoice[] {\n const out: SoundingVoice[] = [];\n for (let t = 0; t < prepared.length; t += 1) {\n const track = prepared[t];\n if (!track || isPercussion(track.role)) {\n continue;\n }\n for (let v = 0; v < track.voices.length; v += 1) {\n // Each sub-voice is monophonic, so this is one binary search per voice\n // rather than a scan of every note in the arrangement.\n const note = track.voices[v]?.at(beat);\n if (note === undefined) {\n continue;\n }\n const snap: VoiceSnapshot = { pitch: note.pitch };\n // Motion reasons compare one real transition shared by both voices.\n // A voice attacking exactly here contributes its adjacent predecessor;\n // a sustained voice contributes the same pitch (oblique motion).\n const previous = sameInstant(note.startBeat, beat) ? note.prevPitch : note.pitch;\n if (previous !== undefined) {\n snap.prevPitch = previous;\n }\n out.push({ track: t, voice: v, trackIndex: track.trackIndex, note, snapshot: snap });\n }\n }\n return out;\n}\n\n/** Get every simultaneous voice except the one being evaluated. */\nfunction otherVoicesSounding(\n prepared: PreparedTrack[],\n excludeTrack: number,\n excludeVoice: number,\n beat: number,\n cache: Map<number, SoundingVoice[]>,\n): SoundingVoice[] {\n const sounding = cache.get(beat) ?? soundingVoicesAt(prepared, beat);\n cache.set(beat, sounding);\n return sounding.filter(({ track, voice }) => track !== excludeTrack || voice !== excludeVoice);\n}\n\n/** Name a sounding voice the way a {@link Conflict} names its own note. */\nfunction conflictPartner(sounding: SoundingVoice, reasons: number): ConflictPartner {\n const partner: ConflictPartner = {\n trackIndex: sounding.trackIndex,\n noteId: sounding.note.id,\n reasons,\n };\n if (sounding.note.originalIndex !== undefined) {\n partner.originalIndex = sounding.note.originalIndex;\n }\n return partner;\n}\n\n/** The snapshots {@link evaluateSafety} reads, in the order partners index them. */\nfunction snapshotsOf(voices: SoundingVoice[]): VoiceSnapshot[] {\n return voices.map(({ snapshot }) => snapshot);\n}\n\n/**\n * Label every note of one polyphonic passage against the harmony under it and\n * the voices around it.\n *\n * {@link analyzeVoice} reads a single line: it takes the note before and the note\n * after each note as that note's melodic neighbours, and it is given no other\n * voices, so over a flat array of everything that sounds it invents figures\n * across parts — the alto between two soprano notes becomes a passing tone —\n * and can never find a suspension, which is by definition a dissonance against\n * something else sounding. So the passage is partitioned into monophonic\n * sub-voices first and each is read with the others handed to it, which is what\n * {@link analyzeArrangement} does with the tracks it is given.\n *\n * @param notes The passage, in any order; the ids are indices into this array.\n * @param chordAtBeat Chord sounding at a given beat, or null.\n * @param key The key, or the key in force at a given beat.\n * @returns One annotation per note, in the order the notes were given, each\n * carrying its position in that array as its id.\n */\nexport function analyzePolyphony(\n notes: readonly NoteEvent[],\n chordAtBeat: (beat: number) => Chord | null,\n key: KeyContext,\n): AnalyzedNote[] {\n // The same reading of an event array the arrangement entrances apply, for the\n // same reason: the passage is walked note by note further down, where a null\n // in the array reports this library's own `TypeError` rather than the input.\n // Zero-length events are admitted here too — the split drops them.\n const passage = assertNoteEvents(notes, 'notes', { allowNonPositiveDuration: true });\n assertFunction<(beat: number) => Chord | null>(chordAtBeat, 'chordAtBeat');\n const prepared = prepareTracks([{ notes: [...passage] }]);\n const track = prepared[0];\n // Every note is read against the other sub-voices sounding under it, so the\n // work is the product of the two. This entry point takes no options, so it is\n // the default budget it is charged against; what matters is that it fails\n // before the cache is filled rather than after the comparisons are made.\n // Its siblings over the same machinery charge the same product.\n assertGenerationBudget(\n passage.length * (track?.voices.length ?? 0),\n 'polyphony note-voice comparisons',\n );\n const soundingCache = new Map<number, SoundingVoice[]>();\n const byIndex = new Map<number, AnalyzedNote>();\n for (let v = 0; v < (track?.voices.length ?? 0); v += 1) {\n const subVoice = track?.voices[v];\n if (subVoice === undefined) {\n continue;\n }\n const analyzed = analyzeVoice(subVoice.voice, chordAtBeat, key, (beat) =>\n snapshotsOf(otherVoicesSounding(prepared, 0, v, beat, soundingCache)),\n );\n for (const note of analyzed) {\n if (note.originalIndex !== undefined) {\n // The split's own bookkeeping stops here: the caller asked about its own\n // array, so the answer carries that position as the id and nothing that\n // says the notes were taken apart on the way.\n const { originalIndex, ...annotation } = note;\n byIndex.set(originalIndex, { ...annotation, noteId: originalIndex });\n }\n }\n }\n // A note of no length never sounds, so the split above drops it and no other\n // voice can be dissonant against it; it is still one of the caller's notes,\n // and reading it against the chord alone says what it is.\n return notes.map((note, index) => {\n const analyzed = byIndex.get(index);\n if (analyzed !== undefined) {\n return analyzed;\n }\n const [only] = analyzeVoice([{ ...note, id: index }], chordAtBeat, key);\n return (\n only ?? {\n noteId: index,\n pitch: note.pitch,\n startBeat: note.startBeat,\n durationBeat: note.durationBeat,\n labels: [],\n }\n );\n });\n}\n\n/** Index of the first segment starting strictly after a beat. */\nfunction firstSegmentAfter(timeline: ChordTimeline, beat: number): number {\n let low = 0;\n let high = timeline.segments.length;\n while (low < high) {\n const middle = Math.floor((low + high) / 2);\n if ((timeline.segments[middle]?.startBeat ?? Number.POSITIVE_INFINITY) <= beat + BEAT_EPS) {\n low = middle + 1;\n } else {\n high = middle;\n }\n }\n return low;\n}\n\n/**\n * The beats at which a note must be checked against the harmony: its onset,\n * plus the onset of every later chord segment the note sustains across. Each\n * crossed segment is visited exactly once (at its own start), so a boundary\n * beat is never evaluated twice for the same note.\n *\n * Segments are in beat order, so the crossed ones are a contiguous run found by\n * binary search rather than by scanning the whole timeline for every note.\n */\nfunction evaluationBeats(note: VoiceNote, timeline: ChordTimeline): number[] {\n const noteEnd = note.startBeat + note.durationBeat;\n const beats = [note.startBeat];\n for (let i = firstSegmentAfter(timeline, note.startBeat); i < timeline.segments.length; i += 1) {\n const start = timeline.segments[i]?.startBeat;\n if (start === undefined || start >= noteEnd - BEAT_EPS) {\n break;\n }\n beats.push(start);\n }\n return beats;\n}\n\n/**\n * Analyse a whole arrangement against the harmony inferred from it.\n *\n * The chord timeline and the key regions it is read against are inferred from\n * the pooled notes of every pitched track (see\n * {@link chordTimelineFromNotes}); pooling all voices is\n * robust even when roles are absent or a track doubles the harmony, so it is\n * preferred over deriving the harmony from a subset. A track marked\n * `role: 'drums'` is excluded — its pitches name instruments, not harmony, and\n * pooling them would corrupt the key, the timeline and every reading built on\n * them. Pass `harmonyTracks` to restrict inference further, or `timeline` to\n * analyse against a progression you already have. Zero- and negative-length\n * notes never sound, so they are dropped at ingest and appear in neither the\n * annotations nor the conflicts.\n *\n * Voice analysis assumes one monophonic voice at a time, so each track is\n * first partitioned into monophonic sub-voices (a polyphonic block-chord track\n * fans out into one sub-voice per simultaneous note; a monophonic track is\n * unaffected). Each sub-voice is labelled note-by-note against the harmony\n * with {@link analyzeVoice}, seeing every other sub-voice — including siblings\n * within the same track — as its accompaniment. Every note is then re-checked\n * with {@link evaluateSafety} at its onset and again at each chord change it\n * sustains across, so a held note that clashes with a later chord is caught:\n * evaluations that are not {@link NoteSafety.Safe} become conflicts, one per\n * clashing beat, sorted worst severity first and then by beat.\n *\n * Every ordinary non-chord tone — a passing note, a neighbour, a prepared\n * suspension — is by definition unsafe against the chord under it, so the\n * conflict list is a report, not a fault list. Each conflict carries the note's\n * `labels` and its `trackIndex`/`originalIndex`, so a caller can tell those\n * apart and map a conflict back to the note it passed in; `minSeverity` narrows\n * the list to outright dissonance.\n *\n * @param tracks The tracks to analyse.\n * @param opts Analysis options; see {@link ArrangementOptions}.\n * @returns The inferred harmony, per-track annotations, cadences, and conflicts.\n * @example\n * ```ts\n * import { analyzeArrangement } from '@libraz/libcantus';\n * const melody = [\n * { pitch: 60, startBeat: 0, durationBeat: 2 },\n * { pitch: 67, startBeat: 2, durationBeat: 2 },\n * ];\n * const { prevailingKey, conflicts } = analyzeArrangement([{ role: 'melody', notes: melody }]);\n * prevailingKey.scale.rootPc; // the key held longest across the analysis\n * conflicts; // notes clashing with the inferred harmony, worst severity first\n * ```\n * @category Arrangement & Analysis\n */\nexport function analyzeArrangement(\n tracks: ArrangementTrack[],\n opts: ArrangementOptions = {},\n): ArrangementAnalysis {\n return analyzeArrangementWith(tracks, opts).analysis;\n}\n\n/**\n * Analyse an arrangement, carrying over the harmonic evidence an edit cannot\n * reach and handing back the evidence this pass leaves behind.\n *\n * Only the harmony is carried over. The per-note annotation pass is re-run in\n * full because neither half of it is local to the edit: sub-voice lane\n * assignment is a greedy left-to-right pass over the whole track, so an inserted\n * note can re-lane every note after it, and every note is evaluated against the\n * other voices sounding beneath it, so an edit in one track changes what every\n * other track hears at those beats.\n *\n * Not part of the public surface: the evidence is the private state of an\n * incremental re-analysis, and a caller holding it could pair it with notes it\n * was never derived from.\n *\n * @param tracks The tracks to analyse.\n * @param opts Analysis options.\n * @param previous Evidence from the analysis being updated, when there is one.\n * @param dirty The slots the edit may have changed.\n * @returns The analysis, and the evidence behind it when the harmony was\n * inferred rather than supplied.\n */\nexport function analyzeArrangementWith(\n tracks: ArrangementTrack[],\n opts: ArrangementOptions = {},\n previous?: TimelineEvidence,\n dirty?: DirtySlots,\n): { analysis: ArrangementAnalysis; evidence?: TimelineEvidence } {\n const meters = resolveMeters(opts, 'arrangement meters');\n const budget = opts.budget;\n const noteOptions: NoteEventAssertOptions = { allowNonPositiveDuration: true, budget };\n if (opts.pickupBeats !== undefined) {\n assertRange(opts.pickupBeats, 0, Number.MAX_SAFE_INTEGER, 'arrangement pickupBeats');\n noteOptions.minStartBeat = -opts.pickupBeats;\n }\n assertArray(tracks, 'arrangement tracks');\n assertGenerationBudget(tracks.length, 'arrangement tracks', budget);\n const noteCount = assertTrackNotes(tracks, noteOptions);\n // The pooled total is what the downstream timeline analysis actually sizes\n // its work by, so it is checked here, under this layer's own name.\n assertGenerationBudget(noteCount, 'arrangement notes', budget);\n const profile = arrangementProfile(opts.profile);\n // A severity outside the scale narrows the report to nothing, which reads\n // exactly like a clean arrangement; the class API refuses it, so the function\n // it delegates to has to refuse the same values.\n const minSeverity =\n opts.minSeverity === undefined\n ? NoteSafety.Warning\n : assertInteger(\n opts.minSeverity,\n 'arrangement minSeverity',\n NoteSafety.Safe,\n NoteSafety.Dissonant,\n );\n // The two options a caller can hand a previous pass's work through are read\n // for their shape here, with the rest of the options, rather than at the line\n // that first dereferences them several layers down.\n assertGivenTimeline(opts.timeline, 'arrangement timeline');\n assertGivenKeys(opts.keys, 'arrangement keys');\n const harmonyTracks = harmonyTrackSet(opts.harmonyTracks, tracks);\n const prepared = prepareTracks(tracks);\n // Every note is evaluated against the other sub-voices sounding beneath it, so\n // the work is the product of the two — not either dimension alone, which is\n // all the scalar checks above measure. The sub-voices have to be derived to be\n // counted, but that is the same O(n log n) pass the note checks already are,\n // and the check still lands before the harmony is pooled and inferred.\n const voiceCount = prepared.reduce((sum, track) => sum + track.voices.length, 0);\n assertGenerationBudget(noteCount * voiceCount, 'arrangement note-voice comparisons', budget);\n const pooled = poolNotes(tracks, harmonyTracks);\n\n let evidence: TimelineEvidence | undefined;\n let inferred: ChordTimelineResult;\n if (opts.timeline === undefined) {\n const run = analyzeTimeline(\n pooled,\n {\n key: opts.key,\n meters,\n pickupBeats: opts.pickupBeats,\n harmonicRhythm: opts.harmonicRhythm,\n budget,\n },\n previous,\n dirty,\n );\n evidence = run.evidence;\n inferred = withGivenKeys(run.result, opts.keys);\n } else {\n inferred = callerTimeline(opts.timeline, opts.key, opts.keys, pooled, meters, budget);\n }\n const { timeline, keys, prevailingKey, segmentConfidence } = inferred;\n const keyAt = keyLookup(keys, prevailingKey);\n // The pitch classes of the key in force, for the readings below that ask\n // which notes are in the key rather than how it is written. Named here so the\n // spelling is dropped once, where it can be seen, instead of at each call.\n const scaleAt = (beat: number) => scaleOf(keyAt(beat));\n // A cadence is heard in the key it arrives in, which after a modulation is\n // not the key the piece opened in.\n const cadences = detectCadences(timeline, scaleAt);\n const soundingCache = new Map<number, SoundingVoice[]>();\n\n const trackAnalyses: TrackAnalysis[] = [];\n const conflicts: Conflict[] = [];\n\n for (let t = 0; t < prepared.length; t += 1) {\n const track = prepared[t];\n if (!track) {\n continue;\n }\n if (isPercussion(track.role)) {\n // A drum hit has no harmonic reading, so labelling it against the chord\n // would produce noise rather than analysis.\n trackAnalyses.push({ name: track.name, role: track.role, notes: [] });\n continue;\n }\n const notes: AnalyzedNote[] = [];\n for (let v = 0; v < track.voices.length; v += 1) {\n const subVoice = track.voices[v];\n if (!subVoice) {\n continue;\n }\n const analyzed = analyzeVoice(subVoice.voice, timeline.at, scaleAt, (beat) =>\n snapshotsOf(otherVoicesSounding(prepared, t, v, beat, soundingCache)),\n );\n // Appended one at a time: a spread of a long sub-voice passes every note\n // as an argument, which overflows the call stack on a track the budget\n // above accepts.\n for (const note of analyzed) {\n notes.push({ ...note, trackIndex: track.trackIndex });\n }\n const labelsById = new Map(analyzed.map((note) => [note.noteId, note.labels]));\n\n for (let noteIndex = 0; noteIndex < subVoice.voice.length; noteIndex += 1) {\n const note = subVoice.voice[noteIndex];\n const preparedNote = subVoice.sounding[noteIndex];\n if (note === undefined || preparedNote === undefined) {\n continue;\n }\n for (const beat of evaluationBeats(note, timeline)) {\n const atOnset = sameInstant(beat, note.startBeat);\n const others = otherVoicesSounding(prepared, t, v, beat, soundingCache);\n const safetyQuery = {\n profile,\n candidatePitch: note.pitch,\n prevPitch: atOnset ? preparedNote.prevPitch : note.pitch,\n chord: timeline.at(beat),\n key: scaleAt(beat),\n otherVoices: snapshotsOf(others),\n strongBeat: isStrongBeat(beat, meters),\n };\n // Most evaluations become no reportable conflict. Avoid their\n // replacement-pitch scan (up to 24 recursive safety checks), then\n // collect suggestions only for the conflicts we will retain.\n let result = evaluateSafety(safetyQuery, { suggestions: false });\n if (result.safety !== NoteSafety.Safe && result.safety >= minSeverity) {\n result = evaluateSafety(safetyQuery, { partners: true });\n const conflict: Conflict = {\n beat,\n trackName: track.name,\n trackIndex: track.trackIndex,\n noteId: note.id,\n pitch: note.pitch,\n labels: labelsById.get(note.id) ?? [],\n safety: result.safety,\n reasons: result.reasons,\n };\n if (note.originalIndex !== undefined) {\n conflict.originalIndex = note.originalIndex;\n }\n if (result.rationale !== undefined) {\n conflict.rationale = result.rationale;\n }\n if (result.resolveTo !== undefined) {\n conflict.resolveTo = result.resolveTo;\n }\n if (result.suggestions !== undefined) {\n conflict.suggestions = [...result.suggestions];\n }\n if (result.partners !== undefined) {\n conflict.partners = result.partners.map(({ index, reasons }) =>\n conflictPartner(others[index] as SoundingVoice, reasons),\n );\n }\n conflicts.push(conflict);\n }\n }\n }\n }\n // Sub-voice results interleave; restore the track's onset-then-pitch order.\n notes.sort((a, b) => a.noteId - b.noteId);\n trackAnalyses.push({ name: track.name, role: track.role, notes });\n }\n\n conflicts.sort((a, b) => b.safety - a.safety || a.beat - b.beat);\n\n const analysis: ArrangementAnalysis = {\n keys,\n prevailingKey,\n timeline,\n segmentConfidence,\n cadences,\n tracks: trackAnalyses,\n conflicts,\n };\n return evidence === undefined ? { analysis } : { analysis, evidence };\n}\n","/**\n * Incremental arrangement analysis: an analysis held open across edits, so a\n * host that re-analyses on every keystroke pays for the beats that changed\n * rather than for the whole piece.\n */\n\nimport { InvalidInputError } from '../../core/errors/index.js';\nimport type { NoteEvent } from '../../core/types.js';\nimport { assertGenerationBudget, assertNoteEvents } from '../../core/validation/index.js';\nimport type { DirtySlots, TimelineEvidence } from '../timeline/index.js';\nimport { dirtySlotsFor } from '../timeline/index.js';\nimport type { ArrangementAnalysis, ArrangementOptions, ArrangementTrack } from './tracks.js';\nimport { analyzeArrangementWith } from './tracks.js';\n\n/**\n * One track's notes as they stand after an edit.\n *\n * The whole note array is given rather than a description of what moved: a host\n * always has the track's current contents, and working out what actually\n * changed is this module's job, not the caller's.\n *\n * @category Arrangement & Analysis\n */\nexport type TrackEdit = {\n /** Index of the track in the session's track array. */\n trackIndex: number;\n /** The track's notes after the edit. */\n notes: readonly NoteEvent[];\n};\n\n/**\n * An arrangement analysis held open for editing.\n *\n * @category Arrangement & Analysis\n */\nexport type ArrangementSession = {\n /** The tracks the current analysis was made from. */\n readonly tracks: readonly ArrangementTrack[];\n /** The analysis of those tracks. */\n readonly analysis: ArrangementAnalysis;\n /**\n * Re-analyse after replacing the notes of one or more tracks, reusing\n * everything the edit provably cannot reach.\n *\n * The result equals {@link analyzeArrangement} over the edited tracks — the\n * same segments, confidences, key regions, cadences, annotations and\n * conflicts — so a host may hold a session for as long as it likes without\n * the analysis drifting from what a fresh pass would say.\n */\n update: (edits: readonly TrackEdit[]) => ArrangementSession;\n};\n\n/** Identity of one note, for telling an edited note array from an unedited one. */\nfunction noteId(note: NoteEvent): string {\n return `${note.pitch}|${note.startBeat}|${note.durationBeat}|${note.velocity ?? ''}`;\n}\n\n/** The beats an edit touched, or null when the two note arrays agree. */\ntype EditedSpan = { startBeat: number; endBeat: number };\n\n/**\n * Whether two note arrays hold the same notes in the same positions.\n *\n * A re-ordering changes no beat of the music, but `originalIndex` reports a\n * note's position in the caller's array, so the annotations still have to be\n * remade against the new order.\n */\nfunction sameOrder(before: readonly NoteEvent[], after: readonly NoteEvent[]): boolean {\n if (before.length !== after.length) {\n return false;\n }\n for (let index = 0; index < before.length; index += 1) {\n const previous = before[index];\n const current = after[index];\n if (previous === undefined || current === undefined || noteId(previous) !== noteId(current)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * The beats covered by the notes that `before` and `after` disagree on.\n *\n * Notes are compared as values and by multiplicity, so re-ordering a track's\n * array — which no beat of the harmony is sensitive to — covers no beats at\n * all, while adding a second copy of a note covers the one it sounds on.\n */\nfunction editedSpan(before: readonly NoteEvent[], after: readonly NoteEvent[]): EditedSpan | null {\n const counts = new Map<string, { count: number; startBeat: number; endBeat: number }>();\n const tally = (notes: readonly NoteEvent[], sign: number): void => {\n for (const note of notes) {\n const id = noteId(note);\n const entry = counts.get(id);\n if (entry === undefined) {\n counts.set(id, {\n count: sign,\n startBeat: note.startBeat,\n endBeat: note.startBeat + note.durationBeat,\n });\n } else {\n entry.count += sign;\n }\n }\n };\n tally(before, 1);\n tally(after, -1);\n let startBeat = Number.POSITIVE_INFINITY;\n let endBeat = Number.NEGATIVE_INFINITY;\n for (const entry of counts.values()) {\n if (entry.count === 0) {\n continue;\n }\n startBeat = Math.min(startBeat, entry.startBeat);\n // A note of zero or negative length never sounds, but its onset still marks\n // where the caller edited, so the span is never empty for a real change.\n endBeat = Math.max(endBeat, entry.endBeat, entry.startBeat);\n }\n return startBeat <= endBeat ? { startBeat, endBeat } : null;\n}\n\n/** Merge two edited spans into the one covering both. */\nfunction unionSpan(a: EditedSpan | null, b: EditedSpan | null): EditedSpan | null {\n if (a === null) {\n return b;\n }\n if (b === null) {\n return a;\n }\n return {\n startBeat: Math.min(a.startBeat, b.startBeat),\n endBeat: Math.max(a.endBeat, b.endBeat),\n };\n}\n\n/** Build a session around one completed analysis. */\nfunction sessionOf(\n tracks: readonly ArrangementTrack[],\n opts: ArrangementOptions,\n analysis: ArrangementAnalysis,\n evidence: TimelineEvidence | undefined,\n): ArrangementSession {\n return {\n tracks,\n analysis,\n update(edits) {\n assertGenerationBudget(edits.length, 'arrangement edits', opts.budget);\n const next = [...tracks];\n let edited: EditedSpan | null = null;\n let reordered = false;\n for (let index = 0; index < edits.length; index += 1) {\n const edit = edits[index];\n if (edit === undefined) {\n continue;\n }\n const target = next[edit.trackIndex];\n if (target === undefined) {\n throw new InvalidInputError(\n `edits[${index}].trackIndex ${edit.trackIndex} is outside the session's ${tracks.length} tracks`,\n );\n }\n if (!Array.isArray(edit.notes)) {\n throw new InvalidInputError(\n `edits[${index}].notes must be an array; received ${typeof edit.notes}`,\n );\n }\n assertNoteEvents(edit.notes, `edits[${index}].notes`, {\n allowNonPositiveDuration: true,\n budget: opts.budget,\n });\n edited = unionSpan(edited, editedSpan(target.notes, edit.notes));\n reordered = reordered || !sameOrder(target.notes, edit.notes);\n next[edit.trackIndex] = { ...target, notes: edit.notes };\n }\n // Nothing the analysis reads actually changed, so the analysis has not.\n if (edited === null && !reordered) {\n return sessionOf(next, opts, analysis, evidence);\n }\n // A pure re-ordering moves no note, so every slot of the harmony is clean\n // and the whole timeline is carried over; what it does move is the array\n // position each annotation reports, so the annotation pass is re-run.\n const dirty: DirtySlots | undefined =\n evidence === undefined\n ? undefined\n : edited === null\n ? { from: 0, to: 0 }\n : dirtySlotsFor(evidence, edited.startBeat, edited.endBeat);\n const run = analyzeArrangementWith(next, opts, evidence, dirty);\n return sessionOf(next, opts, run.analysis, run.evidence);\n },\n };\n}\n\n/**\n * Open an arrangement analysis that can be updated a few beats at a time.\n *\n * The first pass is an ordinary {@link analyzeArrangement}; what the session\n * adds is the slot-level evidence behind it, so a later {@link\n * ArrangementSession.update} can work out which beats an edit could possibly\n * have changed and leave the rest of the harmony alone. An update returns a new\n * session, so the analysis before an edit stays valid and a host can keep it\n * for undo.\n *\n * What an update recomputes is derived rather than guessed at:\n *\n * - The edited beats are the beats covered by the notes the old and new track\n * arrays disagree on, which is narrower than the track and usually far\n * narrower than the piece.\n * - A slot's evidence sees exactly the notes overlapping it, so the slots to\n * re-derive are the slots overlapping those beats — plus one either side, so\n * that an onset sitting a floating-point hair off a slot boundary cannot\n * leave a stale slot behind.\n * - The boundary search itself has no such margin. It is a shortest-path search\n * over a whole run of slots, so a changed slot can move a boundary anywhere in\n * the run, and its change cost is calibrated on the mean weight of every\n * sounding slot in the piece, which couples the runs to one another. The\n * search is therefore re-run over the whole piece — arithmetic over the\n * carried-over evidence, with nothing allocated per slot — rather than being\n * spliced at a margin that does not exist.\n * - Each settled segment's chord is carried over when its bounds, its key and\n * its slots are all unchanged, since those are the only things chord\n * inference reads.\n *\n * Key regions and the per-note annotations are recomputed in full. Both are\n * whole-piece passes — the key search is a shortest-path search with no reset\n * points, sub-voice lane assignment is greedy from the first note, and every\n * note is judged against the other voices sounding beneath it — so neither has\n * a sound local answer, and reporting one would mean an update that quietly\n * disagreed with a fresh analysis.\n *\n * An update falls back to a full analysis whenever the edit moves the ground the\n * evidence stands on: a changed piece length, a changed first onset (both of\n * which move the slot grid), or an edit to a session whose harmony was supplied\n * through {@link ArrangementOptions.timeline}. The answer is the same either\n * way; only the work differs.\n *\n * @param tracks The tracks to analyse.\n * @param opts Analysis options; see {@link ArrangementOptions}.\n * @returns A session holding the analysis and the evidence behind it.\n * @example\n * ```ts\n * import { createArrangementSession } from '@libraz/libcantus';\n * const melody = [{ pitch: 60, startBeat: 0, durationBeat: 4 }];\n * const session = createArrangementSession([{ role: 'melody', notes: melody }]);\n * const moved = [{ pitch: 62, startBeat: 0, durationBeat: 4 }];\n * const next = session.update([{ trackIndex: 0, notes: moved }]);\n * next.analysis.timeline.segments; // as if analyzed from scratch\n * ```\n * @category Arrangement & Analysis\n */\nexport function createArrangementSession(\n tracks: ArrangementTrack[],\n opts: ArrangementOptions = {},\n): ArrangementSession {\n const run = analyzeArrangementWith(tracks, opts);\n return sessionOf([...tracks], opts, run.analysis, run.evidence);\n}\n","/**\n * Arrangement tension analysis: a coarse harmonic-tension curve sampled at\n * regular beats across a whole multi-track piece, combining the sounding\n * chord's harmonic function, the dissonance of the sounding notes, and their\n * registral span into a single normalized reading.\n */\n\nimport type { MeterMap } from '../../core/meter/index.js';\nimport { isStrongBeat, resolveMeters } from '../../core/meter/index.js';\nimport { pitchClassOf as pitchClass } from '../../core/pitch/index.js';\nimport type { KeyScale } from '../../core/types.js';\nimport type { NoteEventAssertOptions } from '../../core/validation/index.js';\nimport {\n assertArray,\n assertGenerationBudget,\n assertInteger,\n assertRange,\n assertRecord,\n} from '../../core/validation/index.js';\nimport { chordPitchClasses } from '../../theory/chord/index.js';\nimport { evaluateSafety, NoteSafety, type SafetyProfile } from '../../theory/safety/index.js';\nimport { majorKey, resolveKey, scaleOf } from '../../theory/scale/index.js';\nimport { BEAT_EPS } from '../adjacency.js';\nimport { functionOf } from '../functional/index.js';\nimport { gridForNotes } from '../grid.js';\nimport { keyLookup, keyTimelineFromNotes, prevailingKeyOf } from '../keys/index.js';\nimport { type ChordTimeline, chordTimelineFromNotes } from '../timeline/index.js';\nimport {\n arrangementProfile,\n assertGivenKeys,\n assertGivenTimeline,\n assertTrackNotes,\n harmonyTrackSet,\n isPercussion,\n type PreparedTrack,\n poolNotes,\n prepareTracks,\n} from './internal.js';\nimport type { ArrangementAnalysis, ArrangementOptions, ArrangementTrack } from './tracks.js';\n\n/**\n * A tension reading sampled at a beat.\n *\n * @category Arrangement & Analysis\n */\nexport type TensionPoint = {\n beat: number;\n /** Combined tension in [0, 1]. */\n tension: number;\n};\n\n/** Tension contributed by the sounding chord's harmonic function. */\nconst FUNCTION_TENSION: Record<'tonic' | 'subdominant' | 'dominant', number> = {\n tonic: 0,\n subdominant: 0.5,\n dominant: 1,\n};\n\n/** Weight of the harmonic-function term in the combined tension score. */\nconst FUNCTION_WEIGHT = 0.5;\n/** Weight of the non-chord-tone / dissonance term. */\nconst DISSONANCE_WEIGHT = 0.35;\n/** Weight of the registral-span term. */\nconst SPAN_WEIGHT = 0.15;\n/** Pitch span, in semitones, that saturates the span term. */\nconst SPAN_SATURATION = 24;\n\n/** Clamp a value into [0, 1]. */\nfunction clamp01(value: number): number {\n return Math.min(1, Math.max(0, value));\n}\n\n/**\n * Sample the harmonic tension of an arrangement at regular beats.\n *\n * At each sample beat the tension combines three normalized terms:\n * - the sounding chord's harmonic function (dominant `1`, subdominant `0.5`,\n * tonic or no chord `0`), weighted `FUNCTION_WEIGHT`;\n * - the dissonance of the sounding notes — the larger of the share of sounding\n * pitches that are not chord tones and the share that {@link evaluateSafety}\n * rates {@link NoteSafety.Dissonant} — weighted `DISSONANCE_WEIGHT`;\n * - the registral span of the sounding notes, saturating at\n * `SPAN_SATURATION` semitones, weighted `SPAN_WEIGHT`.\n *\n * The weighted sum is clamped to [0, 1]. The harmony is inferred from all tracks\n * pooled together, so the result is deterministic and self-contained.\n *\n * @param tracks The tracks to sample.\n * @param opts Analysis options plus an optional `step` (default one beat).\n * @returns One {@link TensionPoint} per sampled beat, in beat order.\n * @throws If `step` is not positive.\n * @example\n * ```ts\n * import { tensionCurve } from '@libraz/libcantus';\n * const notes = [{ pitch: 60, startBeat: 0, durationBeat: 4 }];\n * const curve = tensionCurve([{ notes }], { step: 1 });\n * curve; // one { beat, tension } sample per beat, in beat order\n * ```\n * @category Arrangement & Analysis\n */\nexport function tensionCurve(\n tracks: ArrangementTrack[],\n opts: ArrangementOptions & { step?: number } = {},\n): TensionPoint[] {\n const meters = resolveMeters(opts, 'arrangement meters');\n const budget = opts.budget;\n const noteOptions: NoteEventAssertOptions = { allowNonPositiveDuration: true, budget };\n if (opts.pickupBeats !== undefined) {\n assertRange(opts.pickupBeats, 0, Number.MAX_SAFE_INTEGER, 'arrangement pickupBeats');\n noteOptions.minStartBeat = -opts.pickupBeats;\n }\n assertArray(tracks, 'arrangement tracks');\n assertGenerationBudget(tracks.length, 'arrangement tracks', budget);\n const noteCount = assertTrackNotes(tracks, noteOptions);\n // The pooled total is what the sampling and the inferred timeline size their\n // work by, so it is checked here, under this layer's own name.\n assertGenerationBudget(noteCount, 'arrangement notes', budget);\n const profile = arrangementProfile(opts.profile);\n const step = opts.step ?? 1;\n assertRange(step, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, 'tension sampling step');\n // Read by no sampling this call makes, and checked all the same: the option is\n // one of the arrangement options this entry point accepts, and a value the\n // sibling entry point refuses cannot be one this one silently takes.\n if (opts.minSeverity !== undefined) {\n assertInteger(\n opts.minSeverity,\n 'arrangement minSeverity',\n NoteSafety.Safe,\n NoteSafety.Dissonant,\n );\n }\n\n // The two options a caller can hand a previous pass's work through are read\n // for their shape here, with the rest of the options, rather than at the line\n // that first dereferences them several layers down.\n assertGivenTimeline(opts.timeline, 'arrangement timeline');\n assertGivenKeys(opts.keys, 'arrangement keys');\n const harmonyTracks = harmonyTrackSet(opts.harmonyTracks, tracks);\n const pooled = poolNotes(tracks, harmonyTracks);\n const all = poolNotes(tracks);\n const totalBeats = all.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);\n // The grid the samples sit on is a whole number of steps from beat 0, and the\n // music itself starts at `musicStart`: a pickup sounds before beat 0 and an\n // excerpt lifted from bar 9 begins there, and neither is sampled across\n // silence it never had.\n const { origin: sampleStart, startBeat: musicStart } = gridForNotes(all, step);\n // A caller-supplied timeline still needs key regions to be read against: the\n // ones the caller gave, the single key they named, or the ones the notes\n // themselves give, since supplying chords answers nothing about the key.\n const suppliedKeys =\n opts.timeline === undefined\n ? undefined\n : (opts.keys ??\n (opts.key !== undefined\n ? [\n {\n // The one region a named key holds starts where the music does,\n // as the searched regions and the timeline's own key window do:\n // a key cannot be in force over beats nothing sounds in.\n startBeat: musicStart,\n endBeat: totalBeats,\n key: resolveKey(opts.key),\n confidence: 1,\n },\n ]\n : keyTimelineFromNotes(pooled, { meters, totalBeats, budget })));\n const { timeline, keys, prevailingKey } =\n opts.timeline === undefined || suppliedKeys === undefined\n ? chordTimelineFromNotes(pooled, {\n key: opts.key,\n meters,\n pickupBeats: opts.pickupBeats,\n harmonicRhythm: opts.harmonicRhythm,\n budget,\n })\n : {\n timeline: opts.timeline,\n keys: suppliedKeys,\n prevailingKey: prevailingKeyOf(suppliedKeys) ?? resolveKey(majorKey(0)),\n };\n // Harmonic tension is judged against the key in force at the sample, not\n // against one key for the piece: a chord is only tense relative to a tonic.\n const regions = opts.keys ?? keys;\n const keyAt = keyLookup(regions, prevailingKeyOf(regions) ?? prevailingKey);\n\n const prepared = prepareTracks(tracks);\n const points: TensionPoint[] = [];\n const sampleCount = Math.max(0, Math.ceil((totalBeats - sampleStart) / step - BEAT_EPS));\n assertGenerationBudget(sampleCount, 'tension samples', budget);\n // The per-sample cost is one lookup per sub-voice, so it is their product —\n // not either dimension alone — that has to stay inside the budget.\n const voiceCount = prepared.reduce((sum, track) => sum + track.voices.length, 0);\n assertGenerationBudget(\n sampleCount * Math.min(128, voiceCount) * voiceCount,\n 'tension sample-voice comparisons',\n budget,\n );\n for (let i = 0; i < sampleCount; i += 1) {\n // The first sample is taken where the music starts rather than at the slot\n // boundary before it, so a curve carries no phantom lead-in over silence the\n // piece never had; every later sample stays a whole number of steps from\n // beat 0, which is what keeps them on the bar lines.\n const beat = i === 0 ? Math.max(sampleStart, musicStart) : sampleStart + i * step;\n points.push({\n beat,\n // The pitch classes alone: tension is scored from what sounds against\n // the tonic, and no reading below here asks how the key is written.\n tension: sampleTension(prepared, timeline, scaleOf(keyAt(beat)), meters, profile, beat),\n });\n }\n return points;\n}\n\n/**\n * Sample the tension of an arrangement already analysed by\n * {@link analyzeArrangement}, reusing its harmony instead of inferring it again.\n *\n * `analyzeArrangement` followed by `tensionCurve` runs chord and key inference\n * twice over the same notes. This takes the analysis it already produced, so\n * the curve and the annotations describe the same harmony by construction.\n *\n * The analysis is the harmony to fall back on rather than one laid over the\n * caller: `key`, `keys` and `harmonyTracks` each name a harmony the analysis\n * does not hold, so naming one is asking for a different reading, and the curve\n * is taken under it. Nothing else about the analysis is re-derived.\n *\n * @param tracks The same tracks that were analysed.\n * @param analysis The result of {@link analyzeArrangement} for those tracks.\n * @param opts Analysis options plus an optional `step` (default one beat).\n * @returns One {@link TensionPoint} per sampled beat, in beat order.\n * @example\n * ```ts\n * import { analyzeArrangement, tensionCurveFrom } from '@libraz/libcantus';\n * const tracks = [{ notes: [{ pitch: 60, startBeat: 0, durationBeat: 4 }] }];\n * const analysis = analyzeArrangement(tracks);\n * tensionCurveFrom(tracks, analysis, { step: 1 });\n * ```\n * @category Arrangement & Analysis\n */\nexport function tensionCurveFrom(\n tracks: ArrangementTrack[],\n analysis: ArrangementAnalysis,\n opts: ArrangementOptions & { step?: number } = {},\n): TensionPoint[] {\n const asked = assertRecord<ArrangementOptions & { step?: number }>(opts, 'opts');\n const carried = carriedHarmony(assertRecord<ArrangementAnalysis>(analysis, 'analysis'), asked);\n return tensionCurve(tracks, { ...carried, ...asked });\n}\n\n/**\n * How much of an analysis's harmony a set of options leaves in place.\n *\n * Restricting the harmony to a few tracks changes which notes state it, so\n * nothing of the analysis survives; naming a key or its regions changes only\n * what the chords are heard against, so the chords themselves are kept.\n */\nfunction carriedHarmony(\n analysis: ArrangementAnalysis,\n opts: ArrangementOptions,\n): ArrangementOptions {\n if (opts.harmonyTracks !== undefined) {\n return {};\n }\n if (opts.key !== undefined || opts.keys !== undefined) {\n return { timeline: analysis.timeline };\n }\n return { timeline: analysis.timeline, keys: analysis.keys };\n}\n\n/**\n * All sounding pitched notes across every track at a beat, tagged with their\n * track. Percussion carries no harmony, so it is skipped; each sub-voice is\n * monophonic, so its contribution is one binary search rather than a scan.\n */\nfunction allSounding(prepared: PreparedTrack[], beat: number): { pitch: number; track: number }[] {\n const out: { pitch: number; track: number }[] = [];\n for (let t = 0; t < prepared.length; t += 1) {\n const track = prepared[t];\n if (!track || isPercussion(track.role)) {\n continue;\n }\n for (const subVoice of track.voices) {\n const note = subVoice.at(beat);\n if (note !== undefined) {\n out.push({ pitch: note.pitch, track: t });\n }\n }\n }\n return out;\n}\n\n/** Combine the harmonic, dissonance, and span terms at one sample beat. */\nfunction sampleTension(\n prepared: PreparedTrack[],\n timeline: ChordTimeline,\n key: KeyScale,\n meters: MeterMap,\n profile: SafetyProfile,\n beat: number,\n): number {\n const sounding = allSounding(prepared, beat);\n if (sounding.length === 0) {\n return 0;\n }\n const chord = timeline.at(beat);\n const functionScore = chord ? FUNCTION_TENSION[functionOf(chord, key)] : 0;\n\n const chordPcs = chord ? new Set(chordPitchClasses(chord)) : null;\n const strongBeat = isStrongBeat(beat, meters);\n let nonChord = 0;\n let dissonant = 0;\n // A coarse tension sample supplies no individual voice history. Every voice\n // at the same MIDI pitch consequently receives the same safety verdict, so\n // a dense pad needs at most 128 evaluations instead of one per voice.\n const pitches = new Map<number, number>();\n for (const voice of sounding) {\n pitches.set(voice.pitch, (pitches.get(voice.pitch) ?? 0) + 1);\n }\n // The voices a candidate is weighed against are the texture with one of its\n // own occurrences taken out, so the texture is written down once for the\n // sample and that one occurrence is lifted out and put back around each\n // evaluation. Rebuilding it per distinct pitch instead allocated a record per\n // voice per pitch — a hundred-voice pad spending ten thousand objects on a\n // beat to say the same thing a hundred times.\n const others: { pitch: number }[] = [];\n const firstOf = new Map<number, number>();\n for (const [pitch, count] of pitches) {\n firstOf.set(pitch, others.length);\n for (let i = 0; i < count; i += 1) {\n others.push({ pitch });\n }\n }\n for (const [pitch, count] of pitches) {\n // Non-chord-tone share only applies when a chord is sounding; at a timeline\n // gap there is no reference harmony, so vertical dissonance alone drives the\n // dissonance term (the chord function term is already 0 there).\n if (chordPcs && !chordPcs.has(pitchClass(pitch))) {\n nonChord += count;\n }\n // One occurrence of the candidate's own pitch is moved to the end and\n // dropped, which leaves exactly the multiset the caller would have built.\n const at = firstOf.get(pitch) ?? 0;\n const last = others.length - 1;\n const held = others[at];\n const tail = others[last];\n if (held !== undefined && tail !== undefined) {\n others[at] = tail;\n others.pop();\n }\n // Only the verdict is read, so the replacement-pitch search is skipped.\n const result = evaluateSafety(\n { profile, candidatePitch: pitch, chord, key, otherVoices: others, strongBeat },\n { suggestions: false },\n );\n if (held !== undefined && tail !== undefined) {\n others.push(tail);\n others[at] = held;\n }\n if (result.safety === NoteSafety.Dissonant) {\n dissonant += count;\n }\n }\n const dissonanceScore = Math.max(nonChord, dissonant) / sounding.length;\n\n let low = Number.POSITIVE_INFINITY;\n let high = Number.NEGATIVE_INFINITY;\n for (const voice of sounding) {\n low = Math.min(low, voice.pitch);\n high = Math.max(high, voice.pitch);\n }\n const spanScore = Math.min(1, (high - low) / SPAN_SATURATION);\n\n return clamp01(\n FUNCTION_WEIGHT * functionScore + DISSONANCE_WEIGHT * dissonanceScore + SPAN_WEIGHT * spanScore,\n );\n}\n","/**\n * Hypermeter: the grouping of bars into hyperbars.\n *\n * Bars are felt in groups the way beats are felt in bars — four bars at a time\n * in most tonal and popular music, but two, three, six or eight elsewhere. The\n * grouping is not written in the score, so it has to be inferred from where the\n * music actually restarts: harmony changes on a hypermetric downbeat, and\n * cadences arrive at the end of a hyperbar.\n */\n\nimport type { MeterLike } from '../../core/meter/index.js';\nimport { barIndexAt, resolveMeters } from '../../core/meter/index.js';\nimport type { NoteEvent } from '../../core/types.js';\nimport {\n assertGenerationBudget,\n assertNoteEvents,\n assertOptions,\n assertRange,\n} from '../../core/validation/index.js';\nimport { BEAT_EPS } from '../adjacency.js';\nimport { barGridStart } from '../grid.js';\nimport type { BarSlice } from './internal.js';\nimport { clamp01, firstSoundingBeat, floorMod, harmonicNovelty, sliceBars } from './internal.js';\n\n/**\n * The bar groupings the search considers.\n *\n * Four is the norm, two and eight its halving and doubling, three and six the\n * triple-hypermetre alternatives. Anything else is better described as an\n * irregular succession of these than as a grouping of its own.\n */\nconst GROUP_CANDIDATES: readonly number[] = [2, 3, 4, 6, 8];\n\n/**\n * How readily each grouping is believed before the music is consulted.\n *\n * The prior only settles ties: a piece that is plainly in three does not become\n * a piece in four because four is commoner. It is what decides a genuinely\n * ambiguous case, where two-bar and four-bar readings explain the accents\n * equally well.\n */\nconst GROUP_PRIOR: ReadonlyMap<number, number> = new Map([\n [2, 0.9],\n [3, 0.75],\n [4, 1],\n [6, 0.7],\n [8, 0.85],\n]);\n\n/** Share of the evidence carried by harmonic change landing on group heads. */\nconst CONTRAST_SHARE = 0.65;\n\n/** Share of the evidence carried by cadences landing at group ends. */\nconst CADENCE_SHARE = 0.35;\n\n/** How much of a group end's fit a cadence arriving in it earns. */\nconst GROUP_FINAL_FIT = 1;\n\n/**\n * How much of a group end's fit an arrival on the next group's head earns.\n *\n * An elided cadence lands on the downbeat that starts the next hyperbar, so the\n * reading is real; it is weaker evidence than a group-final arrival only\n * because a chord on a hypermetric downbeat is unremarkable on its own.\n */\nconst ELIDED_FIT = 0.5;\n\n/**\n * How many hyperbars the span must hold before a grouping is considered.\n *\n * One statement of a grouping is not evidence of a grouping: any span is\n * trivially one group of its own length.\n */\nconst MIN_GROUPS = 2;\n\n/** Share of the confidence that survives a rival grouping scoring just as well. */\nconst MARGIN_FLOOR = 0.7;\n\n/**\n * A bar grouping inferred from the music.\n *\n * @category Arrangement & Analysis\n */\nexport type Hypermeter = {\n /**\n * Bars per hyperbar. Falls back to the span's own bar count when the span is\n * too short to hold two of any candidate grouping.\n */\n groupBars: number;\n /**\n * Absolute beat of each hypermetric downbeat inside the analysed span, in\n * time order. A pickup bar is not a group head, so the first entry is the\n * first full bar that starts a group.\n */\n downbeats: number[];\n /**\n * How much the music supports the grouping, in [0, 1]: how strongly harmonic\n * change and cadences prefer these bars over the others, discounted when the\n * runner-up grouping explains the music nearly as well.\n */\n confidence: number;\n /** Why this grouping was chosen. */\n rationale: string;\n};\n\n/**\n * Options for {@link hypermeter}.\n *\n * @category Arrangement & Analysis\n */\nexport type HypermeterOptions = {\n /**\n * End of the analysed span in beats; defaults to the end of the last note.\n *\n * @defaultValue the end of the last note\n */\n totalBeats?: number;\n /**\n * Beats at which cadences arrive, as {@link CadenceHit.atBeat} reports them.\n *\n * A cadence is the clearest hypermetric evidence there is — it arrives at the\n * end of a hyperbar — but finding one needs a chord timeline and a key, which\n * this function is not given. Callers that already hold cadences should pass\n * them; {@link phrasesFromTimeline} does.\n *\n * @defaultValue none\n */\n cadenceBeats?: readonly number[];\n /**\n * Upper bound on the work this call may do.\n *\n * @defaultValue {@link DEFAULT_GENERATION_BUDGET}\n */\n budget?: number;\n};\n\n/** One grouping-and-phase reading, with the evidence behind it. */\ntype Reading = {\n groupBars: number;\n phase: number;\n contrast: number;\n cadenceFit: number;\n evidence: number;\n score: number;\n};\n\n/**\n * How much more the group heads are accented than the bars between them.\n *\n * The difference of the two means rather than the head mean alone: a piece\n * whose every bar changes harmony accents its group heads no more than anything\n * else, and a reading that cannot tell the two apart has found nothing.\n */\nfunction accentContrast(\n novelty: readonly number[],\n slices: readonly BarSlice[],\n groupBars: number,\n phase: number,\n): number {\n let headSum = 0;\n let headCount = 0;\n let otherSum = 0;\n let otherCount = 0;\n for (let i = 0; i < slices.length; i += 1) {\n const slice = slices[i];\n if (slice === undefined) {\n continue;\n }\n const value = novelty[i] ?? 0;\n if (floorMod(slice.index - phase, groupBars) === 0) {\n headSum += value;\n headCount += 1;\n } else {\n otherSum += value;\n otherCount += 1;\n }\n }\n if (headCount === 0 || otherCount === 0) {\n return 0;\n }\n return headSum / headCount - otherSum / otherCount;\n}\n\n/**\n * Share of the hyperbar ends the reading predicts that a cadence closes.\n *\n * Measured over the group ends rather than over the cadences, because the two\n * count different things: a cadence every four bars sits at a group end under a\n * two-bar reading as readily as under a four-bar one, so counting cadences\n * cannot tell the two apart. Counting group ends can — half the two-bar groups\n * end in nothing.\n */\nfunction cadenceFitOf(\n cadenceBars: ReadonlySet<number>,\n slices: readonly BarSlice[],\n groupBars: number,\n phase: number,\n): number {\n const lastBar = slices[slices.length - 1]?.index ?? -1;\n let ends = 0;\n let fit = 0;\n for (const slice of slices) {\n // A pickup heads no group, so it ends none either.\n if (slice.index < 0 || floorMod(slice.index - phase, groupBars) !== groupBars - 1) {\n continue;\n }\n ends += 1;\n if (cadenceBars.has(slice.index)) {\n fit += GROUP_FINAL_FIT;\n } else if (slice.index < lastBar && cadenceBars.has(slice.index + 1)) {\n // Elision needs a next group to arrive into. Past the last bar the span\n // holds there is none, so a cadence beat beyond the music is not evidence\n // about how the music that was read is grouped.\n fit += ELIDED_FIT;\n }\n }\n return ends === 0 ? 0 : clamp01(fit / ends);\n}\n\n/**\n * The reading a span too short to hold two of any candidate grouping gets.\n *\n * The pickup is dropped before the group is read, exactly as it is on the\n * decided path: an upbeat leads into the first hyperbar rather than heading one,\n * so it neither lengthens the group nor supplies its downbeat. A span in which\n * only the pickup sounds has no hypermetric downbeat at all.\n */\nfunction undecidedReading(slices: readonly BarSlice[]): Hypermeter {\n const fullBars = slices.filter((slice) => slice.index >= 0);\n const groupBars = Math.max(1, fullBars.length);\n const first = fullBars[0];\n return {\n groupBars,\n downbeats: first === undefined ? [] : [first.startBeat],\n confidence: 0,\n rationale: `Span of ${fullBars.length} bar(s) is too short to establish a grouping; read as one group`,\n };\n}\n\n/**\n * Infer how a piece's bars group into hyperbars.\n *\n * Groupings of 2, 3, 4, 6, and 8 bars are tried at every phase, and each\n * reading is scored on two counts: how much more harmonic change lands on the\n * bars it calls group heads than on the bars between them, and how many of the\n * hyperbar ends it predicts are closed by one of the cadences it is given.\n * Where cadences are given they settle the phase, since a hyperbar is what a\n * cadence ends; the harmony then chooses among the phases they agree with. The\n * commonest groupings are believed a little more readily than the rare ones,\n * which settles ties without letting a prior overrule the music.\n *\n * The meter is read at the beat in question rather than at the start, so a\n * piece that changes metre keeps being cut at its own bar lines. Bar 0 is the\n * first full bar however much pickup precedes it, so an upbeat is bar -1 and is\n * never taken for a hypermetric downbeat. Whether there is an upbeat at all is\n * decided the way the rest of the analysis decides it: a note a millibeat early\n * is playing the downbeat, not anticipating it.\n *\n * @param notes The notes to read, in any order. Notes that never sound are\n * dropped.\n * @param meter A single time signature, or the piece's meter map; defaults to\n * 4/4 throughout.\n * @param opts Analysis options; see {@link HypermeterOptions}.\n * @returns The grouping, its downbeats, a confidence, and a rationale.\n * @example\n * ```ts\n * import { hypermeter } from '@libraz/libcantus';\n * const notes = [\n * { pitch: 60, startBeat: 0, durationBeat: 16 },\n * { pitch: 65, startBeat: 16, durationBeat: 16 },\n * ];\n * hypermeter(notes).groupBars; // 4\n * ```\n * @category Arrangement & Analysis\n */\nexport function hypermeter(\n notes: readonly NoteEvent[],\n meter?: MeterLike,\n opts: HypermeterOptions = {},\n): Hypermeter {\n const asked = assertOptions(opts, 'opts');\n // Read once, here: every bar question below is asked of the resolved map\n // rather than of whichever form the caller happened to hold. Absent is the\n // library's default bar; a `null` is a caller that meant to name one.\n const meters = resolveMeters({ meters: meter }, 'meter');\n assertNoteEvents(notes, 'hypermeter notes', {\n allowNonPositiveDuration: true,\n budget: asked.budget,\n });\n const sounding = notes.filter((note) => note.durationBeat > 0);\n const firstOnset = firstSoundingBeat(sounding);\n const lastEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);\n const spanEnd = asked.totalBeats ?? lastEnd;\n assertRange(spanEnd, 0, Number.MAX_SAFE_INTEGER, 'hypermeter totalBeats');\n // Whether the music starts before the downbeat is the analysis-wide pickup\n // question, and the slot grids of chord and key inference answer it here: a\n // note a millibeat early is playing beat 0, and only one a whole grid unit\n // early is an upbeat. Reading a hair-early onset as a pickup would hand the\n // phase search a silent bar of its own to group against. An excerpt that\n // begins later keeps the beat it begins on.\n const spanStart = barGridStart(firstOnset, meters);\n const slices = sliceBars(sounding, meters, spanStart, spanEnd, asked.budget);\n\n // Counted over the bars a grouping can actually occupy, which is the set\n // `undecidedReading` and the downbeats are read from: a pickup leads into the\n // first hyperbar rather than filling one, so it cannot be what makes a span\n // long enough to hold two groups.\n const fullBars = slices.reduce((count, slice) => (slice.index >= 0 ? count + 1 : count), 0);\n const candidates = GROUP_CANDIDATES.filter((groupBars) => fullBars >= groupBars * MIN_GROUPS);\n if (candidates.length === 0) {\n return undecidedReading(slices);\n }\n assertGenerationBudget(\n candidates.reduce((sum, groupBars) => sum + groupBars * slices.length, 0),\n 'hypermeter readings',\n asked.budget,\n );\n\n const novelty = slices.map((_, index) => harmonicNovelty(slices, index));\n const cadenceBars = new Set((asked.cadenceBeats ?? []).map((beat) => barIndexAt(beat, meters)));\n const hasCadences = cadenceBars.size > 0;\n\n const readings: Reading[] = [];\n for (const groupBars of candidates) {\n const phases = Array.from({ length: groupBars }, (_, phase) => ({\n phase,\n contrast: accentContrast(novelty, slices, groupBars, phase),\n cadenceFit: cadenceFitOf(cadenceBars, slices, groupBars, phase),\n }));\n // Where the cadences are known, they decide the phase: a cadence is what a\n // hyperbar ends with, so a reading that leaves them mid-group has misread\n // the phase however the harmony moves. Harmonic contrast then chooses among\n // the phases the cadences agree with, and across the groupings.\n const bestFit = phases.reduce((best, reading) => Math.max(best, reading.cadenceFit), 0);\n for (const { phase, contrast, cadenceFit } of phases) {\n if (hasCadences && cadenceFit < bestFit - BEAT_EPS) {\n continue;\n }\n const evidence = clamp01(\n hasCadences\n ? // Floored at zero rather than summed raw: group heads that restate\n // the harmony their group opened with — the commonest way a phrase\n // begins — change less than the bars between them, and a negative\n // contrast subtracted from the cadence term would count that\n // restatement as evidence against the very phase it confirms.\n CONTRAST_SHARE * clamp01(contrast) + CADENCE_SHARE * cadenceFit\n : contrast,\n );\n readings.push({\n groupBars,\n phase,\n contrast,\n cadenceFit,\n evidence,\n score: evidence * (GROUP_PRIOR.get(groupBars) ?? 0),\n });\n }\n }\n // Ties keep the commoner grouping, then the shorter one, then the earlier\n // phase, so the answer never depends on enumeration order.\n readings.sort(\n (a, b) =>\n b.score - a.score ||\n (GROUP_PRIOR.get(b.groupBars) ?? 0) - (GROUP_PRIOR.get(a.groupBars) ?? 0) ||\n a.groupBars - b.groupBars ||\n a.phase - b.phase,\n );\n const best = readings[0];\n if (best === undefined) {\n return undecidedReading(slices);\n }\n const runnerUp = readings.find((reading) => reading.groupBars !== best.groupBars);\n const margin =\n runnerUp === undefined || best.score <= BEAT_EPS\n ? 1\n : clamp01((best.score - runnerUp.score) / best.score);\n\n // A pickup bar is bar -1, and a phase can put it on a group head — but an\n // upbeat is what leads into the first hyperbar, not what starts one, so it\n // never becomes a hypermetric downbeat however the phase falls.\n const downbeats = slices\n .filter((slice) => slice.index >= 0 && floorMod(slice.index - best.phase, best.groupBars) === 0)\n .map((slice) => slice.startBeat);\n const cadencePart = hasCadences\n ? `${Math.round(best.cadenceFit * 100)}% of group ends closed by a cadence`\n : 'no cadences given';\n\n return {\n groupBars: best.groupBars,\n downbeats,\n // Mostly the evidence, discounted when a rival grouping explains the\n // accents nearly as well. The discount is the smaller term because the\n // runner-up is nearly always a multiple or a divisor of the winner — four\n // bars nest inside eight — and a reading of the same accents at another\n // level is weaker counter-evidence than an unrelated rival would be.\n confidence: clamp01(best.evidence * (MARGIN_FLOOR + (1 - MARGIN_FLOOR) * margin)),\n rationale:\n `${best.groupBars}-bar groups from bar ${best.phase}: harmonic change ` +\n `${Math.round(best.contrast * 100)}% stronger on group heads, ${cadencePart}`,\n };\n}\n","/**\n * What every reading of a melody has to agree on first.\n *\n * A melody is compared, named and classified by three modules here, and all\n * three start from the same two questions: which notes sound, in what order,\n * and what the gaps between their onsets are. Answered separately they would\n * drift — one reading folding a chord to its top voice and another not — so\n * they are answered once, here.\n */\n\nimport type { NoteEvent } from '../../core/types.js';\nimport { assertNoteEvents, assertRecord } from '../../core/validation/index.js';\nimport { BEAT_EPS } from '../adjacency.js';\nimport type { MotifData } from './motifs.js';\n\n/**\n * Grid the rhythmic profile is compared on, as a divisor of the cell's first\n * onset gap.\n *\n * Two statements of a motif have to agree on their rhythm exactly, and floating\n * beat arithmetic does not: 1/3 of a beat written two different ways differs in\n * the last bits. Rounding the ratios onto a grid this fine keeps a dotted figure\n * distinct from an even one while absorbing that error. Onsets are still\n * expected to be quantised — a humanized MIDI take should be quantised before it\n * is searched for motifs.\n */\nconst RHYTHM_GRID = 64;\n/**\n * Anything that can be read as a melodic line: a motif, or plain note events.\n *\n * @category Arrangement & Analysis\n */\nexport type MelodicPhrase = MotifData | readonly NoteEvent[];\n/** The notes of anything that can be read as a line. */\nexport function phraseNotes(phrase: MelodicPhrase): readonly NoteEvent[] {\n if (Array.isArray(phrase)) {\n return phrase;\n }\n // Read for its shape before the `in` test, which is itself an operation a\n // string or a null cannot answer: a phrase reaches these entrances from a\n // caller working in JavaScript as readily as from a `Motif`.\n return assertRecord<{ notes: readonly NoteEvent[] }>(phrase, 'melodic phrase').notes;\n}\n/**\n * The sounding notes of a melody in time order, one note per onset.\n *\n * The line is expected to be monophonic, as {@link analyzeVoice} expects one.\n * Notes struck together are one event all the same, and the highest of them\n * stands for it — the voice a listener follows through a chord. Keeping them\n * all would let a chord read as a line: a triad and the note after it would\n * measure three rising steps of melodic motion the music never made, and would\n * be reported as an ascending line with every bit as much confidence as a real\n * one.\n */\nexport function orderedNotes(\n notes: readonly NoteEvent[],\n name: string,\n budget?: number,\n): readonly NoteEvent[] {\n assertNoteEvents(notes, name, { allowNonPositiveDuration: true, budget });\n const sounding = notes\n .filter((note) => note.durationBeat > 0)\n .sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);\n const line: NoteEvent[] = [];\n for (const note of sounding) {\n const previous = line[line.length - 1];\n // Sorted low to high within an onset, so the last note to arrive at one is\n // its top voice.\n if (previous !== undefined && Math.abs(note.startBeat - previous.startBeat) <= BEAT_EPS) {\n line[line.length - 1] = note;\n continue;\n }\n line.push(note);\n }\n return line;\n}\n/** Semitones between consecutive notes. */\nexport function intervalsOf(notes: readonly NoteEvent[]): number[] {\n const out: number[] = [];\n for (let i = 1; i < notes.length; i += 1) {\n out.push((notes[i]?.pitch ?? 0) - (notes[i - 1]?.pitch ?? 0));\n }\n return out;\n}\n/** Onset-to-onset gaps between consecutive notes. */\nexport function onsetGaps(notes: readonly NoteEvent[]): number[] {\n const out: number[] = [];\n for (let i = 1; i < notes.length; i += 1) {\n out.push((notes[i]?.startBeat ?? 0) - (notes[i - 1]?.startBeat ?? 0));\n }\n return out;\n}\n/**\n * The onset gaps a line read back to front would have.\n *\n * Playing a cell backwards keeps each note as long as it was, so the gap that\n * opens before a note in the retrograde is the gap that followed it in the\n * model — the distance between the two notes' ends, not between their onsets.\n * The two coincide only when every note is the same length, which is why the\n * onset gaps reversed cannot stand in for this.\n */\nexport function retrogradeGaps(notes: readonly NoteEvent[]): number[] {\n const out: number[] = [];\n for (let i = notes.length - 1; i >= 1; i -= 1) {\n const later = notes[i];\n const earlier = notes[i - 1];\n out.push(\n (later?.startBeat ?? 0) +\n (later?.durationBeat ?? 0) -\n ((earlier?.startBeat ?? 0) + (earlier?.durationBeat ?? 0)),\n );\n }\n return out;\n}\n/**\n * Onset gaps as multiples of the first gap, rounded onto the comparison grid.\n *\n * Gaps rather than durations, because how long a note is held is a matter of\n * articulation — the same figure played staccato and legato is the same figure —\n * while where the next note falls is the rhythm itself. Normalising by the first\n * gap is what lets a statement in doubled note values match the motif it doubles;\n * the stretch is reported separately as a time ratio.\n *\n * A cell whose first gap is not positive has no profile to normalise by, and\n * answers null.\n */\nexport function rhythmProfile(gaps: readonly number[]): number[] | null {\n const unit = gaps[0] ?? 0;\n if (!(unit > BEAT_EPS)) {\n return gaps.length === 0 ? [] : null;\n }\n return gaps.map((gap) => Math.round((gap / unit) * RHYTHM_GRID) / RHYTHM_GRID);\n}\n/** Compare two numeric sequences elementwise. */\nexport function sameNumbers(a: readonly number[], b: readonly number[]): boolean {\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i += 1) {\n if (Math.abs((a[i] ?? 0) - (b[i] ?? 0)) > BEAT_EPS) {\n return false;\n }\n }\n return true;\n}\n/** Round for display, so a rationale never carries floating-point noise. */\nexport function roundTo(value: number, digits: number): number {\n const scale = 10 ** digits;\n return Math.round(value * scale) / scale;\n}\n/** Beat of the last note's end. */\nexport function endBeatOf(notes: readonly NoteEvent[]): number {\n let end = 0;\n for (const note of notes) {\n end = Math.max(end, note.startBeat + note.durationBeat);\n }\n return end;\n}\n/** Distance from the first onset to the last, which is what a stretch scales. */\nexport function onsetSpan(notes: readonly NoteEvent[]): number {\n if (notes.length < 2) {\n return 0;\n }\n return (notes[notes.length - 1]?.startBeat ?? 0) - (notes[0]?.startBeat ?? 0);\n}\n","/**\n * The shape of a line: which way it moves, and what that adds up to.\n *\n * A contour is the melody with its pitches forgotten — the sequence of ups and\n * downs alone — which is what lets a phrase be called an arch whatever key it\n * is in and whatever intervals it is built from.\n */\n\nimport type { NoteEvent } from '../../core/types.js';\nimport type { MelodicPhrase } from './internal.js';\nimport { intervalsOf, orderedNotes, phraseNotes } from './internal.js';\n\n/**\n * Share of a line's total motion that has to run one way before the line is\n * called ascending, descending, or an arch.\n *\n * A melody that climbs an octave with one dip on the way is heard as ascending,\n * so the classification is a dominance test rather than a strict-monotonicity\n * one. Below this share the line is reported as a wave, which is the honest\n * answer for something that changes direction repeatedly.\n */\nconst DIRECTIONAL_SHARE = 0.75;\n/**\n * Step direction between two consecutive notes.\n *\n * @category Arrangement & Analysis\n */\nexport type ContourDirection = 'up' | 'down' | 'same';\n/**\n * The overall shape of a line.\n *\n * The first four names are the vocabulary the motif generator's contours are\n * written in, and mean the same here. `'static'` is the shape a generator never\n * asks for and an analysis regularly meets: a line that does not move. A line\n * that dips and comes back — an arch upside down — reads as `'wave'`, since the\n * shared vocabulary has no name of its own for it.\n *\n * A wave has to turn more than once to be heard as one, so a cell the generator\n * writes for `'wave'` reads back as `'wave'` only from three bars up; the one-\n * and two-bar cells turn once and read as `'arch'`. The other three shapes come\n * back under their own name at every length.\n *\n * @category Arrangement & Analysis\n */\nexport type MelodicContourShape = 'arch' | 'ascending' | 'descending' | 'wave' | 'static';\n/**\n * The abstract shape of a line: its step directions and what they add up to.\n *\n * @category Arrangement & Analysis\n */\nexport type MelodicContour = {\n /** One direction per adjacent pair of notes, in time order. */\n directions: ContourDirection[];\n /** What the directions add up to. */\n shape: MelodicContourShape;\n /** Index of the highest note; ties keep the earliest, and -1 for a line with no notes. */\n peakIndex: number;\n /** Index of the lowest note; ties keep the earliest, and -1 for a line with no notes. */\n troughIndex: number;\n /** Semitone distance from the lowest note to the highest. */\n range: number;\n /** Why the line was given that shape. */\n rationale: string;\n};\n/**\n * Where the highest and lowest notes of a line sit; ties keep the earliest.\n *\n * A line with no notes has neither, and answers -1 for both rather than 0,\n * which would name a note the caller cannot reach.\n */\nfunction extremes(notes: readonly NoteEvent[]): { peak: number; trough: number } {\n if (notes.length === 0) {\n return { peak: -1, trough: -1 };\n }\n let peak = 0;\n let trough = 0;\n for (let i = 1; i < notes.length; i += 1) {\n if ((notes[i]?.pitch ?? 0) > (notes[peak]?.pitch ?? 0)) {\n peak = i;\n }\n if ((notes[i]?.pitch ?? 0) < (notes[trough]?.pitch ?? 0)) {\n trough = i;\n }\n }\n return { peak, trough };\n}\n/**\n * Read the shape of a line.\n *\n * The step directions are the literal answer; the shape is what they add up to.\n * A line is called ascending or descending when most of its motion runs one way,\n * not only when every step does — a climb with one dip in it is still heard as a\n * climb — and an arch when it rises to an interior peak and comes back down.\n * Anything that keeps changing direction is a wave, and a line that does not move\n * is static.\n *\n * The line is expected to be monophonic, as {@link analyzeVoice} expects a voice\n * to be; split polyphonic material into lines first. Notes struck together are\n * read as one event, the highest of them standing for it — a chord and the note\n * after it trace one step, not a climb through the chord — so a piano part read\n * whole answers for its top voice rather than for a line no one plays.\n *\n * @param notes The line: a motif, or plain note events. Notes that never sound\n * are dropped, and notes sharing an onset are folded to their top voice.\n * @returns The directions, the shape, the peak and trough, and a rationale. A\n * line with no sounding notes is static with a range of 0, and its peak and\n * trough are -1: there is no note for them to point at.\n * @example\n * ```ts\n * import { melodicContour } from '@libraz/libcantus';\n * const notes = [\n * { pitch: 60, startBeat: 0, durationBeat: 1 },\n * { pitch: 64, startBeat: 1, durationBeat: 1 },\n * { pitch: 60, startBeat: 2, durationBeat: 1 },\n * ];\n * melodicContour(notes).shape; // 'arch'\n * ```\n * @category Arrangement & Analysis\n */\nexport function melodicContour(notes: MelodicPhrase): MelodicContour {\n const line = orderedNotes(phraseNotes(notes), 'contour notes');\n const intervals = intervalsOf(line);\n const directions: ContourDirection[] = intervals.map((step) =>\n step > 0 ? 'up' : step < 0 ? 'down' : 'same',\n );\n const { peak, trough } = extremes(line);\n const range = (line[peak]?.pitch ?? 0) - (line[trough]?.pitch ?? 0);\n const motion = intervals.reduce((sum, step) => sum + Math.abs(step), 0);\n if (motion === 0) {\n return {\n directions,\n shape: 'static',\n peakIndex: peak,\n troughIndex: trough,\n range: 0,\n rationale:\n line.length < 2\n ? 'Too short to have a direction, so the line is static'\n : 'Every note at the same pitch, so the line is static',\n };\n }\n\n const first = line[0]?.pitch ?? 0;\n const last = line[line.length - 1]?.pitch ?? 0;\n const net = last - first;\n const directness = Math.abs(net) / motion;\n const share = Math.round(directness * 100);\n if (directness >= DIRECTIONAL_SHARE) {\n const shape = net > 0 ? 'ascending' : 'descending';\n return {\n directions,\n shape,\n peakIndex: peak,\n troughIndex: trough,\n range,\n rationale: `${share}% of the motion runs ${net > 0 ? 'up' : 'down'}, so the line is ${shape}`,\n };\n }\n\n const peakPitch = line[peak]?.pitch ?? 0;\n const archMotion = peakPitch - first + (peakPitch - last);\n if (peak > 0 && peak < line.length - 1 && archMotion >= DIRECTIONAL_SHARE * motion) {\n return {\n directions,\n shape: 'arch',\n peakIndex: peak,\n troughIndex: trough,\n range,\n rationale: `Rises to its peak at note ${peak + 1} of ${line.length} and falls back, so the line is an arch`,\n };\n }\n return {\n directions,\n shape: 'wave',\n peakIndex: peak,\n troughIndex: trough,\n range,\n rationale: `Changes direction without settling either way (${share}% of the motion nets out), so the line is a wave`,\n };\n}\n","/**\n * The recurring cells a melody is built from.\n *\n * A motif is the interval contour plus the rhythmic profile, so every statement\n * of it carries the same identity however far it has been transposed or however\n * wide its note values are written. What this module does is find them: every\n * window of the melody is reduced to that identity, windows that agree are\n * grouped, and a group stated often enough is a motif.\n */\n\nimport { InvalidInputError } from '../../core/errors/index.js';\nimport type { NoteEvent } from '../../core/types.js';\nimport {\n assertGenerationBudget,\n assertInteger,\n assertOptions,\n} from '../../core/validation/index.js';\nimport { BEAT_EPS } from '../adjacency.js';\nimport { melodicContour } from './contour.js';\nimport {\n endBeatOf,\n intervalsOf,\n onsetGaps,\n onsetSpan,\n orderedNotes,\n rhythmProfile,\n roundTo,\n} from './internal.js';\n\n/**\n * Shortest cell the search reports, in notes.\n *\n * Two notes carry a single interval, and a lone rising fourth recurs in almost\n * any melody without naming anything. Three notes are the shortest cell with a\n * shape: two intervals, so it can turn.\n */\nconst MIN_CELL_NOTES = 3;\n/**\n * Longest cell the search reports, in notes.\n *\n * Eight notes span two bars of eighths, or four bars of quarters. Recurring\n * material longer than that is a phrase rather than a motif and wants a\n * phrase-level reading; the bound also keeps the number of windows examined\n * linear in the length of the melody.\n */\nconst MAX_CELL_NOTES = 8;\n/**\n * How many statements make a cell a motif.\n *\n * A motif is by definition restated, so two is the floor. Raising it is how a\n * caller asks for only the material the piece is actually built from.\n */\nconst MIN_OCCURRENCES = 2;\n/**\n * One statement of a motif, addressed in the melody it was found in.\n *\n * The notes themselves are not repeated here: a statement is the motif's notes\n * moved by `transpose` and stretched by `timeRatio`, and `noteIndex` addresses\n * the original array for a caller that wants them exactly.\n *\n * @category Arrangement & Analysis\n */\nexport type MotifOccurrence = {\n /**\n * Index of the statement's first note among the sounding notes of the melody,\n * in time order. Notes that never sound are dropped before the search, so this\n * is not an index into the caller's array unless the caller's array has none.\n */\n noteIndex: number;\n /** Onset of the statement's first note. */\n startBeat: number;\n /** End of the statement's last note, exclusive. */\n endBeat: number;\n /** Semitones this statement stands above the motif's first statement. */\n transpose: number;\n /**\n * How much this statement is stretched against the motif's first: 2 for a\n * statement in doubled note values, 0.5 for one in halved values, 1 for one in\n * the same values.\n */\n timeRatio: number;\n};\n/**\n * A recurring melodic cell.\n *\n * Identity is the interval sequence plus the rhythmic profile, so every\n * statement of the motif carries the same `intervals` and `rhythm` however far\n * it has been transposed or however wide its note values are written. `notes`\n * holds one statement — the first — exactly as it sounds.\n *\n * @category Arrangement & Analysis\n */\nexport type MotifData = {\n /** The first statement, exactly as it sounds in the melody. */\n notes: NoteEvent[];\n /**\n * Semitones between consecutive notes. This is the transposition-invariant\n * identity of the cell: two statements a fifth apart share it.\n */\n intervals: number[];\n /**\n * Onset-to-onset gaps as multiples of the first gap, so an even cell reads\n * `[1, 1, 1]` whatever the tempo or note values it is written in.\n */\n rhythm: number[];\n /** Every statement found, in time order; the first is `notes`. */\n occurrences: MotifOccurrence[];\n /** Why this counts as a motif. */\n rationale: string;\n};\n/**\n * Options controlling {@link extractMotifs}.\n *\n * @category Arrangement & Analysis\n */\nexport type ExtractMotifsOptions = {\n /**\n * Shortest cell to report, in notes.\n *\n * @defaultValue 3\n */\n minNotes?: number;\n /**\n * Longest cell to report, in notes.\n *\n * @defaultValue 8\n */\n maxNotes?: number;\n /**\n * How many statements a cell needs before it is reported.\n *\n * @defaultValue 2\n */\n minOccurrences?: number;\n /**\n * Upper bound on the work this call may do.\n *\n * @defaultValue {@link DEFAULT_GENERATION_BUDGET}\n */\n budget?: number;\n};\n/**\n * The identity of one window: its intervals and its rhythmic profile.\n *\n * Two windows sharing this string are the same cell, wherever they sit and\n * whatever pitch level they sit at. A window with no usable rhythmic profile has\n * no identity and answers null.\n */\nfunction windowSignature(notes: readonly NoteEvent[]): string | null {\n const profile = rhythmProfile(onsetGaps(notes));\n if (profile === null) {\n return null;\n }\n return `${notes.length}|${intervalsOf(notes).join(',')}|${profile.join(',')}`;\n}\n/** A group of identical windows, before it is decided whether it is a motif. */\ntype WindowGroup = {\n signature: string;\n length: number;\n /** Index of the first note of each window, ascending. */\n starts: number[];\n};\n/**\n * Keep the statements that do not overlap, earliest first.\n *\n * A cell of even notes matches itself one note along, so the raw matches of a\n * repetitive figure overlap heavily; counting those as separate statements would\n * report a motif as recurring far more often than it is heard to.\n */\nfunction nonOverlapping(starts: readonly number[], length: number): number[] {\n const picked: number[] = [];\n let lastEnd = Number.NEGATIVE_INFINITY;\n for (const start of starts) {\n if (start > lastEnd) {\n picked.push(start);\n lastEnd = start + length - 1;\n }\n }\n return picked;\n}\n/**\n * Whether every statement of `inner` sits inside one statement of `outer`.\n *\n * Both runs of onsets are ascending, so the two are walked together rather than\n * searched: the statement of `outer` that can hold an inner one is the latest\n * that starts at or before it, since a later start reaches further right.\n */\nfunction subsumes(\n outer: { starts: number[]; length: number },\n inner: { starts: number[]; length: number },\n): boolean {\n let at = 0;\n for (const start of inner.starts) {\n while (at + 1 < outer.starts.length && (outer.starts[at + 1] ?? 0) <= start) {\n at += 1;\n }\n const from = outer.starts[at];\n if (from === undefined || start < from || start + inner.length > from + outer.length) {\n return false;\n }\n }\n return true;\n}\n/** List the statement onsets for a rationale without letting it run away. */\nfunction listBeats(occurrences: readonly MotifOccurrence[]): string {\n const shown = occurrences.slice(0, 5).map((occurrence) => roundTo(occurrence.startBeat, 3));\n return occurrences.length > shown.length\n ? `${shown.join(', ')}, and ${occurrences.length - shown.length} more`\n : shown.join(', ');\n}\n/** Build the motif a group of identical windows stands for. */\nfunction motifFromGroup(group: WindowGroup, notes: readonly NoteEvent[]): MotifData {\n const first = group.starts[0] ?? 0;\n const prime = notes.slice(first, first + group.length).map((note) => ({ ...note }));\n const primePitch = prime[0]?.pitch ?? 0;\n const primeSpan = onsetSpan(prime);\n const occurrences: MotifOccurrence[] = group.starts.map((start) => {\n const statement = notes.slice(start, start + group.length);\n const span = onsetSpan(statement);\n return {\n noteIndex: start,\n startBeat: statement[0]?.startBeat ?? 0,\n endBeat: endBeatOf(statement),\n transpose: (statement[0]?.pitch ?? 0) - primePitch,\n timeRatio: primeSpan > BEAT_EPS ? span / primeSpan : 1,\n };\n });\n const shape = melodicContour(prime).shape;\n const transposed = occurrences.some((occurrence) => occurrence.transpose !== 0);\n const stretched = occurrences.some((occurrence) => Math.abs(occurrence.timeRatio - 1) > BEAT_EPS);\n const detail = [\n transposed ? 'at more than one pitch level' : 'at the same pitch level',\n stretched ? 'and in more than one set of note values' : '',\n ]\n .filter((part) => part !== '')\n .join(' ');\n return {\n notes: prime,\n intervals: intervalsOf(prime),\n rhythm: rhythmProfile(onsetGaps(prime)) ?? [],\n occurrences,\n rationale:\n `${group.length}-note ${shape} cell stated ${occurrences.length} ` +\n `${occurrences.length === 1 ? 'time' : 'times'} (beats ${listBeats(occurrences)}), ${detail}`,\n };\n}\n/**\n * Find the cells a melody keeps coming back to.\n *\n * Every window of `minNotes` to `maxNotes` consecutive notes is reduced to its\n * interval sequence and its rhythmic profile, and windows sharing both are\n * statements of one motif — so a restatement a fifth higher, or one written in\n * doubled note values, is found as the same cell rather than as a new one.\n * Overlapping statements of a repetitive figure are counted once, and a cell\n * whose statements all sit inside the statements of a longer cell that recurs at\n * least as often is dropped, so the answer names the longest thing that recurs\n * rather than every fragment of it.\n *\n * The melody is expected to be monophonic, as {@link analyzeVoice} expects a\n * voice to be; split polyphonic material into lines first. Notes struck together\n * are read as one event, the highest of them standing for it, so a chord is\n * reduced to its top voice rather than read as a line of its own. Onsets are\n * expected to be quantised, since two statements must agree on their rhythm to\n * be recognised as one motif.\n *\n * @param notes The melody, in time order. Notes that never sound are dropped.\n * @param opts Cell-length bounds and the recurrence threshold; see\n * {@link ExtractMotifsOptions}.\n * @returns The motifs found, longest first, then by how often they recur, then\n * by where they first appear. Empty when nothing recurs.\n * @example\n * ```ts\n * import { extractMotifs } from '@libraz/libcantus';\n * const notes = [\n * { pitch: 60, startBeat: 0, durationBeat: 1 },\n * { pitch: 62, startBeat: 1, durationBeat: 1 },\n * { pitch: 64, startBeat: 2, durationBeat: 1 },\n * { pitch: 67, startBeat: 4, durationBeat: 1 },\n * { pitch: 69, startBeat: 5, durationBeat: 1 },\n * { pitch: 71, startBeat: 6, durationBeat: 1 },\n * ];\n * const motifs = extractMotifs(notes);\n * motifs[0]?.occurrences.map((o) => o.startBeat); // [0, 4]\n * ```\n * @category Arrangement & Analysis\n */\nexport function extractMotifs(\n notes: readonly NoteEvent[],\n opts: ExtractMotifsOptions = {},\n): MotifData[] {\n const asked = assertOptions(opts, 'opts');\n const minNotes = assertInteger(asked.minNotes ?? MIN_CELL_NOTES, 'motif minNotes', 2, 64);\n const maxNotes = assertInteger(asked.maxNotes ?? MAX_CELL_NOTES, 'motif maxNotes', 2, 64);\n if (maxNotes < minNotes) {\n throw new InvalidInputError(\n `motif maxNotes must be at least minNotes ${minNotes}; received ${maxNotes}`,\n );\n }\n const minOccurrences = assertInteger(\n asked.minOccurrences ?? MIN_OCCURRENCES,\n 'motif minOccurrences',\n 2,\n );\n const sounding = orderedNotes(notes, 'melody notes', asked.budget);\n if (sounding.length < minNotes) {\n return [];\n }\n assertGenerationBudget(\n sounding.length * (maxNotes - minNotes + 1),\n 'motif windows',\n asked.budget,\n );\n\n const groups = new Map<string, WindowGroup>();\n for (let length = minNotes; length <= maxNotes; length += 1) {\n for (let start = 0; start + length <= sounding.length; start += 1) {\n const signature = windowSignature(sounding.slice(start, start + length));\n if (signature === null) {\n continue;\n }\n const group = groups.get(signature);\n if (group === undefined) {\n groups.set(signature, { signature, length, starts: [start] });\n } else {\n group.starts.push(start);\n }\n }\n }\n\n const candidates = [...groups.values()]\n .map((group) => ({ ...group, starts: nonOverlapping(group.starts, group.length) }))\n .filter((group) => group.starts.length >= minOccurrences)\n // Longest first, then most often stated, then earliest; the signature settles\n // any remaining tie so the order never depends on the map's insertion order.\n .sort(\n (a, b) =>\n b.length - a.length ||\n b.starts.length - a.starts.length ||\n (a.starts[0] ?? 0) - (b.starts[0] ?? 0) ||\n (a.signature < b.signature ? -1 : a.signature > b.signature ? 1 : 0),\n );\n\n // A candidate is dropped when a longer motif already kept states it inside\n // every one of its own statements, so the only groups worth asking are the\n // ones whose statements cover the candidate's first onset — a window that\n // misses that onset cannot hold all of them. One window of a given length\n // starting at a given note is one cell, so the kept groups are indexed by\n // exactly that and the candidate probes the few starts that could reach it,\n // instead of every motif kept so far. A melody that states its verse and\n // chorus twice keeps tens of thousands of groups, and asking each candidate\n // about all of them is what made the pass take seconds on one track.\n const keptByLength = new Map<number, Map<number, WindowGroup>>();\n const kept: WindowGroup[] = [];\n let comparisons = 0;\n for (const candidate of candidates) {\n const first = candidate.starts[0] ?? 0;\n let covered = false;\n for (let length = candidate.length + 1; length <= maxNotes && !covered; length += 1) {\n const byStart = keptByLength.get(length);\n if (byStart === undefined) {\n continue;\n }\n for (let from = Math.max(0, first - (length - candidate.length)); from <= first; from += 1) {\n comparisons += 1;\n const outer = byStart.get(from);\n if (outer === undefined || outer.starts.length < candidate.starts.length) {\n continue;\n }\n comparisons += outer.starts.length + candidate.starts.length;\n if (subsumes(outer, candidate)) {\n covered = true;\n break;\n }\n }\n }\n // Charged as it is spent rather than estimated: what the pass costs is the\n // statements the candidates hold, which the window count alone does not say.\n assertGenerationBudget(comparisons, 'motif subsumption comparisons', asked.budget);\n if (!covered) {\n kept.push(candidate);\n let byStart = keptByLength.get(candidate.length);\n if (byStart === undefined) {\n byStart = new Map<number, WindowGroup>();\n keptByLength.set(candidate.length, byStart);\n }\n for (const start of candidate.starts) {\n byStart.set(start, candidate);\n }\n }\n }\n return kept.map((group) => motifFromGroup(group, sounding));\n}\n/**\n * Read a run of notes as a single-statement motif.\n *\n * {@link extractMotifs} answers with motifs it found recurring; this is how a\n * caller names one it already has — a subject, an answer, a phrase lifted out of\n * a score — so that it can be handed to {@link relateMotifs}.\n *\n * The cell is expected to be monophonic, as {@link analyzeVoice} expects a voice\n * to be. Notes struck together are read as one event, the highest of them\n * standing for it, so a chord is reduced to its top voice rather than read as a\n * line of its own.\n *\n * @param notes The cell, in time order. Notes that never sound are dropped, and\n * notes sharing an onset are folded to their top voice.\n * @returns The motif, carrying exactly one occurrence.\n * @example\n * ```ts\n * import { motifFromNotes, relateMotifs } from '@libraz/libcantus';\n * const subject = motifFromNotes([\n * { pitch: 60, startBeat: 0, durationBeat: 1 },\n * { pitch: 62, startBeat: 1, durationBeat: 1 },\n * { pitch: 64, startBeat: 2, durationBeat: 1 },\n * ]);\n * const answer = motifFromNotes([\n * { pitch: 67, startBeat: 3, durationBeat: 1 },\n * { pitch: 69, startBeat: 4, durationBeat: 1 },\n * { pitch: 71, startBeat: 5, durationBeat: 1 },\n * ]);\n * relateMotifs(subject, answer)?.kind; // 'transposition'\n * ```\n * @category Arrangement & Analysis\n */\nexport function motifFromNotes(notes: readonly NoteEvent[]): MotifData {\n const sounding = orderedNotes(notes, 'motif notes').map((note) => ({ ...note }));\n return motifFromGroup(\n { signature: windowSignature(sounding) ?? '', length: sounding.length, starts: [0] },\n sounding,\n );\n}\n","/**\n * The transformation between two statements of a motif, named.\n *\n * Two cells stand in a relation when one is the other put through a device the\n * repertoire has a name for — repetition, transposition, inversion, retrograde,\n * augmentation — and naming it is what turns a pair of similar phrases into an\n * account of how the second was made from the first.\n */\n\nimport type { SpelledInterval } from '../../core/pitch/index.js';\nimport { midiToNote, pitchClassOf as pitchClass, spelledInterval } from '../../core/pitch/index.js';\nimport type { KeyScale, NoteEvent } from '../../core/types.js';\nimport { assertArray, assertRecord } from '../../core/validation/index.js';\nimport {\n keySignatureFifths,\n type ResolvedKey,\n resolveKey,\n type SpelledKeyLike,\n scaleTonesInDegreeOrder,\n} from '../../theory/scale/index.js';\nimport { BEAT_EPS, HUMANIZE_ADJACENCY } from '../adjacency.js';\nimport {\n endBeatOf,\n intervalsOf,\n onsetGaps,\n retrogradeGaps,\n rhythmProfile,\n roundTo,\n sameNumbers,\n} from './internal.js';\nimport type { MotifData } from './motifs.js';\n\n/**\n * How one statement of a motif stands to another.\n *\n * `'transposition'` is the literal one — every interval preserved, which is what\n * makes a sequence built on it a *real* sequence. `'tonalTransposition'` shifts\n * by scale degrees instead and lets the key resize the intervals, which is what\n * makes a sequence built on it a *tonal* sequence; the two are different devices\n * and are never reported as one. `'augmentation'` and `'diminution'` name the\n * relation only when the pitches are untouched, so an inversion in doubled note\n * values reads as `'inversion'` with a `timeRatio` of 2 rather than losing the\n * inversion to the stretch.\n *\n * @category Arrangement & Analysis\n */\nexport type MotifRelationKind =\n | 'repetition'\n | 'transposition'\n | 'tonalTransposition'\n | 'inversion'\n | 'retrograde'\n | 'retrogradeInversion'\n | 'augmentation'\n | 'diminution';\n/**\n * The transformation that turns one motif into another.\n *\n * @category Arrangement & Analysis\n */\nexport type MotifRelation = {\n /** The named transformation. */\n kind: MotifRelationKind;\n /**\n * Whether the second statement begins where the first ends, which is what\n * separates a sequence from a restatement elsewhere in the piece: a\n * `'transposition'` that is one is a real sequence, a `'tonalTransposition'`\n * that is one is a tonal sequence.\n */\n sequence: boolean;\n /** Semitones from the first statement's first note to the second's. */\n semitones: number;\n /** The same distance named as a spelled interval. */\n interval: SpelledInterval;\n /** Scale degrees the second statement stands above the first, when tonal. */\n degrees?: number;\n /**\n * How much the second statement is stretched against the first: 2 for doubled\n * note values, 0.5 for halved, 1 for unchanged.\n *\n * Measured in the direction the relation names, so a retrograde that kept\n * every note value reports 1 however uneven those values are: it is compared\n * against the model played backwards, not against the model as written.\n */\n timeRatio: number;\n /** Why the pair was given that name. */\n rationale: string;\n};\n/** Whether a key writes its accidentals as flats. */\nfunction spellingOf(key?: ResolvedKey): 'sharp' | 'flat' {\n if (key === undefined) {\n return 'sharp';\n }\n return keySignatureFifths(key.tonic, key.scale) < 0 ? 'flat' : 'sharp';\n}\n/** Name the distance between two pitches as a spelled interval. */\nfunction intervalBetween(from: number, to: number, key?: ResolvedKey): SpelledInterval {\n const spelling = spellingOf(key);\n return spelledInterval(midiToNote(from, spelling), midiToNote(to, spelling));\n}\n/** An interval name in the grammar `parseInterval` reads, such as `-m3`. */\nfunction intervalName(interval: SpelledInterval): string {\n return `${interval.descending && interval.semitones !== 0 ? '-' : ''}${interval.quality}${interval.number}`;\n}\n/**\n * Position of every note on the key's diatonic ladder, counted in scale degrees\n * from the root and continuing across octaves.\n *\n * This is what a tonal transposition moves by, and it only exists for a line\n * that stays in the key: one chromatic note and the answer is null, because a\n * degree cannot be named for a pitch the scale does not contain.\n */\nfunction ladderIndices(notes: readonly NoteEvent[], key: KeyScale): number[] | null {\n const root = pitchClass(key.rootPc);\n const offsets = scaleTonesInDegreeOrder(key).map((pc) => pitchClass(pc - root));\n if (offsets.length === 0) {\n return null;\n }\n const out: number[] = [];\n for (const note of notes) {\n const semitones = note.pitch - root;\n const octave = Math.floor(semitones / 12);\n const degree = offsets.indexOf(semitones - octave * 12);\n if (degree < 0) {\n return null;\n }\n out.push(octave * offsets.length + degree);\n }\n return out;\n}\n/** Differences between consecutive ladder positions. */\nfunction ladderSteps(indices: readonly number[]): number[] {\n const out: number[] = [];\n for (let i = 1; i < indices.length; i += 1) {\n out.push((indices[i] ?? 0) - (indices[i - 1] ?? 0));\n }\n return out;\n}\n/** The pitch shapes one motif can stand in to another, before timing. */\ntype PitchShape = 'exact' | 'inversion' | 'retrograde' | 'retrogradeInversion';\n/**\n * Which pitch transformations carry `a`'s intervals onto `b`'s, simplest first.\n *\n * A pair can answer to more than one name at once — the retrograde of a cell\n * whose intervals read the same upside down is also its inversion — and the\n * pitches cannot say which name is the pair's. Only the rhythm can: an\n * inversion runs the same way round as its model and a retrograde runs\n * backwards. So every shape that holds is reported and the caller, which\n * already knows which way round the rhythm runs, picks among them; answering\n * with the simplest alone would leave a cell like that unnamed whenever its\n * rhythm rules the simplest name out.\n *\n * Only the readings that survive without a key are here. The tonal one is\n * decided by the caller of this, since it needs the scale, and a pattern that\n * is a tonal transposition and a retrograde inversion at once reaches here as\n * the second of those.\n */\nfunction pitchShapes(a: readonly number[], b: readonly number[]): PitchShape[] {\n const shapes: PitchShape[] = [];\n if (sameNumbers(a, b)) {\n shapes.push('exact');\n }\n if (\n sameNumbers(\n b,\n a.map((step) => -step),\n )\n ) {\n shapes.push('inversion');\n }\n const reversed = [...a].reverse();\n if (\n sameNumbers(\n b,\n reversed.map((step) => -step),\n )\n ) {\n shapes.push('retrograde');\n }\n if (sameNumbers(b, reversed)) {\n shapes.push('retrogradeInversion');\n }\n return shapes;\n}\n/** Total distance a run of gaps covers. */\nfunction totalGap(gaps: readonly number[]): number {\n return gaps.reduce((sum, gap) => sum + gap, 0);\n}\n/**\n * How far the answer's note values are stretched against the model's, measured\n * in the direction the relation names.\n *\n * A retrograde is measured against the model played backwards, whose gaps are\n * the distances between note ends. Measuring one forwards instead reports a\n * stretch for an answer that kept every note value — a search for augmentations\n * then picks up a plain retrograde of an uneven cell — because the two ways of\n * measuring coincide only when every note is the same length.\n */\nfunction stretchRatio(modelGaps: readonly number[], answerGaps: readonly number[]): number {\n const span = totalGap(modelGaps);\n return span > BEAT_EPS ? totalGap(answerGaps) / span : 1;\n}\n/** Sentence naming a stretch, for the rationales that mention one. */\nfunction stretchPhrase(ratio: number): string {\n return `note values ${roundTo(ratio, 3)}x the model's`;\n}\n/**\n * Name how two statements of a motif relate.\n *\n * The two are compared as shapes, not as pitches: the interval sequences say\n * which transformations could name the pair and the onset gaps say which of\n * them does, so a statement a fourth higher in doubled note values is\n * recognised for what it is, and a cell that reads as both an inversion and a\n * retrograde is named by the way its rhythm runs rather than by which name came\n * to hand first. A pair whose rhythms correspond in neither direction — the same\n * way round for a repetition or a transposition, the model played backwards for\n * a retrograde, note values and all — is not a transformation of the motif but a\n * different figure, and answers null; {@link melodicSimilarity} is what scores\n * those.\n *\n * With a `key` in hand the search also asks whether the second statement is the\n * first moved by scale degrees rather than by semitones. That is the tonal\n * answer to the real one, and the two are kept apart: a real transposition keeps\n * every interval literally, a tonal one lets the key resize them, so a subject\n * answered a step higher reads as `'transposition'` when its major second stays\n * major and as `'tonalTransposition'` when the scale turns it minor.\n *\n * @param a The model statement.\n * @param b The statement to name against it.\n * @param keyLike Key context for the tonal reading. Without one that reading is\n * unavailable, and a statement holding only diatonically is left unnamed\n * unless its interval pattern also fits a member of the retrograde family, in\n * which case it is named there: a triad restated a degree higher swaps two\n * adjacent interval sizes, which is what a retrograde inversion does to it as\n * well, and equal note values read the same way round in both directions, so\n * the notes alone cannot separate the two. A key separates them.\n * @returns The relation, or null when the two stand in none.\n * @example\n * ```ts\n * import { majorKey, motifFromNotes, relateMotifs } from '@libraz/libcantus';\n * const model = motifFromNotes([\n * { pitch: 60, startBeat: 0, durationBeat: 1 },\n * { pitch: 62, startBeat: 1, durationBeat: 1 },\n * { pitch: 64, startBeat: 2, durationBeat: 1 },\n * ]);\n * const answer = motifFromNotes([\n * { pitch: 62, startBeat: 3, durationBeat: 1 },\n * { pitch: 64, startBeat: 4, durationBeat: 1 },\n * { pitch: 65, startBeat: 5, durationBeat: 1 },\n * ]);\n * relateMotifs(model, answer, majorKey(0))?.kind; // 'tonalTransposition'\n * ```\n * @category Arrangement & Analysis\n */\nexport function relateMotifs(\n a: MotifData,\n b: MotifData,\n keyLike?: SpelledKeyLike,\n): MotifRelation | null {\n // Read whole, so the interval names a motif relation is reported under follow\n // the key the caller named rather than the side its pitch classes read best as.\n const key = keyLike === undefined ? undefined : resolveKey(keyLike);\n // Both motifs are walked note by note below, so both are read as motifs\n // first: a bare field access on a malformed one reports this library's own\n // `TypeError` instead of naming the argument that was wrong.\n const model = assertArray<NoteEvent>(assertRecord<MotifData>(a, 'a').notes, 'a.notes');\n const answer = assertArray<NoteEvent>(assertRecord<MotifData>(b, 'b').notes, 'b.notes');\n if (model.length === 0 || model.length !== answer.length) {\n return null;\n }\n const modelGaps = onsetGaps(model);\n const answerGaps = onsetGaps(answer);\n const modelProfile = rhythmProfile(modelGaps);\n const answerProfile = rhythmProfile(answerGaps);\n // The rhythm the model would have if it were played backwards, which is what\n // a retrograde has to match — note values kept, their order reversed.\n const retroGaps = retrogradeGaps(model);\n const retroProfile = rhythmProfile(retroGaps);\n if (modelProfile === null || answerProfile === null) {\n return null;\n }\n const forward = sameNumbers(modelProfile, answerProfile);\n const backward = retroProfile !== null && sameNumbers(retroProfile, answerProfile);\n const semitones = (answer[0]?.pitch ?? 0) - (model[0]?.pitch ?? 0);\n const interval = intervalBetween(model[0]?.pitch ?? 0, answer[0]?.pitch ?? 0, key);\n const gap = (answer[0]?.startBeat ?? 0) - endBeatOf(model);\n // A sequence follows its model immediately; anything further off is a\n // restatement somewhere else in the piece. Immediately is what the adjacency\n // test already means by it, so a statement that starts a hair early counts.\n const sequence = gap >= -HUMANIZE_ADJACENCY && gap <= HUMANIZE_ADJACENCY;\n const timeRatio = stretchRatio(modelGaps, answerGaps);\n const stretched = Math.abs(timeRatio - 1) > BEAT_EPS;\n const shapes = pitchShapes(intervalsOf(model), intervalsOf(answer));\n const base = { sequence, semitones, interval, timeRatio };\n // A relation of the retrograde family is a stretch of the model played\n // backwards, so that is what its own ratio is measured against.\n const backwardBase = { ...base, timeRatio: stretchRatio(retroGaps, answerGaps) };\n\n if (forward && shapes.includes('exact')) {\n if (semitones === 0) {\n if (!stretched) {\n return {\n ...base,\n kind: 'repetition',\n rationale: 'Exact repetition: the same pitches in the same rhythm',\n };\n }\n const augmented = timeRatio > 1;\n return {\n ...base,\n kind: augmented ? 'augmentation' : 'diminution',\n rationale: `${augmented ? 'Augmentation' : 'Diminution'}: the same pitches in ${stretchPhrase(timeRatio)}`,\n };\n }\n const name = intervalName(interval);\n return {\n ...base,\n kind: 'transposition',\n rationale: sequence\n ? `Real sequence: the cell restated ${name} away with every interval kept literally` +\n (stretched ? `, in ${stretchPhrase(timeRatio)}` : '')\n : `Transposition by ${name}: every interval kept literally` +\n (stretched ? `, in ${stretchPhrase(timeRatio)}` : ''),\n };\n }\n\n if (forward && key !== undefined) {\n const modelLadder = ladderIndices(model, key.scale);\n const answerLadder = ladderIndices(answer, key.scale);\n if (modelLadder !== null && answerLadder !== null) {\n const degrees = (answerLadder[0] ?? 0) - (modelLadder[0] ?? 0);\n if (degrees !== 0 && sameNumbers(ladderSteps(modelLadder), ladderSteps(answerLadder))) {\n const direction = degrees > 0 ? 'higher' : 'lower';\n const count = Math.abs(degrees);\n return {\n ...base,\n kind: 'tonalTransposition',\n degrees,\n rationale:\n `${sequence ? 'Tonal sequence' : 'Tonal transposition'}: the cell restated ` +\n `${count} scale ${count === 1 ? 'degree' : 'degrees'} ${direction} inside the key, ` +\n `so the key resizes its intervals — a real one would have kept them literal`,\n };\n }\n }\n }\n\n if (forward && shapes.includes('inversion')) {\n return {\n ...base,\n kind: 'inversion',\n rationale:\n 'Inversion: every interval turned upside down about the first note' +\n (stretched ? `, in ${stretchPhrase(timeRatio)}` : ''),\n };\n }\n if (backward && shapes.includes('retrograde')) {\n return {\n ...backwardBase,\n kind: 'retrograde',\n rationale: 'Retrograde: the cell read back to front, rhythm included',\n };\n }\n if (backward && shapes.includes('retrogradeInversion')) {\n return {\n ...backwardBase,\n kind: 'retrogradeInversion',\n rationale: 'Retrograde inversion: the cell read back to front and turned upside down',\n };\n }\n return null;\n}\n","/**\n * How alike two phrases are, when they stand in no named relation.\n *\n * A device the repertoire names is an account of how one phrase was made from\n * another; a similarity score is what is left to say when there is no such\n * account — that the two are near, and how near. The reading is an edit\n * distance over the intervals and over the onset gaps, so a phrase answered by\n * a slightly ornamented version of itself still reads as close.\n */\n\nimport { BEAT_EPS } from '../adjacency.js';\nimport type { MelodicPhrase } from './internal.js';\nimport {\n intervalsOf,\n onsetGaps,\n orderedNotes,\n phraseNotes,\n rhythmProfile,\n roundTo,\n} from './internal.js';\n\n/**\n * Interval difference, in semitones, at which two steps count as wholly\n * different in {@link melodicSimilarity}.\n *\n * A third answered by a fourth is nearly the same gesture; a third answered by a\n * minor seventh is not. The tritone is where the graded cost saturates, so\n * anything wider than half an octave apart costs the same as an outright\n * mismatch.\n */\nconst INTERVAL_TOLERANCE = 6;\n/** Share of {@link melodicSimilarity} carried by the interval sequence. */\nconst PITCH_WEIGHT = 0.75;\n/** Share of {@link melodicSimilarity} carried by the rhythmic profile. */\nconst RHYTHM_WEIGHT = 0.25;\n/**\n * How alike two lines are, and how the answer was reached.\n *\n * @category Arrangement & Analysis\n */\nexport type MelodicComparison = {\n /** Overall likeness in [0, 1]; 1 for the same line, transposition included. */\n similarity: number;\n /** Likeness of the interval sequences alone, in [0, 1]. */\n pitchSimilarity: number;\n /** Likeness of the rhythmic profiles alone, in [0, 1]. */\n rhythmSimilarity: number;\n /** Why the lines scored what they did. */\n rationale: string;\n};\n/**\n * Edit distance between two sequences under a graded substitution cost.\n *\n * A flat cost would make a third answered by a fourth as wrong as a third\n * answered by a ninth; `cost` grades that, while an insertion or a deletion — a\n * note added or dropped — always costs one whole step.\n */\nfunction gradedEditDistance(\n a: readonly number[],\n b: readonly number[],\n cost: (x: number, y: number) => number,\n): number {\n let previous = Array.from({ length: b.length + 1 }, (_, i) => i);\n for (let i = 1; i <= a.length; i += 1) {\n const row = new Array<number>(b.length + 1);\n row[0] = i;\n for (let j = 1; j <= b.length; j += 1) {\n const substitution = (previous[j - 1] ?? 0) + cost(a[i - 1] ?? 0, b[j - 1] ?? 0);\n const deletion = (previous[j] ?? 0) + 1;\n const insertion = (row[j - 1] ?? 0) + 1;\n row[j] = Math.min(substitution, deletion, insertion);\n }\n previous = row;\n }\n return previous[b.length] ?? 0;\n}\n/** Turn an edit distance into a likeness in [0, 1]. */\nfunction distanceToSimilarity(distance: number, a: number, b: number): number {\n const longest = Math.max(a, b);\n if (longest === 0) {\n return 1;\n }\n return Math.min(1, Math.max(0, 1 - distance / longest));\n}\n/** Cost of hearing one interval where another was expected. */\nfunction intervalCost(x: number, y: number): number {\n return Math.min(1, Math.abs(x - y) / INTERVAL_TOLERANCE);\n}\n/**\n * Cost of hearing one onset gap where another was expected, measured in octaves\n * of tempo: a gap twice as long as expected costs a whole step, and the measure\n * is the same whichever of the two is the longer.\n */\nfunction gapCost(x: number, y: number): number {\n if (!(x > BEAT_EPS) || !(y > BEAT_EPS)) {\n return Math.abs(x - y) > BEAT_EPS ? 1 : 0;\n }\n return Math.min(1, Math.abs(Math.log2(x / y)));\n}\n/**\n * Compare two lines and say why they scored what they did.\n *\n * The metric is a normalised edit distance over the interval sequence, weighted\n * against the same over the rhythmic profile. Intervals rather than pitches,\n * because the same tune in another key is the same tune; edit distance rather\n * than a step-by-step correlation, because a variant that adds a passing note or\n * drops one still has to line up with its model, and only an alignment-based\n * measure can do that. Substituting one interval for another costs by how far\n * apart they are rather than a flat step, so an answer that widens a third to a\n * fourth stays close to its model while one that replaces it with a leap does\n * not. The rhythm carries the smaller share: a melody is recognised mostly by\n * its pitch shape, but a figure in a wholly different rhythm is a different\n * figure.\n *\n * Both lines are expected to be monophonic, as {@link analyzeVoice} expects a\n * voice to be. Notes struck together are read as one event, the highest of them\n * standing for it, so a chord is reduced to its top voice rather than read as a\n * line of its own.\n *\n * @param a The first line: a motif, or plain note events.\n * @param b The second line.\n * @returns The overall likeness, the two terms behind it, and a rationale.\n * @example\n * ```ts\n * import { compareMelodies } from '@libraz/libcantus';\n * const line = [\n * { pitch: 60, startBeat: 0, durationBeat: 1 },\n * { pitch: 62, startBeat: 1, durationBeat: 1 },\n * { pitch: 64, startBeat: 2, durationBeat: 1 },\n * ];\n * compareMelodies(line, line).similarity; // 1\n * ```\n * @category Arrangement & Analysis\n */\nexport function compareMelodies(a: MelodicPhrase, b: MelodicPhrase): MelodicComparison {\n const left = orderedNotes(phraseNotes(a), 'melody a');\n const right = orderedNotes(phraseNotes(b), 'melody b');\n const leftIntervals = intervalsOf(left);\n const rightIntervals = intervalsOf(right);\n const pitchSimilarity = distanceToSimilarity(\n gradedEditDistance(leftIntervals, rightIntervals, intervalCost),\n leftIntervals.length,\n rightIntervals.length,\n );\n const leftGaps = rhythmProfile(onsetGaps(left)) ?? onsetGaps(left);\n const rightGaps = rhythmProfile(onsetGaps(right)) ?? onsetGaps(right);\n const rhythmSimilarity = distanceToSimilarity(\n gradedEditDistance(leftGaps, rightGaps, gapCost),\n leftGaps.length,\n rightGaps.length,\n );\n const similarity = PITCH_WEIGHT * pitchSimilarity + RHYTHM_WEIGHT * rhythmSimilarity;\n return {\n similarity,\n pitchSimilarity,\n rhythmSimilarity,\n rationale:\n `Interval shapes ${Math.round(pitchSimilarity * 100)}% alike, ` +\n `rhythms ${Math.round(rhythmSimilarity * 100)}% alike, ` +\n `weighted ${PITCH_WEIGHT}/${RHYTHM_WEIGHT} into ${roundTo(similarity, 3)}`,\n };\n}\n/**\n * How alike two lines are, in [0, 1].\n *\n * The number {@link compareMelodies} reaches, for callers that only want to rank\n * phrases: 1 for the same line — a transposition of it included, since the\n * measure is built on intervals — and lower the further two lines are from being\n * each other's variant. Symmetric in its arguments, and deterministic.\n *\n * @param a The first line: a motif, or plain note events.\n * @param b The second line.\n * @returns The likeness in [0, 1].\n * @example\n * ```ts\n * import { melodicSimilarity } from '@libraz/libcantus';\n * const line = [\n * { pitch: 60, startBeat: 0, durationBeat: 1 },\n * { pitch: 62, startBeat: 1, durationBeat: 1 },\n * { pitch: 64, startBeat: 2, durationBeat: 1 },\n * ];\n * melodicSimilarity(line, line); // 1\n * ```\n * @category Arrangement & Analysis\n */\nexport function melodicSimilarity(a: MelodicPhrase, b: MelodicPhrase): number {\n return compareMelodies(a, b).similarity;\n}\n","/**\n * Phrases: the unit of music one bar larger than the chord.\n *\n * Every other analysis in this library is local — a chord against the next\n * chord, a note against the chord under it — and that is the ceiling on what it\n * can say. A cadence is a cadence because it falls at the end of a phrase, so\n * two chords read in isolation cannot settle how much weight one carries. This\n * module draws the boundaries, then ranks the cadences that sit on them.\n *\n * No single signal draws a phrase boundary reliably, so three are combined: the\n * cadences the harmony forms, the rests and long notes the melody breathes at,\n * and the points where the music starts saying something it has said before. A\n * boundary is reported with the evidence behind it and a confidence, because a\n * phrase reading is an interpretation and pretending otherwise would be worse\n * than useless to a caller deciding whether to trust it.\n */\n\nimport type { MeterLike, MeterMap } from '../../core/meter/index.js';\nimport {\n barIndexAt,\n barPositionToBeat,\n beatsPerBarAt,\n resolveMeters,\n} from '../../core/meter/index.js';\nimport type { NoteEvent } from '../../core/types.js';\nimport {\n assertArray,\n assertGenerationBudget,\n assertNoteEvents,\n assertPositiveInt,\n assertRange,\n assertRecord,\n} from '../../core/validation/index.js';\nimport { majorKey, resolveKey, scaleOf } from '../../theory/scale/index.js';\nimport { BEAT_EPS } from '../adjacency.js';\nimport type { CadenceResult } from '../functional/index.js';\nimport { barGridStart } from '../grid.js';\nimport { keyLookup, keyTimelineFromNotes, prevailingKeyOf } from '../keys/index.js';\nimport { melodicSimilarity } from '../melody/index.js';\nimport type { CadenceHit, ChordTimeline } from '../timeline/index.js';\nimport { assertChordTimeline, detectCadences, segmentStartingAt } from '../timeline/index.js';\nimport type { KeyContext } from '../voice/index.js';\nimport type { Hypermeter } from './hypermeter.js';\nimport { hypermeter } from './hypermeter.js';\nimport { clamp01, combineEvidence, sliceBars } from './internal.js';\n\n/**\n * A reason a phrase boundary was placed where it was.\n *\n * @category Arrangement & Analysis\n */\nexport type PhraseSignal =\n /** A cadence closed on the beat the boundary falls at. */\n | 'cadence'\n /** The melody stopped sounding long enough to have taken a breath. */\n | 'rest'\n /** A note was held long enough to read as an arrival. */\n | 'longNote'\n /** The music began repeating material heard immediately before. */\n | 'repetition'\n /** The boundary falls on a hypermetric downbeat. */\n | 'hypermeter'\n /** The music ends here, which closes whatever phrase was open. */\n | 'pieceEnd';\n\n/**\n * One phrase: a span of music closed by a boundary, and the evidence for it.\n *\n * @category Arrangement & Analysis\n */\nexport type Phrase = {\n /** First beat of the phrase; negative when the phrase begins in a pickup. */\n startBeat: number;\n /** End of the phrase, exclusive. */\n endBeat: number;\n /**\n * The cadence closing the phrase, or null when the boundary rests on the\n * melody and the metre alone.\n */\n cadence: CadenceHit | null;\n /** Why the closing boundary was placed here, strongest evidence first. */\n signals: PhraseSignal[];\n /**\n * How well evidenced the phrase is, in [0, 1]: how strong its closing\n * boundary is, and how close its length is to the length expected of a\n * phrase in this piece.\n */\n confidence: number;\n /**\n * How much structural weight the closing cadence carries, in [0, 1]; 0 for a\n * phrase that no cadence closes.\n *\n * This is the number that answers \"which cadence matters most\": it grades the\n * cadence by how conclusive its type is, then by where the phrase it closes\n * sits — a cadence ending the piece or landing on a hypermetric boundary\n * closes more than one in the middle of a hyperbar.\n * {@link structuralCadences} ranks by it.\n */\n structuralWeight: number;\n /** A short reading of the phrase in words. */\n rationale: string;\n};\n\n/**\n * Options for {@link phrasesFromTimeline}.\n *\n * @category Arrangement & Analysis\n */\nexport type PhraseOptions = {\n /**\n * A single time signature held across the whole span, as sugar for a\n * one-element `meters`; defaults to 4/4. Giving both is an input error.\n *\n * @defaultValue `4/4`\n */\n ts?: MeterLike;\n /**\n * The meter as it changes over the span. Bar lines and hypermetric downbeats\n * follow the signature in force at the beat in question, so a piece that\n * changes metre is cut at its own bar lines; the length defaults below are\n * lengths rather than positions, and each is taken once from the meter the\n * span opens in.\n *\n * @defaultValue 4/4 throughout\n */\n meters?: MeterLike;\n /**\n * Key context for cadence detection: one key for the whole span, or the key\n * in force at a given beat. Omit it to have the key searched for over time,\n * so a piece that modulates has each cadence read in the key it arrives in.\n *\n * @defaultValue the key inferred from the notes over time\n */\n key?: KeyContext;\n /**\n * End of the analysed span in beats; defaults to the end of the last note or\n * timeline segment.\n *\n * @defaultValue the end of the input\n */\n totalBeats?: number;\n /**\n * Length a phrase is expected to run, in beats; defaults to one hyperbar.\n *\n * This is a prior, not a rule: a boundary is placed where the evidence puts\n * it, and this decides how much evidence an irregular length has to carry\n * before it is preferred to a regular one. Set it to two hyperbars for music\n * whose phrases are periods.\n *\n * @defaultValue one hyperbar of the opening meter\n */\n expectedPhraseBeats?: number;\n /**\n * Shortest phrase that may be reported, in beats; defaults to one bar of the\n * opening meter.\n *\n * @defaultValue one bar of the opening meter\n */\n minPhraseBeats?: number;\n /**\n * Silence at least this long divides phrases; defaults to half a bar of the\n * opening meter.\n *\n * @defaultValue half a bar of the opening meter\n */\n restBeats?: number;\n /**\n * A note held at least this long reads as an arrival and closes a phrase;\n * defaults to one bar of the opening meter.\n *\n * @defaultValue one bar of the opening meter\n */\n longNoteBeats?: number;\n /**\n * A hypermeter already computed for this piece, to be used instead of\n * inferring one. Pass it when the caller has already asked for the grouping,\n * or to impose a grouping the search would not have chosen.\n *\n * @defaultValue inferred from the notes and the cadences found\n */\n hypermeter?: Hypermeter;\n /**\n * Upper bound on the work this call may do.\n *\n * @defaultValue {@link DEFAULT_GENERATION_BUDGET}\n */\n budget?: number;\n};\n\n/**\n * A cadence ranked by how much of the structure it closes.\n *\n * @category Arrangement & Analysis\n */\nexport type StructuralCadence = {\n /** Index of the phrase this cadence closes, in the array it came from. */\n phraseIndex: number;\n /** The beat the cadence arrives on. */\n atBeat: number;\n /** The beat the phrase it closes ends at. */\n endBeat: number;\n /** The cadence itself. */\n cadence: CadenceResult;\n /** Structural weight in [0, 1]; {@link Phrase.structuralWeight}. */\n weight: number;\n /** Why it weighs what it does. */\n rationale: string;\n};\n\n/**\n * How strongly each cadence type marks a boundary.\n *\n * This grades the cadence as a punctuation mark, not as a close: a half cadence\n * ends an antecedent as audibly as an authentic one ends a consequent, which is\n * why it scores near it here and far below it in {@link CADENCE_CLOSURE}. A\n * deceptive cadence scores lowest of all because its whole effect is to\n * withhold the ending it prepared, so the phrase usually runs on past it.\n */\nconst CADENCE_BOUNDARY_STRENGTH: Record<NonNullable<CadenceResult['type']>, number> = {\n authentic: 0.9,\n modal: 0.75,\n plagal: 0.7,\n phrygian: 0.65,\n half: 0.6,\n deceptive: 0.45,\n};\n\n/**\n * How much of the structure each cadence type closes.\n *\n * The perfect authentic cadence is the only one that ends anything outright;\n * everything else leaves the music open to some degree, and the half cadence\n * most of all — it ends a phrase by pointing at the next one.\n */\nconst CADENCE_CLOSURE: Record<NonNullable<CadenceResult['type']>, number> = {\n authentic: 0.85,\n modal: 0.6,\n plagal: 0.55,\n phrygian: 0.4,\n half: 0.35,\n deceptive: 0.3,\n};\n\n/** Weight of a full-bar rest as a boundary signal. */\nconst REST_STRENGTH = 0.6;\n\n/** Weight of a held note as a boundary signal. */\nconst LONG_NOTE_STRENGTH = 0.4;\n\n/** Weight of immediate repetition as a boundary signal. */\nconst REPETITION_STRENGTH = 0.5;\n\n/** How alike two adjacent spans must be before the second is heard as a restart. */\nconst REPETITION_THRESHOLD = 0.75;\n\n/** Weight of a hypermetric downbeat as a boundary signal. */\nconst HYPERMETER_STRENGTH = 0.25;\n\n/** Weight of the end of the music as a boundary signal. */\nconst PIECE_END_STRENGTH = 0.5;\n\n/**\n * What a phrase boundary has to be worth before one is placed.\n *\n * The search adds up boundary evidence and length fit across a whole reading,\n * so without a cost per cut it would cut wherever cutting was not actively\n * wrong. Set just above the best length fit alone, so that a boundary needs\n * some evidence of its own: metre may confirm a phrase ending, but it may not\n * declare one. A boundary worth exactly this much is break-even, and a\n * break-even boundary is not taken — see {@link choosePhrasePath}.\n *\n * Not part of the public surface; exported so the search's break-even point can\n * be stated exactly rather than approximated.\n */\nexport const PHRASE_CUT_COST = 1.3;\n\n/** Share of a phrase's confidence carried by its closing boundary. */\nconst CLOSING_SHARE = 0.65;\n\n/** Bonus for a cadence that closes the last phrase of the piece. */\nconst FINAL_PHRASE_BONUS = 0.15;\n\n/** Bonus for a cadence whose phrase ends on a hypermetric boundary. */\nconst HYPERMETRIC_CLOSE_BONUS = 0.1;\n\n/** How much of a cadence's weight survives resolving onto an inverted tonic. */\nconst EVADED_FACTOR = 0.6;\n\n/** Order the boundary signals are reported in when they are equally strong. */\nconst SIGNAL_ORDER: readonly PhraseSignal[] = [\n 'cadence',\n 'rest',\n 'repetition',\n 'longNote',\n 'pieceEnd',\n 'hypermeter',\n];\n\n/** A candidate boundary and everything that argues for it. */\ntype Boundary = {\n beat: number;\n strengths: Map<PhraseSignal, number>;\n cadence: CadenceHit | null;\n};\n\n/** Record a signal at a beat, keeping the strongest claim made for it. */\nfunction addSignal(\n boundaries: Map<number, Boundary>,\n beat: number,\n signal: PhraseSignal,\n strength: number,\n cadence: CadenceHit | null = null,\n): void {\n // Beats arrive from bar arithmetic and from note ends, which land an epsilon\n // apart on the same boundary; keying on a rounded beat merges them.\n const key = Math.round(beat / BEAT_EPS);\n const existing = boundaries.get(key);\n const boundary = existing ?? { beat, strengths: new Map(), cadence: null };\n boundary.strengths.set(signal, Math.max(boundary.strengths.get(signal) ?? 0, strength));\n if (cadence !== null && boundary.cadence === null) {\n boundary.cadence = cadence;\n }\n boundaries.set(key, boundary);\n}\n\n/** Total strength of a boundary, and its signals strongest first. */\nfunction readBoundary(boundary: Boundary): { strength: number; signals: PhraseSignal[] } {\n const signals = [...boundary.strengths.entries()].sort(\n (a, b) => b[1] - a[1] || SIGNAL_ORDER.indexOf(a[0]) - SIGNAL_ORDER.indexOf(b[0]),\n );\n return {\n strength: combineEvidence(signals.map(([, strength]) => strength)),\n signals: signals.map(([signal]) => signal),\n };\n}\n\n/**\n * How well a length matches what a phrase is expected to run.\n *\n * 1 at the expected length, 0 at twice or none of it, and negative beyond —\n * a phrase three times the expected length is not merely unsurprising, it is\n * evidence that a boundary was missed.\n */\nfunction lengthFit(length: number, expected: number): number {\n if (!(expected > 0)) {\n return 0;\n }\n return Math.max(-1, 1 - Math.abs(length - expected) / expected);\n}\n\n/**\n * Choose which of the candidate stops become phrase boundaries.\n *\n * A reading is worth the evidence at every boundary it takes, plus how well\n * each phrase's length matches what a phrase is expected to run, less a cost\n * per cut. The best reading is found by dynamic programming over the stops,\n * since a boundary's worth depends on where the previous one fell.\n *\n * Predecessors are considered in time order, and only a strictly better reading\n * displaces the one already found. Two readings that score the same therefore\n * settle on the earlier predecessor — the longer phrase, with one boundary\n * fewer — because a search may not invent a boundary that the evidence does not\n * prefer to having none. The rule is a property of the comparison, not of the\n * order the loop happens to run in.\n *\n * Not part of the public surface: it takes the stops and strengths some other\n * pass already derived, so exposing it would invite callers to pair the two by\n * hand.\n *\n * @param beats Beat of each stop, ascending; index 0 is the span's start and\n * the last is its end.\n * @param strengths Boundary evidence at each stop, in the same order.\n * @param minPhraseBeats Shortest phrase the search may report.\n * @param expected Length a phrase is expected to run, in beats.\n * @returns The chosen stop indices after the start, in time order.\n */\nexport function choosePhrasePath(\n beats: readonly number[],\n strengths: readonly number[],\n minPhraseBeats: number,\n expected: number,\n): number[] {\n const value = new Array<number>(beats.length).fill(Number.NEGATIVE_INFINITY);\n const cameFrom = new Array<number>(beats.length).fill(-1);\n value[0] = 0;\n for (let j = 1; j < beats.length; j += 1) {\n const endBeat = beats[j] ?? 0;\n for (let i = 0; i < j; i += 1) {\n if (!Number.isFinite(value[i] ?? Number.NEGATIVE_INFINITY)) {\n continue;\n }\n const length = endBeat - (beats[i] ?? 0);\n if (length < minPhraseBeats - BEAT_EPS) {\n continue;\n }\n const gain =\n (value[i] ?? 0) + (strengths[j] ?? 0) + lengthFit(length, expected) - PHRASE_CUT_COST;\n if (gain > (value[j] ?? Number.NEGATIVE_INFINITY)) {\n value[j] = gain;\n cameFrom[j] = i;\n }\n }\n }\n\n const last = beats.length - 1;\n const path: number[] = [];\n if (last > 0 && Number.isFinite(value[last] ?? Number.NEGATIVE_INFINITY)) {\n for (let i = last; i > 0; i = cameFrom[i] ?? 0) {\n path.push(i);\n }\n path.reverse();\n } else if (last > 0) {\n // Nothing reached the end — the span is shorter than one phrase — so the\n // whole of it is the phrase.\n path.push(last);\n }\n return path;\n}\n\n/** The beat at which the chord arriving at `atBeat` gives way to the next. */\nfunction arrivalEnd(timeline: ChordTimeline, atBeat: number): number {\n return segmentStartingAt(timeline.segments, atBeat)?.endBeat ?? atBeat;\n}\n\n/**\n * How many bars a span covers, in the meters in force across it.\n *\n * Counted bar line by bar line rather than by dividing the length by one bar,\n * so a span inside a 3/4 stretch is six beats of two bars and not of one and a\n * half. The whole bars between the two ends, plus the part of each end bar the\n * span actually holds.\n */\nfunction barSpanOf(startBeat: number, endBeat: number, meters: MeterMap): number {\n const firstBar = barIndexAt(startBeat, meters);\n const lastBar = barIndexAt(endBeat, meters);\n const partOf = (beat: number, bar: number): number => {\n const barStart = barPositionToBeat({ bar, beat: 0 }, meters);\n const barLength = beatsPerBarAt(barStart, meters);\n return barLength > 0 ? (beat - barStart) / barLength : 0;\n };\n const bars = lastBar - firstBar + partOf(endBeat, lastBar) - partOf(startBeat, firstBar);\n return Math.round(bars * 100) / 100;\n}\n\n/** The key context cadence detection is to be read against. */\nfunction keyContextFor(\n opts: PhraseOptions,\n notes: NoteEvent[],\n meters: MeterMap,\n totalBeats: number,\n): KeyContext {\n if (opts.key !== undefined) {\n return opts.key;\n }\n const regions = keyTimelineFromNotes(notes, {\n meters,\n totalBeats,\n budget: opts.budget,\n });\n // The pitch classes alone: a phrase boundary is argued from cadences and\n // scale membership, neither of which reads how the key is written.\n const keyAt = keyLookup(regions, prevailingKeyOf(regions) ?? resolveKey(majorKey(0)));\n return (beat) => scaleOf(keyAt(beat));\n}\n\n/** Boundaries argued for by silence in the melody. */\nfunction restBoundaries(\n boundaries: Map<number, Boundary>,\n notes: readonly NoteEvent[],\n meters: MeterMap,\n restBeats: number,\n): void {\n const ordered = [...notes].sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);\n let covered = Number.NEGATIVE_INFINITY;\n for (const note of ordered) {\n const gap = note.startBeat - covered;\n if (Number.isFinite(covered) && gap >= restBeats - BEAT_EPS) {\n const bar = beatsPerBarAt(note.startBeat, meters);\n addSignal(\n boundaries,\n note.startBeat,\n 'rest',\n REST_STRENGTH * clamp01(bar > 0 ? gap / bar : 1),\n );\n }\n covered = Math.max(covered, note.startBeat + note.durationBeat);\n }\n}\n\n/** Boundaries argued for by immediate repetition of the preceding span. */\nfunction repetitionBoundaries(\n boundaries: Map<number, Boundary>,\n notes: readonly NoteEvent[],\n meters: MeterMap,\n spanStart: number,\n spanEnd: number,\n windowBeats: number,\n budget: number | undefined,\n): void {\n if (!(windowBeats > 0)) {\n return;\n }\n const slices = sliceBars(notes, meters, spanStart, spanEnd, budget);\n assertGenerationBudget(slices.length * notes.length, 'form repetition comparisons', budget);\n const onsetsIn = (from: number, to: number): NoteEvent[] =>\n notes.filter((note) => note.startBeat >= from - BEAT_EPS && note.startBeat < to - BEAT_EPS);\n // Comparing the two windows runs an edit distance over their notes, so the\n // work is the product of what each side holds rather than one unit per slice.\n // It is charged as it is incurred, so a passage dense enough to matter is\n // refused before its distance is run rather than after.\n let comparisons = 0;\n for (const slice of slices) {\n const beat = slice.startBeat;\n if (beat - windowBeats < spanStart - BEAT_EPS || beat + windowBeats > spanEnd + BEAT_EPS) {\n continue;\n }\n const before = onsetsIn(beat - windowBeats, beat);\n const after = onsetsIn(beat, beat + windowBeats);\n // One note is a pitch, not a figure; two identical single notes would score\n // a perfect likeness and mark a boundary at every held tone.\n if (before.length < 2 || after.length < 2) {\n continue;\n }\n comparisons += before.length * after.length;\n assertGenerationBudget(comparisons, 'form repetition melody comparisons', budget);\n const similarity = melodicSimilarity(before, after);\n if (similarity >= REPETITION_THRESHOLD) {\n addSignal(boundaries, beat, 'repetition', REPETITION_STRENGTH * similarity);\n }\n }\n}\n\n/** Grade a cadence by how much of the structure it closes. */\nfunction structuralWeightOf(\n cadence: CadenceHit | null,\n confidence: number,\n aligned: boolean,\n isLast: boolean,\n): number {\n if (cadence === null || cadence.cadence.type === null) {\n return 0;\n }\n const result = cadence.cadence;\n let base = CADENCE_CLOSURE[result.type as NonNullable<CadenceResult['type']>];\n if (result.type === 'authentic') {\n if (result.strength === 'perfect') {\n base = 1;\n } else if (result.strength === 'imperfect') {\n base *= 0.8;\n }\n }\n if (result.evaded) {\n base *= EVADED_FACTOR;\n }\n let weight = base * confidence;\n if (isLast) {\n weight += FINAL_PHRASE_BONUS;\n }\n if (aligned) {\n weight += HYPERMETRIC_CLOSE_BONUS;\n }\n return clamp01(weight);\n}\n\n/** Name a cadence the way a rationale reads it. */\nfunction describeCadence(cadence: CadenceHit | null): string {\n if (cadence === null || cadence.cadence.type === null) {\n return 'no cadence';\n }\n const { type, strength } = cadence.cadence;\n const graded = strength === null ? type : `${strength} ${type}`;\n return `a ${graded} cadence at beat ${cadence.atBeat}`;\n}\n\n/**\n * Split a piece into phrases.\n *\n * Three kinds of evidence are gathered independently and then reconciled. The\n * harmony contributes its cadences, each closing at the end of the chord it\n * arrives on, and at the bar line where that chord is held across one — a tonic\n * still sounding into the next phrase closed this one at the bar line. The\n * cadence reported for a phrase is always one that arrived inside it. The\n * melody contributes the rests it breathes at and the notes it\n * holds long enough to read as arrivals. Repetition contributes the beats where\n * the music restarts material it has just played. The metre contributes its\n * hypermetric downbeats, which confirm a boundary but never declare one on\n * their own.\n *\n * The boundaries are then chosen together rather than one at a time: the\n * reading that best trades each boundary's evidence against how regular the\n * phrase lengths come out is selected by dynamic programming, at a fixed cost\n * per cut. That is what stops a phrase being declared at every plausible beat,\n * and what lets a well-evidenced irregular phrase survive next to regular ones.\n *\n * The result is contiguous: phrases abut, and together they cover the span from\n * the first note to the end of the music. A cadence found in the middle of a\n * phrase is not lost — it is still reachable through {@link detectCadences} —\n * but only the cadences the reading puts at phrase ends are ranked by\n * {@link structuralCadences}.\n *\n * @param timeline The chord timeline the cadences are read from.\n * @param notes The melody, for its rests, held notes and repetitions. Notes\n * that never sound are dropped.\n * @param opts Analysis options; see {@link PhraseOptions}.\n * @returns The phrases, in time order; one phrase covering the whole span when\n * the span is too short to divide, and none at all when nothing sounds —\n * silence is no material to hear a phrase in.\n * @example\n * ```ts\n * import { chordTimelineFromNotes, phrasesFromTimeline } from '@libraz/libcantus';\n * // Four bars of C - F - G - C. Triads rather than single notes: one line at a\n * // time names no harmony, so nothing would cadence.\n * const triad = (pitches: number[], startBeat: number) =>\n * pitches.map((pitch) => ({ pitch, startBeat, durationBeat: 4 }));\n * const notes = [\n * ...triad([60, 64, 67], 0),\n * ...triad([65, 69, 72], 4),\n * ...triad([67, 71, 74], 8),\n * ...triad([60, 64, 67], 12),\n * ];\n * const { timeline } = chordTimelineFromNotes(notes);\n * const phrases = phrasesFromTimeline(timeline, notes);\n * phrases[phrases.length - 1]?.cadence?.cadence.type; // 'authentic'\n * phrases[phrases.length - 1]?.cadence?.atBeat; // 12, inside the phrase it closes\n * ```\n * @category Arrangement & Analysis\n */\nexport function phrasesFromTimeline(\n timeline: ChordTimeline,\n notes: readonly NoteEvent[],\n opts: PhraseOptions = {},\n): Phrase[] {\n assertChordTimeline(timeline);\n const meters = resolveMeters(opts, 'phrase meters');\n assertNoteEvents(notes, 'phrase notes', {\n allowNonPositiveDuration: true,\n budget: opts.budget,\n });\n if (opts.hypermeter !== undefined) {\n // The one structured option, held to the contract its own type states: a\n // confidence outside [0, 1] scales the metrical signal past the cost of a\n // cut, which would let the metre declare a boundary rather than confirm\n // one, and a fractional group is not a number of bars.\n assertRange(opts.hypermeter.confidence, 0, 1, 'phrase hypermeter confidence');\n assertPositiveInt(opts.hypermeter.groupBars, 'phrase hypermeter groupBars');\n }\n const sounding = notes.filter((note) => note.durationBeat > 0);\n const segments = timeline.segments;\n const firstSegment = segments[0];\n const lastSegment = segments[segments.length - 1];\n const firstOnset = sounding.reduce(\n (first, n) => Math.min(first, n.startBeat),\n firstSegment?.startBeat ?? 0,\n );\n // The pickup rule the whole analysis shares, so the first beat this reports\n // is the one sections and hypermeter report for the same notes.\n const spanStart = barGridStart(firstOnset, meters);\n const notesEnd = sounding.reduce(\n (end, n) => Math.max(end, n.startBeat + n.durationBeat),\n lastSegment?.endBeat ?? 0,\n );\n const spanEnd = Math.max(spanStart, opts.totalBeats ?? notesEnd);\n assertRange(spanEnd, 0, Number.MAX_SAFE_INTEGER, 'phrase totalBeats');\n if (spanEnd <= spanStart + BEAT_EPS) {\n // Nothing sounds, so there is no material to hear a phrase in — the answer\n // `sectionsFromNotes` gives an empty span. A phrase of no length, closed by\n // nothing, would be a marker rather than a phrase.\n return [];\n }\n\n const barBeats = beatsPerBarAt(Math.max(0, spanStart), meters);\n const restBeats = opts.restBeats ?? barBeats / 2;\n assertRange(restBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, 'phrase restBeats');\n const longNoteBeats = opts.longNoteBeats ?? barBeats;\n assertRange(longNoteBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, 'phrase longNoteBeats');\n const minPhraseBeats = opts.minPhraseBeats ?? barBeats;\n assertRange(minPhraseBeats, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, 'phrase minPhraseBeats');\n\n const key = keyContextFor(opts, sounding, meters, spanEnd);\n const cadences = detectCadences(timeline, key);\n const grouping =\n opts.hypermeter ??\n hypermeter(sounding, meters, {\n totalBeats: spanEnd,\n cadenceBeats: cadences.map((hit) => hit.atBeat),\n budget: opts.budget,\n });\n // The hyperbar the span opens in, not the one the piece opens in. Bar 0 is\n // always the bar beginning at beat 0 of the meter map, so measuring the group\n // from there hands an excerpt starting after a change of meter the bar length\n // of a meter it never sounds in — and that wrong length is the prior every\n // phrase in it is fitted against. The bar the span itself opens in is the\n // anchor the line above already uses.\n const firstBar = barIndexAt(Math.max(0, spanStart), meters);\n const hyperBeats = Math.max(\n barBeats,\n barPositionToBeat({ bar: firstBar + grouping.groupBars, beat: 0 }, meters) -\n barPositionToBeat({ bar: firstBar, beat: 0 }, meters),\n );\n const expected = opts.expectedPhraseBeats ?? hyperBeats;\n assertRange(expected, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, 'phrase expectedPhraseBeats');\n\n const boundaries = new Map<number, Boundary>();\n for (const hit of cadences) {\n const type = hit.cadence.type;\n if (type === null) {\n continue;\n }\n const strength = CADENCE_BOUNDARY_STRENGTH[type];\n const chordEnd = arrivalEnd(timeline, hit.atBeat);\n addSignal(boundaries, chordEnd, 'cadence', strength, hit);\n // A chord held past the bar line closes its phrase at that line as well as\n // where it stops sounding: the tonic a period's consequent opens on is the\n // same tonic the antecedent cadenced to, and reading the arrival only at\n // the end of the held chord would leave the seam between the two with no\n // cadence on it and hand the cadence to the phrase that follows it.\n const barEnd = barPositionToBeat({ bar: barIndexAt(hit.atBeat, meters) + 1, beat: 0 }, meters);\n if (chordEnd > barEnd + BEAT_EPS) {\n addSignal(boundaries, barEnd, 'cadence', strength, hit);\n }\n }\n restBoundaries(boundaries, sounding, meters, restBeats);\n for (const note of sounding) {\n if (note.durationBeat >= longNoteBeats - BEAT_EPS) {\n addSignal(boundaries, note.startBeat + note.durationBeat, 'longNote', LONG_NOTE_STRENGTH);\n }\n }\n repetitionBoundaries(boundaries, sounding, meters, spanStart, spanEnd, hyperBeats, opts.budget);\n for (const downbeat of grouping.downbeats) {\n addSignal(boundaries, downbeat, 'hypermeter', HYPERMETER_STRENGTH * grouping.confidence);\n }\n addSignal(boundaries, spanEnd, 'pieceEnd', PIECE_END_STRENGTH);\n\n // The span's own ends are boundaries whether or not anything argues for them,\n // so the phrases cover the music rather than only its well-marked middle.\n const candidates = [...boundaries.values()]\n .filter(\n (boundary) => boundary.beat > spanStart + BEAT_EPS && boundary.beat < spanEnd - BEAT_EPS,\n )\n // A stop nothing argues for is not a boundary that a length can buy. A\n // hypermeter position read with no confidence registers at strength zero,\n // and the path search weighs a cut by the two lengths it makes as well as\n // by the evidence: over a pedal point or a drone, where no cadence and no\n // harmonic novelty argue anywhere, the length term alone put boundaries in\n // the reading that a caller cannot tell from evidenced ones.\n .filter((boundary) => readBoundary(boundary).strength > 0)\n .sort((a, b) => a.beat - b.beat);\n const endBoundary = boundaries.get(Math.round(spanEnd / BEAT_EPS)) ?? {\n beat: spanEnd,\n strengths: new Map<PhraseSignal, number>(),\n cadence: null,\n };\n const stops: Boundary[] = [\n { beat: spanStart, strengths: new Map(), cadence: null },\n ...candidates,\n endBoundary,\n ];\n assertGenerationBudget(stops.length * stops.length, 'form phrase boundary pairs', opts.budget);\n\n const readings = stops.map(readBoundary);\n const path = choosePhrasePath(\n stops.map((stop) => stop.beat),\n readings.map((reading) => reading.strength),\n minPhraseBeats,\n expected,\n );\n\n const downbeatKeys = new Set(\n grouping.downbeats.map((downbeat) => Math.round(downbeat / BEAT_EPS)),\n );\n const phrases: Phrase[] = [];\n let from = 0;\n for (let n = 0; n < path.length; n += 1) {\n const to = path[n] ?? stops.length - 1;\n const startBeat = stops[from]?.beat ?? spanStart;\n const stop = stops[to];\n const endBeat = stop?.beat ?? spanEnd;\n const reading = readings[to] ?? { strength: 0, signals: [] as PhraseSignal[] };\n const length = endBeat - startBeat;\n const confidence = clamp01(\n CLOSING_SHARE * reading.strength +\n (1 - CLOSING_SHARE) * Math.max(0, lengthFit(length, expected)),\n );\n const isLast = n === path.length - 1;\n // A cadence that arrived before this phrase began cannot be the one closing\n // it: its chord was still sounding at the boundary, which is a held arrival\n // and not a second one. The cadence belongs to the phrase it arrived in, so\n // a reported `cadence.atBeat` always lies inside `[startBeat, endBeat)`.\n const closing = stop?.cadence ?? null;\n const cadence =\n closing !== null &&\n closing.atBeat >= startBeat - BEAT_EPS &&\n closing.atBeat < endBeat - BEAT_EPS\n ? closing\n : null;\n const structuralWeight = structuralWeightOf(\n cadence,\n confidence,\n downbeatKeys.has(Math.round(endBeat / BEAT_EPS)),\n isLast,\n );\n // The signals say what the boundary was read from, so a cadence dropped as\n // belonging to an earlier phrase is dropped from them too: reporting a\n // phrase closing on no cadence whose boundary came `from cadence` states\n // both halves of one fact and contradicts itself.\n const signals =\n cadence === null ? reading.signals.filter((signal) => signal !== 'cadence') : reading.signals;\n const bars = barSpanOf(startBeat, endBeat, meters);\n phrases.push({\n startBeat,\n endBeat,\n cadence,\n signals,\n confidence,\n structuralWeight,\n rationale:\n `Phrase of ${bars} bar(s) closing on ${describeCadence(cadence)}; ` +\n `boundary from ${signals.length === 0 ? 'the end of the span' : signals.join(', ')}`,\n });\n from = to;\n }\n return phrases;\n}\n\n/**\n * Rank the cadences that close phrases by how much structure they close.\n *\n * This is the question the phrase layer exists to answer. Two chords alone\n * cannot say whether a V-to-I is the end of the piece or a passing confirmation\n * halfway through a hyperbar; a phrase reading can, because it knows what the\n * cadence closes and where that sits. Phrases no cadence closes are left out.\n *\n * @param phrases The phrases, as {@link phrasesFromTimeline} returns them.\n * @returns The cadences, heaviest first; ties keep the earlier one.\n * @example\n * ```ts\n * import { chordTimelineFromNotes, phrasesFromTimeline, structuralCadences } from '@libraz/libcantus';\n * const triad = (pitches: number[], startBeat: number) =>\n * pitches.map((pitch) => ({ pitch, startBeat, durationBeat: 4 }));\n * const notes = [\n * ...triad([60, 64, 67], 0),\n * ...triad([65, 69, 72], 4),\n * ...triad([67, 71, 74], 8),\n * ...triad([60, 64, 67], 12),\n * ];\n * const { timeline } = chordTimelineFromNotes(notes);\n * const ranked = structuralCadences(phrasesFromTimeline(timeline, notes));\n * ranked[0]?.cadence.type; // 'authentic' — the cadence carrying the most weight\n * ranked[0]?.atBeat; // 12\n * ```\n * @category Arrangement & Analysis\n */\nexport function structuralCadences(phrases: readonly Phrase[]): StructuralCadence[] {\n assertArray(phrases, 'phrases');\n const ranked: StructuralCadence[] = [];\n for (let index = 0; index < phrases.length; index += 1) {\n const phrase = assertRecord<Phrase>(phrases[index], `phrases[${index}]`);\n if (\n phrase.cadence === null ||\n phrase.cadence === undefined ||\n phrase.cadence.cadence.type === null\n ) {\n continue;\n }\n const hit = phrase.cadence;\n ranked.push({\n phraseIndex: index,\n atBeat: hit.atBeat,\n endBeat: phrase.endBeat,\n cadence: hit.cadence,\n weight: phrase.structuralWeight,\n rationale: `Closes ${describeCadence(hit)} on a phrase of confidence ${\n Math.round(phrase.confidence * 100) / 100\n }`,\n });\n }\n return ranked.sort((a, b) => b.weight - a.weight || a.atBeat - b.atBeat);\n}\n","/**\n * Sections: the largest unit this library reads, recovered from repetition.\n *\n * A section is found, not named. What the analysis can see is that a span of\n * music is a restatement of an earlier one, so the spans are labelled by order\n * of first appearance — A, B, C — and nothing more is claimed. Calling a\n * repeated span a chorus would be a guess dressed as a finding: repetition\n * alone cannot tell a chorus from a second verse, and the label a listener\n * would give depends on lyrics, arrangement and loudness that the notes do not\n * carry.\n */\n\nimport type { MeterLike } from '../../core/meter/index.js';\nimport { barIndexAt, barPositionToBeat, resolveMeters } from '../../core/meter/index.js';\nimport type { NoteEvent } from '../../core/types.js';\nimport {\n assertGenerationBudget,\n assertNoteEvents,\n assertPositiveInt,\n assertRange,\n} from '../../core/validation/index.js';\nimport { BEAT_EPS } from '../adjacency.js';\nimport { barGridStart } from '../grid.js';\nimport { windowWeights } from '../histogram.js';\nimport { melodicSimilarity } from '../melody/index.js';\nimport { hypermeter } from './hypermeter.js';\nimport { clamp01, firstSoundingBeat, lastBarOf, weightSimilarity } from './internal.js';\n\n/**\n * A span of music labelled by which earlier span it restates.\n *\n * @category Arrangement & Analysis\n */\nexport type FormSection = {\n /**\n * The section's letter, assigned in order of first appearance: the opening\n * material is A, the first material unlike it is B, and a later restatement\n * of A is A again. Past Z the letters double (AA, AB), the way spreadsheet\n * columns do. No functional name — verse, chorus, bridge — is ever inferred.\n */\n label: string;\n /** First beat of the section; negative when it opens with a pickup. */\n startBeat: number;\n /** End of the section, exclusive. */\n endBeat: number;\n /** The section's length in bars. */\n bars: number;\n /**\n * Index, in the returned array, of the first section carrying this label; the\n * section's own index when it is that first appearance.\n */\n firstOccurrence: number;\n /**\n * How alike this section and its first occurrence are, in [0, 1]; 1 on the\n * first occurrence itself. The mean over the units the section was merged\n * from, each scored against the unit it matched.\n */\n similarity: number;\n /** How the section came to carry its label. */\n rationale: string;\n};\n\n/**\n * Options for {@link sectionsFromNotes}.\n *\n * @category Arrangement & Analysis\n */\nexport type FormSectionOptions = {\n /**\n * A single time signature held across the whole span, as sugar for a\n * one-element `meters`; defaults to 4/4. Giving both is an input error.\n *\n * @defaultValue `4/4`\n */\n ts?: MeterLike;\n /**\n * The meter as it changes over the span, so a piece that changes metre is cut\n * at its own bar lines.\n *\n * @defaultValue 4/4 throughout\n */\n meters?: MeterLike;\n /**\n * Length of the unit spans are compared in, in bars; defaults to one hyperbar.\n *\n * Sections are found by comparing equal units and merging the neighbours that\n * match, so this is the resolution of the answer rather than the length of a\n * section: a section is any whole number of units long.\n *\n * @defaultValue the inferred hyperbar, in bars\n */\n unitBars?: number;\n /**\n * How alike two units must be before the later one is called a restatement of\n * the earlier, in [0, 1].\n *\n * @defaultValue `0.8`\n */\n threshold?: number;\n /**\n * End of the analysed span in beats; defaults to the end of the last note.\n *\n * @defaultValue the end of the last note\n */\n totalBeats?: number;\n /**\n * Upper bound on the work this call may do.\n *\n * @defaultValue {@link DEFAULT_GENERATION_BUDGET}\n */\n budget?: number;\n};\n\n/** Share of a unit's likeness carried by its melodic line. */\nconst MELODY_SHARE = 0.6;\n\n/** Share of a unit's likeness carried by its harmonic content. */\nconst HARMONY_SHARE = 0.4;\n\n/** How alike two units must be before one is called a restatement of the other. */\nconst DEFAULT_THRESHOLD = 0.8;\n\n/** One unit of comparison: a fixed run of bars and what sounds in it. */\ntype Unit = {\n startBeat: number;\n endBeat: number;\n /** The notes whose onsets fall inside the unit, in time order. */\n notes: NoteEvent[];\n /** Pitch-class weights over the unit. */\n weights: number[];\n};\n\n/**\n * The nth label in the sequence A, B, ... Z, AA, AB, ...\n *\n * Doubling the letters past Z rather than numbering them (A1, A2) keeps the\n * label free of digits, so nothing reads as \"the second A\" when it is in fact\n * the twenty-seventh distinct span.\n */\nfunction labelAt(index: number): string {\n let n = index;\n let label = '';\n do {\n label = String.fromCharCode(65 + (n % 26)) + label;\n n = Math.floor(n / 26) - 1;\n } while (n >= 0);\n return label;\n}\n\n/**\n * Whether nothing sounds in a unit.\n *\n * A unit holds the notes that begin in it, and its weights are drawn from those\n * notes, so a unit with no onsets carries no melodic content and an all-zero\n * histogram: a tacet, a general pause, a drum break, a track that enters late.\n * Silence states nothing, restates nothing and is like nothing, so it takes no\n * label of its own — the fixed-length grid put a boundary there, the music did\n * not.\n */\nfunction isSilent(unit: Unit): boolean {\n return unit.notes.length === 0;\n}\n\n/**\n * How alike two units are, in [0, 1].\n *\n * Melody and harmony are both asked, because either alone is fooled: the same\n * tune over new chords is a variation and not a restatement, and the same\n * chords under a new tune is a different section of the same song.\n *\n * A figure needs two notes, so a unit holding fewer has no melody to compare.\n * Two such units are compared on their harmony alone; one of them against a\n * unit that does have a melody is not a restatement at all, and scores zero\n * however alike the two harmonies are — a bar reduced to a single held tone is\n * not a statement of the passage it was reduced from. The harmony is compared\n * as pitch-class weight over the unit, which is a shape rather than a series,\n * so it says nothing about the two units being the same length.\n */\nfunction unitSimilarity(a: Unit, b: Unit): number {\n const harmony = weightSimilarity(a.weights, b.weights);\n if (a.notes.length < 2 || b.notes.length < 2) {\n return a.notes.length === b.notes.length ? harmony : 0;\n }\n const melody = melodicSimilarity(a.notes, b.notes);\n return clamp01(MELODY_SHARE * melody + HARMONY_SHARE * harmony);\n}\n\n/**\n * Recover a piece's sections from what it repeats.\n *\n * The span is cut into equal units of `unitBars` bars, each unit is compared\n * with every unit before it, and a unit alike enough to an earlier one takes\n * that unit's label. Neighbouring units carrying the same label are then merged,\n * so an eight-bar section built of two like four-bar units is reported as one\n * section rather than two.\n *\n * Likeness is measured on the melodic line and on the harmonic content\n * together — the same tune under different harmony is a variation, not a\n * restatement — and the labels are letters in order of first appearance.\n * Nothing beyond \"this is a restatement of that\" is asserted: see\n * {@link FormSection.label}.\n *\n * A pickup belongs to the section it leads into, so material before the first\n * bar is folded into the opening unit rather than made a section of its own.\n *\n * @param notes The piece's notes, in any order. Notes that never sound are\n * dropped.\n * @param opts Analysis options; see {@link FormSectionOptions}.\n * @returns The sections, in time order.\n * @example\n * ```ts\n * import { sectionsFromNotes } from '@libraz/libcantus';\n * const bar = (pitch: number, at: number) => ({ pitch, startBeat: at, durationBeat: 4 });\n * const notes = [bar(60, 0), bar(64, 4), bar(67, 8), bar(60, 12)];\n * sectionsFromNotes(notes, { unitBars: 2 }).map((section) => section.label);\n * ```\n * @category Arrangement & Analysis\n */\nexport function sectionsFromNotes(\n notes: readonly NoteEvent[],\n opts: FormSectionOptions = {},\n): FormSection[] {\n const meters = resolveMeters(opts, 'section meters');\n assertNoteEvents(notes, 'section notes', {\n allowNonPositiveDuration: true,\n budget: opts.budget,\n });\n const sounding = notes.filter((note) => note.durationBeat > 0);\n if (sounding.length === 0) {\n // Nothing sounds, so there is no material to hear a form in. A section over\n // an empty span would name a stretch of silence as a statement.\n return [];\n }\n // The pickup rule the whole analysis shares: a note a millibeat before the\n // downbeat is playing it, and only one a whole bar early opens a pickup.\n const firstOnset = barGridStart(firstSoundingBeat(sounding), meters);\n const lastEnd = sounding.reduce((end, n) => Math.max(end, n.startBeat + n.durationBeat), 0);\n const spanEnd = Math.max(firstOnset, opts.totalBeats ?? lastEnd);\n assertRange(spanEnd, 0, Number.MAX_SAFE_INTEGER, 'section totalBeats');\n const threshold = opts.threshold ?? DEFAULT_THRESHOLD;\n assertRange(threshold, 0, 1, 'section threshold');\n const unitBars =\n opts.unitBars ??\n hypermeter(sounding, meters, { totalBeats: spanEnd, budget: opts.budget }).groupBars;\n assertPositiveInt(unitBars, 'section unitBars');\n\n const firstBar = barIndexAt(firstOnset, meters);\n const lastBar = lastBarOf(meters, firstOnset, spanEnd);\n // The units are counted from the bar the music starts in, not from bar 0: an\n // excerpt lifted from bar 9 has no bars 0 to 8 to divide. A pickup leads into\n // the first full bar rather than starting a unit of its own, so it is folded\n // into the opening one.\n const startBar = Math.max(0, firstBar);\n const unitCount = Math.max(1, Math.ceil((lastBar - startBar + 1) / unitBars));\n assertGenerationBudget(unitCount * unitCount, 'form section comparisons', opts.budget);\n assertGenerationBudget(unitCount * sounding.length, 'form section unit notes', opts.budget);\n\n const units: Unit[] = [];\n for (let index = 0; index < unitCount; index += 1) {\n const barStart = startBar + index * unitBars;\n const startBeat =\n index === 0 ? firstOnset : barPositionToBeat({ bar: barStart, beat: 0 }, meters);\n const endBeat =\n index === unitCount - 1\n ? spanEnd\n : barPositionToBeat({ bar: barStart + unitBars, beat: 0 }, meters);\n const unitNotes = sounding\n .filter(\n (note) => note.startBeat >= startBeat - BEAT_EPS && note.startBeat < endBeat - BEAT_EPS,\n )\n .sort((a, b) => a.startBeat - b.startBeat || a.pitch - b.pitch);\n units.push({\n startBeat,\n endBeat,\n notes: unitNotes,\n weights: windowWeights(unitNotes, startBeat, endBeat, meters).weights,\n });\n }\n\n // Comparing two units runs an edit distance over their notes, so what the\n // comparisons cost is the sum over pairs of the product of what each holds —\n // not the pair count, and not the pair count times the piece. A piece whose\n // notes gather in a few units keeps both of those inside the budget while the\n // distances themselves run for seconds, so the sum is charged before the\n // first of them is run.\n let comparisons = 0;\n let earlierNotes = 0;\n for (const unit of units) {\n comparisons += unit.notes.length * earlierNotes;\n earlierNotes += unit.notes.length;\n }\n assertGenerationBudget(comparisons, 'form section melody comparisons', opts.budget);\n\n // Label every unit, then merge the neighbours that agree. A silent unit takes\n // no label: it is carried by the section it follows, so the letters count\n // statements rather than stretches of the grid.\n const labels: (string | null)[] = [];\n const scores: number[] = [];\n let distinct = 0;\n for (let index = 0; index < units.length; index += 1) {\n const unit = units[index];\n if (unit === undefined) {\n continue;\n }\n if (isSilent(unit)) {\n labels.push(null);\n scores.push(scores[index - 1] ?? 1);\n continue;\n }\n let bestScore = -1;\n let bestIndex = -1;\n for (let earlier = 0; earlier < index; earlier += 1) {\n const other = units[earlier];\n if (other === undefined || isSilent(other)) {\n continue;\n }\n const score = unitSimilarity(unit, other);\n // Ties keep the earlier match, which is the statement being restated.\n if (score > bestScore) {\n bestScore = score;\n bestIndex = earlier;\n }\n }\n if (bestIndex >= 0 && bestScore >= threshold) {\n labels.push(labels[bestIndex] ?? labelAt(distinct));\n scores.push(bestScore);\n } else {\n labels.push(labelAt(distinct));\n scores.push(1);\n distinct += 1;\n }\n }\n\n const sections: FormSection[] = [];\n const firstByLabel = new Map<string, number>();\n for (let index = 0; index < units.length; ) {\n const label = labels[index] ?? null;\n if (label === null) {\n // Only reachable before anything sounds, which the unit grid rules out:\n // it starts at the first onset. Nothing to report over silence either way.\n index += 1;\n continue;\n }\n let end = index;\n let scoreSum = 0;\n let sounded = 0;\n while (end < units.length && labels[end] === label) {\n scoreSum += scores[end] ?? 1;\n sounded += 1;\n end += 1;\n }\n // Silence following the statement belongs to it — a section runs on until\n // the next one begins — but it also closes it: a restatement after a general\n // pause is heard as a second statement, not as more of the first.\n while (end < units.length && labels[end] === null) {\n end += 1;\n }\n const startBeat = units[index]?.startBeat ?? 0;\n const endBeat = units[end - 1]?.endBeat ?? spanEnd;\n // Counted on the unit grid the sections were merged on, not from the raw\n // start beat: a pickup was folded into the opening unit, so re-deriving the\n // length from its beat would hand the first section a bar the grid does not\n // hold and leave it off the multiple of `unitBars` a section is built from.\n const sectionFirstBar = startBar + index * unitBars;\n const sectionLastBar = Math.max(\n sectionFirstBar,\n Math.min(lastBar, startBar + end * unitBars - 1),\n );\n const sectionIndex = sections.length;\n const first = firstByLabel.get(label);\n if (first === undefined) {\n firstByLabel.set(label, sectionIndex);\n }\n const similarity = first === undefined ? 1 : clamp01(scoreSum / Math.max(1, sounded));\n sections.push({\n label,\n startBeat,\n endBeat,\n bars: sectionLastBar - sectionFirstBar + 1,\n firstOccurrence: first ?? sectionIndex,\n similarity,\n rationale:\n first === undefined\n ? `Section ${label}: material first heard at beat ${startBeat}`\n : `Section ${label}: ${Math.round(similarity * 100)}% alike the ${label} at beat ${\n sections[first]?.startBeat ?? 0\n }`,\n });\n index = end;\n }\n return sections;\n}\n","/**\n * Harmonic reduction: which chords carry the progression and which decorate it.\n *\n * `analyzeVoice` can say that a note is a passing tone; a timeline is flat, so\n * nothing could say the same of a chord. This is that answer one level up — the\n * middle chord of `Cmaj7 - C#dim7 - Dm7` is an embellishment, and the frame\n * under `I - IV - #ivo7 - V - I` is `I - IV - V - I`.\n *\n * **The position taken here.** What counts as structural is a theoretical\n * choice, not a fact, and this module makes the functional one: *a chord is\n * structural unless it is demonstrably an embellishment of its neighbours*. The\n * frame is what carries harmonic function in the prevailing key — the tonic, the\n * dominant, and the chords that cadence — so those are never reduced away; a\n * chord that tonicizes something other than the tonic is prolonging, not\n * framing, however dominant its sonority. Everything else keeps its place unless\n * its root traces one of two figures against the chords around it, the same two\n * figures {@link analyzeVoice} matches at the note level: a step through\n * (passing) and a step away and back (neighbor). Defaulting to structural, and\n * demoting only on evidence, is what keeps a reduction from quietly deleting\n * harmony it merely failed to recognize.\n *\n * The salience reading — that the frame is whatever lasts longest, regardless of\n * function — is the main alternative, and it is a live one for repertoire where\n * function is weak. {@link ReduceProgressionOptions.basis} switches to it: the\n * figures stay the same, only what protects a chord from them changes.\n *\n * Both readings are deterministic: the same timeline and key always reduce the\n * same way.\n */\n\nimport { pitchClassOf as mod12 } from '../../core/pitch/index.js';\nimport type { KeyScale } from '../../core/types.js';\nimport { assertOneOf, assertOptions } from '../../core/validation/index.js';\nimport type { Chord, ChordSegment } from '../../theory/chord/index.js';\nimport { isDominantChordOf } from '../../theory/tendency/index.js';\n// Segment boundaries are computed, so the end of one and the start of the next\n// meet to within rounding rather than exactly; a real gap is a rest, and chords\n// either side of a rest embellish nothing.\nimport { BEAT_EPS } from '../adjacency.js';\nimport { isCadentialSixFour } from '../functional/cadence.js';\nimport type { CadenceResult } from '../functional/index.js';\nimport { detectCadence } from '../functional/index.js';\nimport { isAppliedDominant } from '../functional/tonicization.js';\nimport { assertChordTimeline, type ChordTimeline } from '../timeline/index.js';\nimport type { KeyContext } from '../voice/index.js';\nimport { keyScaleAt } from '../voice/index.js';\n\n/**\n * The place a chord holds in a progression.\n *\n * `'structural'` is the frame; `'passing'` and `'neighbor'` are the two\n * embellishing figures, spelled exactly as {@link TheoryLabel} spells them for\n * a note, so one legend covers both levels of analysis.\n *\n * @category Arrangement & Analysis\n */\nexport type ReductionLevel = 'structural' | 'passing' | 'neighbor';\n\n/**\n * A chord with the place it holds, the beats it holds it for, and a short\n * rationale.\n *\n * One entry per timeline segment, in the same order. The entry carries the\n * segment's own beat range, so the chords that survive a filter still say when\n * they sound: holding the structural chords fixed is what a reharmonizer needs,\n * and it cannot hold a chord at a place the filter threw away.\n *\n * @category Arrangement & Analysis\n */\nexport type ReducedChord = {\n chord: Chord;\n startBeat: number;\n endBeat: number;\n level: ReductionLevel;\n rationale: string;\n};\n\n/**\n * What a reduction treats as the frame.\n *\n * `'function'` is the functional reading: the tonic, the dominant and the\n * chords that cadence are the frame. `'duration'` is the salience reading: a\n * chord is the frame when it outlasts the chords around it — its length beats\n * the median of the other chords' — whatever function it carries. Nothing\n * outlasts anything in an even harmonic rhythm, so a loop or a vamp protects no\n * chord by length and the two figures decide, as they do under either reading.\n *\n * @category Arrangement & Analysis\n */\nexport type ReductionBasis = 'function' | 'duration';\n\n/**\n * Options for {@link reduceProgression}.\n *\n * @category Arrangement & Analysis\n */\nexport type ReduceProgressionOptions = {\n /**\n * Which reading decides what the frame is; see {@link ReductionBasis}.\n *\n * @defaultValue `'function'`\n */\n basis?: ReductionBasis;\n};\n\n/** Cadences whose arrival is the tonic or the substitute standing in for it. */\nconst TONIC_ARRIVING: ReadonlySet<string> = new Set(['authentic', 'plagal', 'modal', 'deceptive']);\n\n/** Why a chord was kept in the frame, or which figure demoted it. */\ntype ReductionReason =\n | { kind: 'tonic' }\n | { kind: 'dominant' }\n | { kind: 'cadentialSixFour' }\n | { kind: 'cadenceArrival'; cadence: NonNullable<CadenceResult['type']> }\n | { kind: 'cadenceAgent'; cadence: NonNullable<CadenceResult['type']> }\n | { kind: 'salient' }\n | { kind: 'first' }\n | { kind: 'last' }\n | { kind: 'unfigured' }\n | { kind: 'passing' }\n | { kind: 'neighbor' };\n\n/** Signed semitone distance between two pitch classes, in [-6, 5]. */\nfunction signedStep(fromPc: number, toPc: number): number {\n return ((mod12(toPc) - mod12(fromPc) + 18) % 12) - 6;\n}\n\n/** Whether a root motion is a step: a semitone or a whole tone, either way. */\nfunction isStep(distance: number): boolean {\n const size = Math.abs(distance);\n return size === 1 || size === 2;\n}\n\n/** Whether two segments touch, so that the motion between them is heard. */\nfunction touches(before: ChordSegment, after: ChordSegment): boolean {\n return Math.abs(after.startBeat - before.endBeat) <= BEAT_EPS;\n}\n\n/**\n * The length each segment has to beat to count as salient: the median of the\n * lengths of every *other* segment.\n *\n * Leaving the segment itself out is what keeps the reading from protecting\n * everything at once. A progression whose harmonic rhythm is even — the loop, the\n * modular vamp, the cue — has every segment sitting on the median, so measuring\n * against the median of the whole set would call every chord salient and reduce\n * nothing, in exactly the repertoire the salience reading exists for. Measured\n * against its neighbours, an even progression singles out nobody and the figures\n * decide, while a chord that genuinely outlasts the harmony around it still does.\n */\nfunction salienceFloors(durations: readonly number[]): number[] {\n const order = durations\n .map((_, index) => index)\n .sort((a, b) => {\n const left = durations[a] ?? 0;\n const right = durations[b] ?? 0;\n return left - right || a - b;\n });\n const rankOf: number[] = new Array<number>(durations.length).fill(0);\n for (let rank = 0; rank < order.length; rank += 1) {\n rankOf[order[rank] ?? 0] = rank;\n }\n const floors: number[] = [];\n const remaining = durations.length - 1;\n for (let index = 0; index < durations.length; index += 1) {\n if (remaining <= 0) {\n floors.push(0);\n continue;\n }\n const skipped = rankOf[index] ?? 0;\n const at = (position: number): number =>\n durations[order[position < skipped ? position : position + 1] ?? 0] ?? 0;\n const middle = remaining >> 1;\n floors.push(remaining % 2 === 1 ? at(middle) : (at(middle - 1) + at(middle)) / 2);\n }\n return floors;\n}\n\n/**\n * Whether a chord stands on the key's tonic and is heard as the tonic.\n *\n * The root alone does not settle it. `C7` in C major stands on the tonic and\n * tonicizes the subdominant — the function layer already reads it as a dominant\n * — and this module's position is that a chord tonicizing something other than\n * the tonic is prolonging rather than framing, however it is spelled.\n */\nfunction isTonicChord(chord: Chord, key: KeyScale): boolean {\n return mod12(chord.rootPc - key.rootPc) === 0 && !isAppliedDominant(chord, key);\n}\n\n/**\n * Why a chord belongs to the frame under the functional reading, or null when\n * nothing keeps it there.\n *\n * A cadence protects both of its chords, but asymmetrically: the arrival of any\n * cadence is a goal, while the chord driving one only holds the frame when the\n * cadence reaches the tonic. That asymmetry is what lets a chromatic chord slip\n * into a half cadence — `IV - #ivo7 - V` — and still be heard as passing,\n * while the dominant it arrives on is not.\n */\nfunction functionalReason(\n segment: ChordSegment,\n before: ChordSegment | undefined,\n after: ChordSegment | undefined,\n keyAt: (beat: number) => KeyScale,\n): ReductionReason | null {\n const key = keyAt(segment.startBeat);\n // Asked before the tonic, because the cadential six-four is a tonic triad and\n // would otherwise be kept as one: it stands on the bass of the dominant that\n // follows and is that dominant, not the tonic framing the progression. A\n // six-four the bass leaves — the passing `IV-I64-IV` — answers false here and\n // is read as the inverted tonic it is.\n if (after !== undefined && isCadentialSixFour(segment.chord, after.chord, key)) {\n return { kind: 'cadentialSixFour' };\n }\n if (isTonicChord(segment.chord, key)) {\n return { kind: 'tonic' };\n }\n if (isDominantChordOf(segment.chord, key)) {\n return { kind: 'dominant' };\n }\n if (before !== undefined) {\n const arrival = detectCadence(before.chord, segment.chord, key).type;\n if (arrival !== null) {\n return { kind: 'cadenceArrival', cadence: arrival };\n }\n }\n if (after !== undefined) {\n const agent = detectCadence(segment.chord, after.chord, keyAt(after.startBeat)).type;\n if (agent !== null && TONIC_ARRIVING.has(agent)) {\n return { kind: 'cadenceAgent', cadence: agent };\n }\n }\n return null;\n}\n\n/** The figure a chord's root traces against its neighbours, or null for none. */\nfunction figureReason(\n segment: ChordSegment,\n before: ChordSegment | undefined,\n after: ChordSegment | undefined,\n): ReductionReason | null {\n if (before === undefined || after === undefined) {\n return null;\n }\n const incoming = signedStep(before.chord.rootPc, segment.chord.rootPc);\n const outgoing = signedStep(segment.chord.rootPc, after.chord.rootPc);\n if (!isStep(incoming) || !isStep(outgoing)) {\n return null;\n }\n // A step in and a step on in the same direction passes through; a step away\n // from a harmony that comes back is a neighbour to it.\n if (incoming > 0 === outgoing > 0) {\n return { kind: 'passing' };\n }\n return mod12(before.chord.rootPc) === mod12(after.chord.rootPc) ? { kind: 'neighbor' } : null;\n}\n\n/** The rationale for a reduction level, in the phrasing a note label uses. */\nfunction describe(reason: ReductionReason): string {\n switch (reason.kind) {\n case 'tonic':\n return 'Structural: the tonic of the key, which the progression is heard against';\n case 'dominant':\n return 'Structural: the dominant of the key, the chord its tonic is approached from';\n case 'cadentialSixFour':\n return 'Structural: a tonic six-four on the bass of the dominant that follows, which prolongs that dominant rather than framing the tonic';\n case 'cadenceArrival':\n return `Structural: the arrival of a ${reason.cadence} cadence`;\n case 'cadenceAgent':\n return `Structural: the chord that drives a ${reason.cadence} cadence`;\n case 'salient':\n return 'Structural: it holds the harmony longer than the chords around it';\n case 'first':\n return 'Structural: the first chord of the progression frames it';\n case 'last':\n return 'Structural: the last chord of the progression frames it';\n case 'unfigured':\n return 'Structural: its root neither steps through to the next chord nor returns to the last, so it embellishes nothing';\n case 'passing':\n return 'Passing: its root steps in from the previous chord and on to the next in the same direction';\n case 'neighbor':\n return 'Neighbor: its root steps away from the surrounding harmony and returns to it';\n }\n}\n\n/** What a reading is given to decide whether a chord holds the frame. */\ntype FrameContext = {\n segment: ChordSegment;\n before: ChordSegment | undefined;\n after: ChordSegment | undefined;\n keyAt: (beat: number) => KeyScale;\n /** The length this segment has to outlast; see {@link salienceFloors}. */\n salienceFloor: number;\n};\n\n/**\n * What keeps a chord in the frame, one entry per reading of what a frame is.\n *\n * {@link ReductionBasis} describes the two readings; the entrance check reads\n * their names out of this table, so a reading cannot be added without becoming\n * accepted input in the same edit.\n */\nconst BASIS_READINGS: Record<ReductionBasis, (context: FrameContext) => ReductionReason | null> = {\n function: ({ segment, before, after, keyAt }) => functionalReason(segment, before, after, keyAt),\n duration: ({ segment, salienceFloor }) =>\n segment.endBeat - segment.startBeat > salienceFloor ? { kind: 'salient' } : null,\n};\n\n/** Every reading's name, read from the table so the two cannot drift apart. */\nconst REDUCTION_BASES = Object.keys(BASIS_READINGS) as ReductionBasis[];\n\n/** The level a reason places a chord at. */\nfunction levelOf(reason: ReductionReason): ReductionLevel {\n if (reason.kind === 'passing') {\n return 'passing';\n }\n return reason.kind === 'neighbor' ? 'neighbor' : 'structural';\n}\n\n/**\n * Reduce a chord progression to its frame, labelling every chord.\n *\n * One entry comes back per timeline segment, in the same order, so the two read\n * in step. Filtering the result to `'structural'` is the skeleton view — the\n * `I - IV - V - I` under a progression that spells far more chords than that —\n * and holding those chords fixed is what a reharmonizer needs in order to\n * rewrite the surface without moving the harmony. Each entry carries its own\n * `startBeat` and `endBeat`, so a filtered chord still knows where it sounds\n * once the segments it was read against are gone.\n *\n * A chord is structural unless one of two figures demotes it: its root stepping\n * in from the previous chord and on to the next in the same direction (passing),\n * or stepping away from a harmony that returns (neighbor). Chords carrying the\n * frame are never demoted, and what carries the frame is the module's stated\n * theoretical position — see {@link ReductionBasis} for the reading this takes\n * by default and the one it can be switched to.\n *\n * Chords separated by a rest form no figure across it, the way\n * {@link detectCadences} pairs no chords across one.\n *\n * @param timeline The chord timeline to reduce.\n * @param key The prevailing key, or the key in force at a given beat, for music\n * that modulates.\n * @param opts Which reading decides the frame; see\n * {@link ReduceProgressionOptions}.\n * @returns One labelled chord per segment, in time order.\n * @throws If `opts.basis` is not one of the readings {@link ReductionBasis}\n * names.\n * @example\n * ```ts\n * import { chordTimelineFromChords, majorKey, reduceProgression } from '@libraz/libcantus';\n * const timeline = chordTimelineFromChords(\n * [\n * { rootPc: 0, quality: 'maj7', startBeat: 0 },\n * { rootPc: 1, quality: 'dim7', startBeat: 4 },\n * { rootPc: 2, quality: 'min7', startBeat: 8 },\n * ],\n * 12,\n * );\n * reduceProgression(timeline, majorKey(0)).map((entry) => entry.level);\n * // ['structural', 'passing', 'structural']\n * ```\n * @category Arrangement & Analysis\n */\nexport function reduceProgression(\n timeline: ChordTimeline,\n key: KeyContext,\n opts: ReduceProgressionOptions = {},\n): ReducedChord[] {\n assertChordTimeline(timeline);\n const asked = assertOptions(opts, 'opts');\n const keyAt = keyScaleAt(key);\n const basis = assertOneOf(asked.basis ?? 'function', REDUCTION_BASES, 'reduction basis');\n const reading = BASIS_READINGS[basis];\n const segments = timeline.segments;\n const floors =\n basis === 'duration'\n ? salienceFloors(segments.map((segment) => segment.endBeat - segment.startBeat))\n : [];\n const result: ReducedChord[] = [];\n\n for (let index = 0; index < segments.length; index += 1) {\n const segment = segments[index];\n if (segment === undefined) {\n continue;\n }\n const previous = segments[index - 1];\n const following = segments[index + 1];\n const before = previous !== undefined && touches(previous, segment) ? previous : undefined;\n const after = following !== undefined && touches(segment, following) ? following : undefined;\n\n let reason = reading({ segment, before, after, keyAt, salienceFloor: floors[index] ?? 0 });\n if (reason === null && index === 0) {\n reason = { kind: 'first' };\n }\n if (reason === null && index === segments.length - 1) {\n reason = { kind: 'last' };\n }\n reason ??= figureReason(segment, before, after) ?? { kind: 'unfigured' };\n\n result.push({\n chord: segment.chord,\n startBeat: segment.startBeat,\n endBeat: segment.endBeat,\n level: levelOf(reason),\n rationale: describe(reason),\n });\n }\n\n return result;\n}\n"],"mappings":"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","names":["clamp01","best"]}