@libraz/libcantus 1.1.0 → 1.3.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 (103) hide show
  1. package/README.md +4 -0
  2. package/README_ja.md +4 -0
  3. package/dist/analyze/index.cjs +31 -7
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +155 -9
  6. package/dist/analyze/index.d.ts +155 -9
  7. package/dist/analyze/index.js +37 -13
  8. package/dist/{chunk-TT6PKMPR.cjs → chunk-35NBV2FT.cjs} +16 -16
  9. package/dist/{chunk-TT6PKMPR.cjs.map → chunk-35NBV2FT.cjs.map} +1 -1
  10. package/dist/{chunk-YPB3A3EY.cjs → chunk-3R3QVKY5.cjs} +314 -285
  11. package/dist/chunk-3R3QVKY5.cjs.map +1 -0
  12. package/dist/{chunk-GDKE6RQW.cjs → chunk-4R2LWV7E.cjs} +691 -413
  13. package/dist/chunk-4R2LWV7E.cjs.map +1 -0
  14. package/dist/{chunk-RVS4G4MR.cjs → chunk-76ZBTRG2.cjs} +198 -198
  15. package/dist/{chunk-RVS4G4MR.cjs.map → chunk-76ZBTRG2.cjs.map} +1 -1
  16. package/dist/{chunk-X6RJAHII.js → chunk-7KG3RTYS.js} +2 -2
  17. package/dist/{chunk-PQL7E5F6.js → chunk-AGOVW7U6.js} +1 -1
  18. package/dist/{chunk-PQL7E5F6.js.map → chunk-AGOVW7U6.js.map} +1 -1
  19. package/dist/{chunk-5KW7DNCJ.cjs → chunk-B2PW6EHN.cjs} +2423 -453
  20. package/dist/chunk-B2PW6EHN.cjs.map +1 -0
  21. package/dist/{chunk-J772DL3B.cjs → chunk-CGBADIO5.cjs} +53 -53
  22. package/dist/{chunk-J772DL3B.cjs.map → chunk-CGBADIO5.cjs.map} +1 -1
  23. package/dist/{chunk-R5VUXQMP.js → chunk-CPMB7P75.js} +1978 -8
  24. package/dist/chunk-CPMB7P75.js.map +1 -0
  25. package/dist/{chunk-VLE5R7PF.js → chunk-GNVN4PPJ.js} +1874 -8
  26. package/dist/chunk-GNVN4PPJ.js.map +1 -0
  27. package/dist/{chunk-Q6R3QAZD.cjs → chunk-IFZDWIKM.cjs} +2199 -333
  28. package/dist/chunk-IFZDWIKM.cjs.map +1 -0
  29. package/dist/{chunk-DKICR7AN.js → chunk-IPEF6NWB.js} +296 -18
  30. package/dist/chunk-IPEF6NWB.js.map +1 -0
  31. package/dist/{chunk-AWEMD747.js → chunk-M5RYMAFI.js} +3 -3
  32. package/dist/{chunk-HSY2VQXZ.js → chunk-MLBPXA4Z.js} +330 -728
  33. package/dist/chunk-MLBPXA4Z.js.map +1 -0
  34. package/dist/{chunk-H66ARFU5.js → chunk-NB25P3EH.js} +3 -3
  35. package/dist/{chunk-5JJ6EHEZ.cjs → chunk-NM7UAMC3.cjs} +62 -62
  36. package/dist/{chunk-5JJ6EHEZ.cjs.map → chunk-NM7UAMC3.cjs.map} +1 -1
  37. package/dist/{chunk-R7B5N2JK.cjs → chunk-PM4XJE2Y.cjs} +47 -47
  38. package/dist/{chunk-R7B5N2JK.cjs.map → chunk-PM4XJE2Y.cjs.map} +1 -1
  39. package/dist/{chunk-7GOKQJTQ.js → chunk-PUKJP2Z5.js} +2 -2
  40. package/dist/{chunk-QCXYKSZS.cjs → chunk-PWC4CRWT.cjs} +19 -19
  41. package/dist/{chunk-QCXYKSZS.cjs.map → chunk-PWC4CRWT.cjs.map} +1 -1
  42. package/dist/{chunk-FPBZFNCD.js → chunk-RFIBF2RR.js} +47 -18
  43. package/dist/{chunk-FPBZFNCD.js.map → chunk-RFIBF2RR.js.map} +1 -1
  44. package/dist/{chunk-XKSO2V5I.cjs → chunk-S5BKZZGM.cjs} +1 -1
  45. package/dist/{chunk-XKSO2V5I.cjs.map → chunk-S5BKZZGM.cjs.map} +1 -1
  46. package/dist/{chunk-HOHFYGKW.js → chunk-UMWZ4ZET.js} +2 -2
  47. package/dist/{chunk-XVMJO3QF.cjs → chunk-YFA7X2F6.cjs} +489 -887
  48. package/dist/chunk-YFA7X2F6.cjs.map +1 -0
  49. package/dist/{chunk-57WB2EDF.js → chunk-ZBUROTJQ.js} +3 -3
  50. package/dist/{coerce-wwdELNYS.d.ts → coerce-DP0vYu-x.d.ts} +77 -1
  51. package/dist/{coerce-CJlgULrQ.d.cts → coerce-DRERzdve.d.cts} +77 -1
  52. package/dist/core/index.cjs +5 -5
  53. package/dist/core/index.d.cts +2 -2
  54. package/dist/core/index.d.ts +2 -2
  55. package/dist/core/index.js +4 -4
  56. package/dist/generate/index.cjs +20 -8
  57. package/dist/generate/index.cjs.map +1 -1
  58. package/dist/generate/index.d.cts +87 -9
  59. package/dist/generate/index.d.ts +87 -9
  60. package/dist/generate/index.js +19 -7
  61. package/dist/{index-BaoKVVhE.d.cts → index-B9QLziDe.d.cts} +2 -2
  62. package/dist/{index-CnHQgqNh.d.ts → index-BHIUyjhZ.d.ts} +2 -2
  63. package/dist/{index-CBCP84rv.d.cts → index-BL5N2Oyz.d.cts} +1 -1
  64. package/dist/{index-D_hzxD8w.d.cts → index-BMweUlm5.d.cts} +3 -0
  65. package/dist/{index-Ci3xyows.d.ts → index-CL4b4gUQ.d.ts} +3 -0
  66. package/dist/{index-BzLNkmA7.d.cts → index-CY-3NVfy.d.cts} +486 -5
  67. package/dist/{index-CnIdP4EZ.d.ts → index-Dw2DP7vm.d.ts} +1 -1
  68. package/dist/{index-DpC-mDlV.d.ts → index-zaZdmnU4.d.ts} +486 -5
  69. package/dist/index.cjs +49 -13
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.d.cts +10 -10
  72. package/dist/index.d.ts +10 -10
  73. package/dist/index.js +55 -19
  74. package/dist/{masks-O73Rhj5d.d.ts → masks-Cc7-MzzY.d.ts} +1 -1
  75. package/dist/{masks-C8TWLFIi.d.cts → masks-pADfTOiD.d.cts} +1 -1
  76. package/dist/model/index.cjs +12 -12
  77. package/dist/model/index.d.cts +213 -16
  78. package/dist/model/index.d.ts +213 -16
  79. package/dist/model/index.js +11 -11
  80. package/dist/{index-DTsKaPaC.d.cts → profile-CQMEqdH9.d.cts} +88 -601
  81. package/dist/{index-BUtIf2go.d.ts → profile-DLdlIpS9.d.ts} +88 -601
  82. package/dist/theory/index.cjs +5 -5
  83. package/dist/theory/index.d.cts +7 -7
  84. package/dist/theory/index.d.ts +7 -7
  85. package/dist/theory/index.js +4 -4
  86. package/dist/{index-BpWOhkkE.d.ts → types-D-XgxGvp.d.cts} +1170 -14
  87. package/dist/{index-B3WCj_Hm.d.cts → types-Dh7C064C.d.ts} +1170 -14
  88. package/package.json +6 -5
  89. package/dist/chunk-5KW7DNCJ.cjs.map +0 -1
  90. package/dist/chunk-DKICR7AN.js.map +0 -1
  91. package/dist/chunk-GDKE6RQW.cjs.map +0 -1
  92. package/dist/chunk-HSY2VQXZ.js.map +0 -1
  93. package/dist/chunk-Q6R3QAZD.cjs.map +0 -1
  94. package/dist/chunk-R5VUXQMP.js.map +0 -1
  95. package/dist/chunk-VLE5R7PF.js.map +0 -1
  96. package/dist/chunk-XVMJO3QF.cjs.map +0 -1
  97. package/dist/chunk-YPB3A3EY.cjs.map +0 -1
  98. /package/dist/{chunk-X6RJAHII.js.map → chunk-7KG3RTYS.js.map} +0 -0
  99. /package/dist/{chunk-AWEMD747.js.map → chunk-M5RYMAFI.js.map} +0 -0
  100. /package/dist/{chunk-H66ARFU5.js.map → chunk-NB25P3EH.js.map} +0 -0
  101. /package/dist/{chunk-7GOKQJTQ.js.map → chunk-PUKJP2Z5.js.map} +0 -0
  102. /package/dist/{chunk-HOHFYGKW.js.map → chunk-UMWZ4ZET.js.map} +0 -0
  103. /package/dist/{chunk-57WB2EDF.js.map → chunk-ZBUROTJQ.js.map} +0 -0
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkXKSO2V5Icjs = require('./chunk-XKSO2V5I.cjs');
6
+ var _chunkS5BKZZGMcjs = require('./chunk-S5BKZZGM.cjs');
7
7
 
8
8
  // src/core/event-index/index.ts
9
9
  function upperBound(values, beat) {
@@ -25,8 +25,8 @@ function sortedNoteEvents(events) {
25
25
  return sorted;
26
26
  }
27
27
  function createNoteEventIndex(events, options = {}) {
28
- const asked = _chunkXKSO2V5Icjs.assertOptions.call(void 0, options, "options");
29
- _chunkXKSO2V5Icjs.assertNoteEvents.call(void 0, events, _nullishCoalesce(asked.name, () => ( "note events")), asked);
28
+ const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, options, "options");
29
+ _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, events, _nullishCoalesce(asked.name, () => ( "note events")), asked);
30
30
  const tieBreak = _nullishCoalesce(asked.tieBreak, () => ( "highest"));
31
31
  const notes = Object.freeze(
32
32
  events.map(
@@ -61,7 +61,7 @@ function createNoteEventIndex(events, options = {}) {
61
61
  const latestActiveIndex = (exclusive, beat) => {
62
62
  const tree = endTree();
63
63
  const find = (node, start, end) => {
64
- if (start >= exclusive || (_nullishCoalesce(tree[node], () => ( Number.NEGATIVE_INFINITY))) <= beat + _chunkXKSO2V5Icjs.BEAT_EPS) {
64
+ if (start >= exclusive || (_nullishCoalesce(tree[node], () => ( Number.NEGATIVE_INFINITY))) <= beat + _chunkS5BKZZGMcjs.BEAT_EPS) {
65
65
  return -1;
66
66
  }
67
67
  if (end - start === 1) return start < notes.length ? start : -1;
@@ -74,15 +74,15 @@ function createNoteEventIndex(events, options = {}) {
74
74
  return {
75
75
  notes,
76
76
  at(beat) {
77
- _chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, beat, "beat");
78
- const index = latestActiveIndex(upperBound(notes, beat + _chunkXKSO2V5Icjs.BEAT_EPS), beat);
77
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, beat, "beat");
78
+ const index = latestActiveIndex(upperBound(notes, beat + _chunkS5BKZZGMcjs.BEAT_EPS), beat);
79
79
  if (index < 0) return void 0;
80
80
  const onset = _optionalChain([notes, 'access', _6 => _6[index], 'optionalAccess', _7 => _7.note, 'access', _8 => _8.startBeat]);
81
81
  let best;
82
82
  for (let candidateIndex = index; candidateIndex >= 0; candidateIndex -= 1) {
83
83
  const indexed = notes[candidateIndex];
84
84
  if (indexed !== void 0 && indexed.note.startBeat !== onset) break;
85
- if (indexed === void 0 || indexed.note.durationBeat <= 0 || indexed.note.startBeat - _chunkXKSO2V5Icjs.BEAT_EPS > beat || beat >= indexed.endBeat - _chunkXKSO2V5Icjs.BEAT_EPS) {
85
+ if (indexed === void 0 || indexed.note.durationBeat <= 0 || indexed.note.startBeat - _chunkS5BKZZGMcjs.BEAT_EPS > beat || beat >= indexed.endBeat - _chunkS5BKZZGMcjs.BEAT_EPS) {
86
86
  continue;
87
87
  }
88
88
  if (best === void 0) {
@@ -99,11 +99,11 @@ function createNoteEventIndex(events, options = {}) {
99
99
  return best;
100
100
  },
101
101
  attacksAt(beat) {
102
- _chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, beat, "beat");
103
- let index = upperBound(notes, beat + _chunkXKSO2V5Icjs.BEAT_EPS) - 1;
102
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, beat, "beat");
103
+ let index = upperBound(notes, beat + _chunkS5BKZZGMcjs.BEAT_EPS) - 1;
104
104
  while (index >= 0) {
105
105
  const candidate = notes[index];
106
- if (candidate === void 0 || Math.abs(candidate.note.startBeat - beat) >= _chunkXKSO2V5Icjs.BEAT_EPS) {
106
+ if (candidate === void 0 || Math.abs(candidate.note.startBeat - beat) >= _chunkS5BKZZGMcjs.BEAT_EPS) {
107
107
  return false;
108
108
  }
109
109
  if (candidate.note.durationBeat > 0) {
@@ -114,14 +114,14 @@ function createNoteEventIndex(events, options = {}) {
114
114
  return false;
115
115
  },
116
116
  onsetsBetween(startBeat, endBeat) {
117
- _chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, startBeat, "startBeat");
118
- _chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, endBeat, "endBeat");
117
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, startBeat, "startBeat");
118
+ _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, endBeat, "endBeat");
119
119
  const result = [];
120
- let index = upperBound(notes, startBeat + _chunkXKSO2V5Icjs.BEAT_EPS);
120
+ let index = upperBound(notes, startBeat + _chunkS5BKZZGMcjs.BEAT_EPS);
121
121
  while (index < notes.length) {
122
122
  const indexed = notes[index];
123
123
  const onset = _optionalChain([indexed, 'optionalAccess', _9 => _9.note, 'access', _10 => _10.startBeat]);
124
- if (indexed === void 0 || onset === void 0 || onset >= endBeat - _chunkXKSO2V5Icjs.BEAT_EPS) {
124
+ if (indexed === void 0 || onset === void 0 || onset >= endBeat - _chunkS5BKZZGMcjs.BEAT_EPS) {
125
125
  break;
126
126
  }
127
127
  index += 1;
@@ -129,7 +129,7 @@ function createNoteEventIndex(events, options = {}) {
129
129
  continue;
130
130
  }
131
131
  const previous = result[result.length - 1];
132
- if (previous === void 0 || Math.abs(previous - onset) >= _chunkXKSO2V5Icjs.BEAT_EPS) {
132
+ if (previous === void 0 || Math.abs(previous - onset) >= _chunkS5BKZZGMcjs.BEAT_EPS) {
133
133
  result.push(onset);
134
134
  }
135
135
  }
@@ -142,4 +142,4 @@ function createNoteEventIndex(events, options = {}) {
142
142
 
143
143
 
144
144
  exports.sortedNoteEvents = sortedNoteEvents; exports.createNoteEventIndex = createNoteEventIndex;
145
- //# sourceMappingURL=chunk-TT6PKMPR.cjs.map
145
+ //# sourceMappingURL=chunk-35NBV2FT.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/libcantus/libcantus/dist/chunk-TT6PKMPR.cjs","../src/core/event-index/index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/libcantus/libcantus/dist/chunk-TT6PKMPR.cjs","sourcesContent":[null,"import { BEAT_EPS } from '../meter/internal.js';\nimport type { NoteEvent } from '../types.js';\nimport { assertFiniteNumber, assertNoteEvents, assertOptions } from '../validation/index.js';\n\n/**\n * A validated note retaining its position in the caller's original array.\n *\n * @category Core\n */\nexport type IndexedNoteEvent = {\n readonly note: Readonly<NoteEvent>;\n readonly originalIndex: number;\n readonly endBeat: number;\n};\n\n/**\n * Which note wins when several sound from the same onset.\n *\n * `'highest'` and `'lowest'` name a voice, so the answer does not depend on the\n * order the caller happened to store the chord in; `'last'` takes the one\n * latest in the input array. Two notes of the same pitch name no voice apart,\n * so both voice tie-breaks settle a unison on the lower `originalIndex` and\n * name the same note.\n *\n * @category Core\n */\nexport type OnsetTieBreak = 'highest' | 'lowest' | 'last';\n\n/**\n * Binary-searchable, stable-sorted index over note onsets and active spans.\n *\n * @category Core\n */\nexport type NoteEventIndex = {\n notes: readonly IndexedNoteEvent[];\n /**\n * Latest-onset note sounding at `beat`. Simultaneous onsets are resolved by\n * the index's {@link OnsetTieBreak}.\n *\n * @throws If `beat` is not finite.\n */\n at: (beat: number) => IndexedNoteEvent | undefined;\n /**\n * Whether one or more sounding notes attack at `beat`.\n *\n * @throws If `beat` is not finite.\n */\n attacksAt: (beat: number) => boolean;\n /**\n * Unique sounding-note attack beats strictly inside `(startBeat, endBeat)`.\n *\n * @throws If either bound is not finite.\n */\n onsetsBetween: (startBeat: number, endBeat: number) => number[];\n};\n\nfunction upperBound(values: readonly IndexedNoteEvent[], beat: number): number {\n let low = 0;\n let high = values.length;\n while (low < high) {\n const middle = Math.floor((low + high) / 2);\n if ((values[middle]?.note.startBeat ?? Number.POSITIVE_INFINITY) <= beat) {\n low = middle + 1;\n } else {\n high = middle;\n }\n }\n return low;\n}\n\n/**\n * Copy note events and put them in stable onset order.\n *\n * The order is the one {@link createNoteEventIndex} numbers its notes in, so a\n * pass may read the sequence here and the index there and see the same notes in\n * the same places. What it does not build is the index itself: a caller that\n * only walks the notes in time order would otherwise pay for a segment tree and\n * a wrapper per note that it never queries.\n *\n * The events are copied, so the sequence is the caller's own and two entries\n * that arrived as one shared object stay two.\n *\n * @param events The events to order; already validated by the caller.\n * @returns The copies, earliest onset first, ties in input order.\n */\nexport function sortedNoteEvents(events: readonly NoteEvent[]): NoteEvent[] {\n const sorted = events.map((note) => ({ ...note }));\n // Array sort is stable, so events sharing an onset keep the order they were\n // given in — the tie-break the index spells out as `originalIndex`.\n sorted.sort((a, b) => a.startBeat - b.startBeat);\n return sorted;\n}\n\n/**\n * How an index reads the events it is built from.\n *\n * @category Core\n */\nexport type NoteEventIndexOptions = {\n allowNonPositiveDuration?: boolean;\n tieBreak?: OnsetTieBreak;\n budget?: number;\n /** What the events are, for the validation error message. */\n name?: string;\n};\n\n/**\n * Validate and stable-sort note events once, then expose onset and active-note\n * lookups that cost `O(log n + k)`, where `k` is how many notes share the onset\n * the answer comes from. Non-positive-duration notes may be retained for\n * callers that intentionally filter them later, but never count as sounding.\n *\n * @category Core\n */\nexport function createNoteEventIndex(\n events: readonly NoteEvent[],\n options: NoteEventIndexOptions = {},\n): NoteEventIndex {\n const asked = assertOptions(options, 'options');\n assertNoteEvents(events, asked.name ?? 'note events', asked);\n const tieBreak = asked.tieBreak ?? 'highest';\n // Each wrapper is frozen, not only the array and the note inside it: `endBeat`\n // is what the active-note search is built from, so a host that writes to it\n // would leave the index answering from a span nothing else knows about.\n const notes = Object.freeze(\n events\n .map((note, originalIndex) =>\n Object.freeze({\n note: Object.freeze({ ...note }),\n originalIndex,\n endBeat: note.startBeat + note.durationBeat,\n }),\n )\n .sort((a, b) => a.note.startBeat - b.note.startBeat || a.originalIndex - b.originalIndex),\n );\n // Segment-tree maxima find the latest active onset in logarithmic time even\n // when an early, very long note would defeat a prefix-max backwards scan.\n //\n // It is built on the first `at` query rather than on construction: the onset\n // queries answer from the sorted notes alone, so a caller that never asks\n // which note is sounding would otherwise pay for a structure it never reads.\n let maxEndTree: number[] | undefined;\n let treeSize = 0;\n const endTree = (): number[] => {\n if (maxEndTree !== undefined) {\n return maxEndTree;\n }\n treeSize = 1;\n while (treeSize < notes.length) treeSize *= 2;\n const tree = new Array<number>(treeSize * 2).fill(Number.NEGATIVE_INFINITY);\n for (let index = 0; index < notes.length; index += 1) {\n tree[treeSize + index] = notes[index]?.endBeat ?? Number.NEGATIVE_INFINITY;\n }\n for (let index = treeSize - 1; index > 0; index -= 1) {\n tree[index] = Math.max(\n tree[index * 2] ?? Number.NEGATIVE_INFINITY,\n tree[index * 2 + 1] ?? Number.NEGATIVE_INFINITY,\n );\n }\n maxEndTree = tree;\n return tree;\n };\n const latestActiveIndex = (exclusive: number, beat: number): number => {\n const tree = endTree();\n const find = (node: number, start: number, end: number): number => {\n if (start >= exclusive || (tree[node] ?? Number.NEGATIVE_INFINITY) <= beat + BEAT_EPS) {\n return -1;\n }\n if (end - start === 1) return start < notes.length ? start : -1;\n const mid = Math.floor((start + end) / 2);\n const right = find(node * 2 + 1, mid, end);\n return right >= 0 ? right : find(node * 2, start, mid);\n };\n return find(1, 0, treeSize);\n };\n\n return {\n notes,\n at(beat) {\n // A query beat comes from wherever the host got it — a scrub position, a\n // parsed field — so it is checked like any other entry argument. `NaN`\n // compares false against every bound, which would read as an open window\n // rather than as the missing value it is.\n assertFiniteNumber(beat, 'beat');\n const index = latestActiveIndex(upperBound(notes, beat + BEAT_EPS), beat);\n if (index < 0) return undefined;\n const onset = notes[index]?.note.startBeat;\n let best: IndexedNoteEvent | undefined;\n for (let candidateIndex = index; candidateIndex >= 0; candidateIndex -= 1) {\n const indexed = notes[candidateIndex];\n if (indexed !== undefined && indexed.note.startBeat !== onset) break;\n if (\n indexed === undefined ||\n indexed.note.durationBeat <= 0 ||\n indexed.note.startBeat - BEAT_EPS > beat ||\n beat >= indexed.endBeat - BEAT_EPS\n ) {\n continue;\n }\n if (best === undefined) {\n best = indexed;\n continue;\n }\n if (tieBreak === 'last') continue;\n const difference = indexed.note.pitch - best.note.pitch;\n // Equal pitches name no voice apart, so they are settled on the lower\n // original index rather than on whichever end of the array the scan\n // happens to reach last — otherwise `'highest'` and `'lowest'` would\n // answer a unison differently.\n const wins =\n difference === 0\n ? indexed.originalIndex < best.originalIndex\n : difference > 0 === (tieBreak === 'highest');\n if (wins) {\n best = indexed;\n }\n }\n return best;\n },\n attacksAt(beat) {\n assertFiniteNumber(beat, 'beat');\n // Only a sounding note attacks: a zero-length artefact must not make\n // `attacksAt(b)` true on a beat where `at(b)` finds nothing.\n let index = upperBound(notes, beat + BEAT_EPS) - 1;\n while (index >= 0) {\n const candidate = notes[index];\n if (candidate === undefined || Math.abs(candidate.note.startBeat - beat) >= BEAT_EPS) {\n return false;\n }\n if (candidate.note.durationBeat > 0) {\n return true;\n }\n index -= 1;\n }\n return false;\n },\n onsetsBetween(startBeat, endBeat) {\n assertFiniteNumber(startBeat, 'startBeat');\n assertFiniteNumber(endBeat, 'endBeat');\n const result: number[] = [];\n let index = upperBound(notes, startBeat + BEAT_EPS);\n while (index < notes.length) {\n const indexed = notes[index];\n const onset = indexed?.note.startBeat;\n if (indexed === undefined || onset === undefined || onset >= endBeat - BEAT_EPS) {\n break;\n }\n index += 1;\n if (indexed.note.durationBeat <= 0) {\n continue;\n }\n const previous = result[result.length - 1];\n if (previous === undefined || Math.abs(previous - onset) >= BEAT_EPS) {\n result.push(onset);\n }\n }\n return result;\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/libcantus/libcantus/dist/chunk-35NBV2FT.cjs","../src/core/event-index/index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/libcantus/libcantus/dist/chunk-35NBV2FT.cjs","sourcesContent":[null,"import { BEAT_EPS } from '../meter/internal.js';\nimport type { NoteEvent } from '../types.js';\nimport { assertFiniteNumber, assertNoteEvents, assertOptions } from '../validation/index.js';\n\n/**\n * A validated note retaining its position in the caller's original array.\n *\n * @category Core\n */\nexport type IndexedNoteEvent = {\n readonly note: Readonly<NoteEvent>;\n readonly originalIndex: number;\n readonly endBeat: number;\n};\n\n/**\n * Which note wins when several sound from the same onset.\n *\n * `'highest'` and `'lowest'` name a voice, so the answer does not depend on the\n * order the caller happened to store the chord in; `'last'` takes the one\n * latest in the input array. Two notes of the same pitch name no voice apart,\n * so both voice tie-breaks settle a unison on the lower `originalIndex` and\n * name the same note.\n *\n * @category Core\n */\nexport type OnsetTieBreak = 'highest' | 'lowest' | 'last';\n\n/**\n * Binary-searchable, stable-sorted index over note onsets and active spans.\n *\n * @category Core\n */\nexport type NoteEventIndex = {\n notes: readonly IndexedNoteEvent[];\n /**\n * Latest-onset note sounding at `beat`. Simultaneous onsets are resolved by\n * the index's {@link OnsetTieBreak}.\n *\n * @throws If `beat` is not finite.\n */\n at: (beat: number) => IndexedNoteEvent | undefined;\n /**\n * Whether one or more sounding notes attack at `beat`.\n *\n * @throws If `beat` is not finite.\n */\n attacksAt: (beat: number) => boolean;\n /**\n * Unique sounding-note attack beats strictly inside `(startBeat, endBeat)`.\n *\n * @throws If either bound is not finite.\n */\n onsetsBetween: (startBeat: number, endBeat: number) => number[];\n};\n\nfunction upperBound(values: readonly IndexedNoteEvent[], beat: number): number {\n let low = 0;\n let high = values.length;\n while (low < high) {\n const middle = Math.floor((low + high) / 2);\n if ((values[middle]?.note.startBeat ?? Number.POSITIVE_INFINITY) <= beat) {\n low = middle + 1;\n } else {\n high = middle;\n }\n }\n return low;\n}\n\n/**\n * Copy note events and put them in stable onset order.\n *\n * The order is the one {@link createNoteEventIndex} numbers its notes in, so a\n * pass may read the sequence here and the index there and see the same notes in\n * the same places. What it does not build is the index itself: a caller that\n * only walks the notes in time order would otherwise pay for a segment tree and\n * a wrapper per note that it never queries.\n *\n * The events are copied, so the sequence is the caller's own and two entries\n * that arrived as one shared object stay two.\n *\n * @param events The events to order; already validated by the caller.\n * @returns The copies, earliest onset first, ties in input order.\n */\nexport function sortedNoteEvents(events: readonly NoteEvent[]): NoteEvent[] {\n const sorted = events.map((note) => ({ ...note }));\n // Array sort is stable, so events sharing an onset keep the order they were\n // given in — the tie-break the index spells out as `originalIndex`.\n sorted.sort((a, b) => a.startBeat - b.startBeat);\n return sorted;\n}\n\n/**\n * How an index reads the events it is built from.\n *\n * @category Core\n */\nexport type NoteEventIndexOptions = {\n allowNonPositiveDuration?: boolean;\n tieBreak?: OnsetTieBreak;\n budget?: number;\n /** What the events are, for the validation error message. */\n name?: string;\n};\n\n/**\n * Validate and stable-sort note events once, then expose onset and active-note\n * lookups that cost `O(log n + k)`, where `k` is how many notes share the onset\n * the answer comes from. Non-positive-duration notes may be retained for\n * callers that intentionally filter them later, but never count as sounding.\n *\n * @category Core\n */\nexport function createNoteEventIndex(\n events: readonly NoteEvent[],\n options: NoteEventIndexOptions = {},\n): NoteEventIndex {\n const asked = assertOptions(options, 'options');\n assertNoteEvents(events, asked.name ?? 'note events', asked);\n const tieBreak = asked.tieBreak ?? 'highest';\n // Each wrapper is frozen, not only the array and the note inside it: `endBeat`\n // is what the active-note search is built from, so a host that writes to it\n // would leave the index answering from a span nothing else knows about.\n const notes = Object.freeze(\n events\n .map((note, originalIndex) =>\n Object.freeze({\n note: Object.freeze({ ...note }),\n originalIndex,\n endBeat: note.startBeat + note.durationBeat,\n }),\n )\n .sort((a, b) => a.note.startBeat - b.note.startBeat || a.originalIndex - b.originalIndex),\n );\n // Segment-tree maxima find the latest active onset in logarithmic time even\n // when an early, very long note would defeat a prefix-max backwards scan.\n //\n // It is built on the first `at` query rather than on construction: the onset\n // queries answer from the sorted notes alone, so a caller that never asks\n // which note is sounding would otherwise pay for a structure it never reads.\n let maxEndTree: number[] | undefined;\n let treeSize = 0;\n const endTree = (): number[] => {\n if (maxEndTree !== undefined) {\n return maxEndTree;\n }\n treeSize = 1;\n while (treeSize < notes.length) treeSize *= 2;\n const tree = new Array<number>(treeSize * 2).fill(Number.NEGATIVE_INFINITY);\n for (let index = 0; index < notes.length; index += 1) {\n tree[treeSize + index] = notes[index]?.endBeat ?? Number.NEGATIVE_INFINITY;\n }\n for (let index = treeSize - 1; index > 0; index -= 1) {\n tree[index] = Math.max(\n tree[index * 2] ?? Number.NEGATIVE_INFINITY,\n tree[index * 2 + 1] ?? Number.NEGATIVE_INFINITY,\n );\n }\n maxEndTree = tree;\n return tree;\n };\n const latestActiveIndex = (exclusive: number, beat: number): number => {\n const tree = endTree();\n const find = (node: number, start: number, end: number): number => {\n if (start >= exclusive || (tree[node] ?? Number.NEGATIVE_INFINITY) <= beat + BEAT_EPS) {\n return -1;\n }\n if (end - start === 1) return start < notes.length ? start : -1;\n const mid = Math.floor((start + end) / 2);\n const right = find(node * 2 + 1, mid, end);\n return right >= 0 ? right : find(node * 2, start, mid);\n };\n return find(1, 0, treeSize);\n };\n\n return {\n notes,\n at(beat) {\n // A query beat comes from wherever the host got it — a scrub position, a\n // parsed field — so it is checked like any other entry argument. `NaN`\n // compares false against every bound, which would read as an open window\n // rather than as the missing value it is.\n assertFiniteNumber(beat, 'beat');\n const index = latestActiveIndex(upperBound(notes, beat + BEAT_EPS), beat);\n if (index < 0) return undefined;\n const onset = notes[index]?.note.startBeat;\n let best: IndexedNoteEvent | undefined;\n for (let candidateIndex = index; candidateIndex >= 0; candidateIndex -= 1) {\n const indexed = notes[candidateIndex];\n if (indexed !== undefined && indexed.note.startBeat !== onset) break;\n if (\n indexed === undefined ||\n indexed.note.durationBeat <= 0 ||\n indexed.note.startBeat - BEAT_EPS > beat ||\n beat >= indexed.endBeat - BEAT_EPS\n ) {\n continue;\n }\n if (best === undefined) {\n best = indexed;\n continue;\n }\n if (tieBreak === 'last') continue;\n const difference = indexed.note.pitch - best.note.pitch;\n // Equal pitches name no voice apart, so they are settled on the lower\n // original index rather than on whichever end of the array the scan\n // happens to reach last — otherwise `'highest'` and `'lowest'` would\n // answer a unison differently.\n const wins =\n difference === 0\n ? indexed.originalIndex < best.originalIndex\n : difference > 0 === (tieBreak === 'highest');\n if (wins) {\n best = indexed;\n }\n }\n return best;\n },\n attacksAt(beat) {\n assertFiniteNumber(beat, 'beat');\n // Only a sounding note attacks: a zero-length artefact must not make\n // `attacksAt(b)` true on a beat where `at(b)` finds nothing.\n let index = upperBound(notes, beat + BEAT_EPS) - 1;\n while (index >= 0) {\n const candidate = notes[index];\n if (candidate === undefined || Math.abs(candidate.note.startBeat - beat) >= BEAT_EPS) {\n return false;\n }\n if (candidate.note.durationBeat > 0) {\n return true;\n }\n index -= 1;\n }\n return false;\n },\n onsetsBetween(startBeat, endBeat) {\n assertFiniteNumber(startBeat, 'startBeat');\n assertFiniteNumber(endBeat, 'endBeat');\n const result: number[] = [];\n let index = upperBound(notes, startBeat + BEAT_EPS);\n while (index < notes.length) {\n const indexed = notes[index];\n const onset = indexed?.note.startBeat;\n if (indexed === undefined || onset === undefined || onset >= endBeat - BEAT_EPS) {\n break;\n }\n index += 1;\n if (indexed.note.durationBeat <= 0) {\n continue;\n }\n const previous = result[result.length - 1];\n if (previous === undefined || Math.abs(previous - onset) >= BEAT_EPS) {\n result.push(onset);\n }\n }\n return result;\n },\n };\n}\n"]}