@libraz/libcantus 1.3.0 → 1.4.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 (130) hide show
  1. package/NOTICE +63 -0
  2. package/README.md +3 -1
  3. package/README_ja.md +13 -11
  4. package/dist/analyze/index.cjs +7 -7
  5. package/dist/analyze/index.cjs.map +1 -1
  6. package/dist/analyze/index.d.cts +14 -12
  7. package/dist/analyze/index.d.ts +14 -12
  8. package/dist/analyze/index.js +6 -6
  9. package/dist/{chunk-NB25P3EH.js → chunk-2G5MCGYU.js} +33 -11
  10. package/dist/chunk-2G5MCGYU.js.map +1 -0
  11. package/dist/{chunk-NM7UAMC3.cjs → chunk-3FDBMIPR.cjs} +64 -64
  12. package/dist/chunk-3FDBMIPR.cjs.map +1 -0
  13. package/dist/{chunk-YFA7X2F6.cjs → chunk-3MDOEFIQ.cjs} +411 -327
  14. package/dist/chunk-3MDOEFIQ.cjs.map +1 -0
  15. package/dist/{chunk-IPEF6NWB.js → chunk-5MWRBYAT.js} +278 -154
  16. package/dist/chunk-5MWRBYAT.js.map +1 -0
  17. package/dist/{chunk-B2PW6EHN.cjs → chunk-6TTBEON3.cjs} +1253 -889
  18. package/dist/chunk-6TTBEON3.cjs.map +1 -0
  19. package/dist/{chunk-UMWZ4ZET.js → chunk-7NQ6AL3H.js} +4 -4
  20. package/dist/chunk-7NQ6AL3H.js.map +1 -0
  21. package/dist/{chunk-PWC4CRWT.cjs → chunk-E3QO3UET.cjs} +31 -23
  22. package/dist/chunk-E3QO3UET.cjs.map +1 -0
  23. package/dist/{chunk-GNVN4PPJ.js → chunk-FJFULJZE.js} +531 -213
  24. package/dist/chunk-FJFULJZE.js.map +1 -0
  25. package/dist/{chunk-CPMB7P75.js → chunk-FK3DGAIZ.js} +608 -244
  26. package/dist/chunk-FK3DGAIZ.js.map +1 -0
  27. package/dist/chunk-GEDTJHAC.cjs.map +1 -1
  28. package/dist/{chunk-RFIBF2RR.js → chunk-H7FVMBUC.js} +138 -65
  29. package/dist/chunk-H7FVMBUC.js.map +1 -0
  30. package/dist/{chunk-4R2LWV7E.cjs → chunk-HYSVSFNI.cjs} +664 -540
  31. package/dist/chunk-HYSVSFNI.cjs.map +1 -0
  32. package/dist/{chunk-35NBV2FT.cjs → chunk-KMYGRFZ6.cjs} +47 -24
  33. package/dist/chunk-KMYGRFZ6.cjs.map +1 -0
  34. package/dist/{chunk-IFZDWIKM.cjs → chunk-LQZQDBCD.cjs} +1048 -730
  35. package/dist/chunk-LQZQDBCD.cjs.map +1 -0
  36. package/dist/{chunk-PM4XJE2Y.cjs → chunk-P3CPXAJ5.cjs} +76 -54
  37. package/dist/chunk-P3CPXAJ5.cjs.map +1 -0
  38. package/dist/{chunk-S5BKZZGM.cjs → chunk-S3QELJUE.cjs} +72 -56
  39. package/dist/chunk-S3QELJUE.cjs.map +1 -0
  40. package/dist/{chunk-PUKJP2Z5.js → chunk-TXFWSU5D.js} +31 -8
  41. package/dist/chunk-TXFWSU5D.js.map +1 -0
  42. package/dist/{chunk-76ZBTRG2.cjs → chunk-U4EQTDGN.cjs} +258 -218
  43. package/dist/chunk-U4EQTDGN.cjs.map +1 -0
  44. package/dist/chunk-UGLFDG7L.cjs.map +1 -1
  45. package/dist/{chunk-MLBPXA4Z.js → chunk-UTE5A36R.js} +198 -114
  46. package/dist/chunk-UTE5A36R.js.map +1 -0
  47. package/dist/{chunk-ZBUROTJQ.js → chunk-UZKQISRF.js} +17 -9
  48. package/dist/chunk-UZKQISRF.js.map +1 -0
  49. package/dist/{chunk-7KG3RTYS.js → chunk-VL5NXVCN.js} +281 -155
  50. package/dist/chunk-VL5NXVCN.js.map +1 -0
  51. package/dist/{chunk-CGBADIO5.cjs → chunk-WOMCXN2W.cjs} +328 -202
  52. package/dist/chunk-WOMCXN2W.cjs.map +1 -0
  53. package/dist/{chunk-3R3QVKY5.cjs → chunk-YDGMWH43.cjs} +397 -324
  54. package/dist/chunk-YDGMWH43.cjs.map +1 -0
  55. package/dist/{chunk-AGOVW7U6.js → chunk-YYVXRV3V.js} +72 -56
  56. package/dist/chunk-YYVXRV3V.js.map +1 -0
  57. package/dist/{chunk-M5RYMAFI.js → chunk-ZLNSH4UL.js} +71 -31
  58. package/dist/chunk-ZLNSH4UL.js.map +1 -0
  59. package/dist/{coerce-DRERzdve.d.cts → coerce-CUhHc3Tu.d.ts} +16 -8
  60. package/dist/{coerce-DP0vYu-x.d.ts → coerce-Ytt2Eiq9.d.cts} +16 -8
  61. package/dist/core/index.cjs +5 -5
  62. package/dist/core/index.cjs.map +1 -1
  63. package/dist/core/index.d.cts +9 -8
  64. package/dist/core/index.d.ts +9 -8
  65. package/dist/core/index.js +4 -4
  66. package/dist/generate/index.cjs +8 -8
  67. package/dist/generate/index.cjs.map +1 -1
  68. package/dist/generate/index.d.cts +28 -18
  69. package/dist/generate/index.d.ts +28 -18
  70. package/dist/generate/index.js +7 -7
  71. package/dist/{index-BMweUlm5.d.cts → index-5VJgKz_Q.d.cts} +6 -2
  72. package/dist/{index-BHIUyjhZ.d.ts → index-BLuDesVG.d.ts} +20 -13
  73. package/dist/{index-B9QLziDe.d.cts → index-BV6APVa6.d.cts} +20 -13
  74. package/dist/{index-CL4b4gUQ.d.ts → index-Bf0Sv8a9.d.ts} +6 -2
  75. package/dist/{index-Dw2DP7vm.d.ts → index-BkT21EMv.d.cts} +3 -2
  76. package/dist/{index-BL5N2Oyz.d.cts → index-C7KCaTRz.d.ts} +3 -2
  77. package/dist/{index-Cy15oTvh.d.ts → index-C9MYbuKS.d.cts} +7 -3
  78. package/dist/{index-Cy15oTvh.d.cts → index-C9MYbuKS.d.ts} +7 -3
  79. package/dist/{index-CY-3NVfy.d.cts → index-DVInzCDS.d.cts} +53 -29
  80. package/dist/{index-9eoQs2C1.d.ts → index-Dg-SAfhz.d.ts} +6 -4
  81. package/dist/{index-zaZdmnU4.d.ts → index-jgeug9EA.d.ts} +53 -29
  82. package/dist/{index-cHvbYgJI.d.cts → index-pQihL4z7.d.cts} +6 -4
  83. package/dist/index.cjs +13 -13
  84. package/dist/index.cjs.map +1 -1
  85. package/dist/index.d.cts +11 -11
  86. package/dist/index.d.ts +11 -11
  87. package/dist/index.js +12 -12
  88. package/dist/{masks-Cc7-MzzY.d.ts → masks-BnzXnBj0.d.cts} +3 -2
  89. package/dist/{masks-pADfTOiD.d.cts → masks-tycmkrgs.d.ts} +3 -2
  90. package/dist/model/index.cjs +12 -12
  91. package/dist/model/index.cjs.map +1 -1
  92. package/dist/model/index.d.cts +63 -48
  93. package/dist/model/index.d.ts +63 -48
  94. package/dist/model/index.js +11 -11
  95. package/dist/{positional-DJgs6ltD.d.ts → positional-CscpxtlE.d.cts} +3 -2
  96. package/dist/{positional-DMZer-FM.d.cts → positional-DLnfUgqG.d.ts} +3 -2
  97. package/dist/{profile-DLdlIpS9.d.ts → profile-B4WqcM4H.d.ts} +25 -17
  98. package/dist/{profile-CQMEqdH9.d.cts → profile-BJd1XkPB.d.cts} +25 -17
  99. package/dist/theory/index.cjs +5 -5
  100. package/dist/theory/index.cjs.map +1 -1
  101. package/dist/theory/index.d.cts +18 -14
  102. package/dist/theory/index.d.ts +18 -14
  103. package/dist/theory/index.js +4 -4
  104. package/dist/{types-D-XgxGvp.d.cts → types-7mGFwvMm.d.ts} +45 -22
  105. package/dist/{types-Dh7C064C.d.ts → types-_ofpb2be.d.cts} +45 -22
  106. package/package.json +13 -13
  107. package/dist/chunk-35NBV2FT.cjs.map +0 -1
  108. package/dist/chunk-3R3QVKY5.cjs.map +0 -1
  109. package/dist/chunk-4R2LWV7E.cjs.map +0 -1
  110. package/dist/chunk-76ZBTRG2.cjs.map +0 -1
  111. package/dist/chunk-7KG3RTYS.js.map +0 -1
  112. package/dist/chunk-AGOVW7U6.js.map +0 -1
  113. package/dist/chunk-B2PW6EHN.cjs.map +0 -1
  114. package/dist/chunk-CGBADIO5.cjs.map +0 -1
  115. package/dist/chunk-CPMB7P75.js.map +0 -1
  116. package/dist/chunk-GNVN4PPJ.js.map +0 -1
  117. package/dist/chunk-IFZDWIKM.cjs.map +0 -1
  118. package/dist/chunk-IPEF6NWB.js.map +0 -1
  119. package/dist/chunk-M5RYMAFI.js.map +0 -1
  120. package/dist/chunk-MLBPXA4Z.js.map +0 -1
  121. package/dist/chunk-NB25P3EH.js.map +0 -1
  122. package/dist/chunk-NM7UAMC3.cjs.map +0 -1
  123. package/dist/chunk-PM4XJE2Y.cjs.map +0 -1
  124. package/dist/chunk-PUKJP2Z5.js.map +0 -1
  125. package/dist/chunk-PWC4CRWT.cjs.map +0 -1
  126. package/dist/chunk-RFIBF2RR.js.map +0 -1
  127. package/dist/chunk-S5BKZZGM.cjs.map +0 -1
  128. package/dist/chunk-UMWZ4ZET.js.map +0 -1
  129. package/dist/chunk-YFA7X2F6.cjs.map +0 -1
  130. package/dist/chunk-ZBUROTJQ.js.map +0 -1
@@ -3,7 +3,9 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkS5BKZZGMcjs = require('./chunk-S5BKZZGM.cjs');
6
+
7
+
8
+ var _chunkS3QELJUEcjs = require('./chunk-S3QELJUE.cjs');
7
9
 
8
10
  // src/core/event-index/index.ts
9
11
  function upperBound(values, beat) {
@@ -11,7 +13,7 @@ function upperBound(values, beat) {
11
13
  let high = values.length;
12
14
  while (low < high) {
13
15
  const middle = Math.floor((low + high) / 2);
14
- if ((_nullishCoalesce(_optionalChain([values, 'access', _ => _[middle], 'optionalAccess', _2 => _2.note, 'access', _3 => _3.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat) {
16
+ if ((_nullishCoalesce(_optionalChain([values, 'access', _2 => _2[middle], 'optionalAccess', _3 => _3.note, 'access', _4 => _4.startBeat]), () => ( Number.POSITIVE_INFINITY))) <= beat) {
15
17
  low = middle + 1;
16
18
  } else {
17
19
  high = middle;
@@ -25,9 +27,9 @@ function sortedNoteEvents(events) {
25
27
  return sorted;
26
28
  }
27
29
  function createNoteEventIndex(events, options = {}) {
28
- const asked = _chunkS5BKZZGMcjs.assertOptions.call(void 0, options, "options");
29
- _chunkS5BKZZGMcjs.assertNoteEvents.call(void 0, events, _nullishCoalesce(asked.name, () => ( "note events")), asked);
30
- const tieBreak = _nullishCoalesce(asked.tieBreak, () => ( "highest"));
30
+ const asked = _chunkS3QELJUEcjs.assertOptions.call(void 0, options, "options");
31
+ _chunkS3QELJUEcjs.assertNoteEvents.call(void 0, events, _nullishCoalesce(asked.name, () => ( "note events")), asked);
32
+ const tieBreak = asked.tieBreak === void 0 || asked.tieBreak === null ? "highest" : _chunkS3QELJUEcjs.assertOneOf.call(void 0, asked.tieBreak, ["highest", "lowest", "last"], "options.tieBreak");
31
33
  const notes = Object.freeze(
32
34
  events.map(
33
35
  (note, originalIndex) => Object.freeze({
@@ -47,7 +49,7 @@ function createNoteEventIndex(events, options = {}) {
47
49
  while (treeSize < notes.length) treeSize *= 2;
48
50
  const tree = new Array(treeSize * 2).fill(Number.NEGATIVE_INFINITY);
49
51
  for (let index = 0; index < notes.length; index += 1) {
50
- tree[treeSize + index] = _nullishCoalesce(_optionalChain([notes, 'access', _4 => _4[index], 'optionalAccess', _5 => _5.endBeat]), () => ( Number.NEGATIVE_INFINITY));
52
+ tree[treeSize + index] = _nullishCoalesce(_optionalChain([notes, 'access', _5 => _5[index], 'optionalAccess', _6 => _6.endBeat]), () => ( Number.NEGATIVE_INFINITY));
51
53
  }
52
54
  for (let index = treeSize - 1; index > 0; index -= 1) {
53
55
  tree[index] = Math.max(
@@ -61,7 +63,7 @@ function createNoteEventIndex(events, options = {}) {
61
63
  const latestActiveIndex = (exclusive, beat) => {
62
64
  const tree = endTree();
63
65
  const find = (node, start, end) => {
64
- if (start >= exclusive || (_nullishCoalesce(tree[node], () => ( Number.NEGATIVE_INFINITY))) <= beat + _chunkS5BKZZGMcjs.BEAT_EPS) {
66
+ if (start >= exclusive || (_nullishCoalesce(tree[node], () => ( Number.NEGATIVE_INFINITY))) <= beat + _chunkS3QELJUEcjs.BEAT_EPS) {
65
67
  return -1;
66
68
  }
67
69
  if (end - start === 1) return start < notes.length ? start : -1;
@@ -74,22 +76,25 @@ function createNoteEventIndex(events, options = {}) {
74
76
  return {
75
77
  notes,
76
78
  at(beat) {
77
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, beat, "beat");
78
- const index = latestActiveIndex(upperBound(notes, beat + _chunkS5BKZZGMcjs.BEAT_EPS), beat);
79
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, beat, "beat");
80
+ const index = latestActiveIndex(upperBound(notes, beat + _chunkS3QELJUEcjs.BEAT_EPS), beat);
79
81
  if (index < 0) return void 0;
80
- const onset = _optionalChain([notes, 'access', _6 => _6[index], 'optionalAccess', _7 => _7.note, 'access', _8 => _8.startBeat]);
82
+ const onset = _nullishCoalesce(_optionalChain([notes, 'access', _7 => _7[index], 'optionalAccess', _8 => _8.note, 'access', _9 => _9.startBeat]), () => ( 0));
81
83
  let best;
82
84
  for (let candidateIndex = index; candidateIndex >= 0; candidateIndex -= 1) {
83
85
  const indexed = notes[candidateIndex];
84
- if (indexed !== void 0 && indexed.note.startBeat !== onset) break;
85
- if (indexed === void 0 || indexed.note.durationBeat <= 0 || indexed.note.startBeat - _chunkS5BKZZGMcjs.BEAT_EPS > beat || beat >= indexed.endBeat - _chunkS5BKZZGMcjs.BEAT_EPS) {
86
+ if (indexed !== void 0 && Math.abs(indexed.note.startBeat - onset) >= _chunkS3QELJUEcjs.BEAT_EPS) break;
87
+ if (indexed === void 0 || indexed.note.durationBeat <= 0 || indexed.note.startBeat - _chunkS3QELJUEcjs.BEAT_EPS > beat || beat >= indexed.endBeat - _chunkS3QELJUEcjs.BEAT_EPS) {
86
88
  continue;
87
89
  }
88
90
  if (best === void 0) {
89
91
  best = indexed;
90
92
  continue;
91
93
  }
92
- if (tieBreak === "last") continue;
94
+ if (tieBreak === "last") {
95
+ if (indexed.originalIndex > best.originalIndex) best = indexed;
96
+ continue;
97
+ }
93
98
  const difference = indexed.note.pitch - best.note.pitch;
94
99
  const wins = difference === 0 ? indexed.originalIndex < best.originalIndex : difference > 0 === (tieBreak === "highest");
95
100
  if (wins) {
@@ -99,11 +104,11 @@ function createNoteEventIndex(events, options = {}) {
99
104
  return best;
100
105
  },
101
106
  attacksAt(beat) {
102
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, beat, "beat");
103
- let index = upperBound(notes, beat + _chunkS5BKZZGMcjs.BEAT_EPS) - 1;
107
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, beat, "beat");
108
+ let index = upperBound(notes, beat + _chunkS3QELJUEcjs.BEAT_EPS) - 1;
104
109
  while (index >= 0) {
105
110
  const candidate = notes[index];
106
- if (candidate === void 0 || Math.abs(candidate.note.startBeat - beat) >= _chunkS5BKZZGMcjs.BEAT_EPS) {
111
+ if (candidate === void 0 || Math.abs(candidate.note.startBeat - beat) >= _chunkS3QELJUEcjs.BEAT_EPS) {
107
112
  return false;
108
113
  }
109
114
  if (candidate.note.durationBeat > 0) {
@@ -114,14 +119,14 @@ function createNoteEventIndex(events, options = {}) {
114
119
  return false;
115
120
  },
116
121
  onsetsBetween(startBeat, endBeat) {
117
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, startBeat, "startBeat");
118
- _chunkS5BKZZGMcjs.assertFiniteNumber.call(void 0, endBeat, "endBeat");
122
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, startBeat, "startBeat");
123
+ _chunkS3QELJUEcjs.assertFiniteNumber.call(void 0, endBeat, "endBeat");
119
124
  const result = [];
120
- let index = upperBound(notes, startBeat + _chunkS5BKZZGMcjs.BEAT_EPS);
125
+ let index = upperBound(notes, startBeat + _chunkS3QELJUEcjs.BEAT_EPS);
121
126
  while (index < notes.length) {
122
127
  const indexed = notes[index];
123
- const onset = _optionalChain([indexed, 'optionalAccess', _9 => _9.note, 'access', _10 => _10.startBeat]);
124
- if (indexed === void 0 || onset === void 0 || onset >= endBeat - _chunkS5BKZZGMcjs.BEAT_EPS) {
128
+ const onset = _optionalChain([indexed, 'optionalAccess', _10 => _10.note, 'access', _11 => _11.startBeat]);
129
+ if (indexed === void 0 || onset === void 0 || onset >= endBeat - _chunkS3QELJUEcjs.BEAT_EPS) {
125
130
  break;
126
131
  }
127
132
  index += 1;
@@ -129,7 +134,7 @@ function createNoteEventIndex(events, options = {}) {
129
134
  continue;
130
135
  }
131
136
  const previous = result[result.length - 1];
132
- if (previous === void 0 || Math.abs(previous - onset) >= _chunkS5BKZZGMcjs.BEAT_EPS) {
137
+ if (previous === void 0 || Math.abs(previous - onset) >= _chunkS3QELJUEcjs.BEAT_EPS) {
133
138
  result.push(onset);
134
139
  }
135
140
  }
@@ -138,8 +143,26 @@ function createNoteEventIndex(events, options = {}) {
138
143
  };
139
144
  }
140
145
 
146
+ // src/core/event-index/order.ts
147
+ function canonicalNoteOrder(notes) {
148
+ return notes.map((_, index) => index).sort((a, b) => {
149
+ const left = notes[a];
150
+ const right = notes[b];
151
+ if (!left || !right) {
152
+ return a - b;
153
+ }
154
+ return left.startBeat - right.startBeat || left.pitch - right.pitch || a - b;
155
+ });
156
+ }
157
+ function soundingIndices(notes) {
158
+ const sounding = new Set(_chunkS3QELJUEcjs.dropSilentNotes.call(void 0, notes));
159
+ return notes.flatMap((note, index) => sounding.has(note) ? [index] : []);
160
+ }
161
+
162
+
163
+
141
164
 
142
165
 
143
166
 
144
- exports.sortedNoteEvents = sortedNoteEvents; exports.createNoteEventIndex = createNoteEventIndex;
145
- //# sourceMappingURL=chunk-35NBV2FT.cjs.map
167
+ exports.sortedNoteEvents = sortedNoteEvents; exports.createNoteEventIndex = createNoteEventIndex; exports.canonicalNoteOrder = canonicalNoteOrder; exports.soundingIndices = soundingIndices;
168
+ //# sourceMappingURL=chunk-KMYGRFZ6.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/libcantus/libcantus/ts/dist/chunk-KMYGRFZ6.cjs","../src/core/event-index/index.ts","../src/core/event-index/order.ts"],"names":[],"mappings":"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","file":"/home/runner/work/libcantus/libcantus/ts/dist/chunk-KMYGRFZ6.cjs","sourcesContent":[null,"import { BEAT_EPS } from '../meter/internal.js';\nimport type { NoteEvent } from '../types.js';\nimport {\n assertFiniteNumber,\n assertNoteEvents,\n assertOneOf,\n assertOptions,\n} 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: OnsetTieBreak =\n asked.tieBreak === undefined || asked.tieBreak === null\n ? 'highest'\n : assertOneOf(asked.tieBreak, ['highest', 'lowest', 'last'], 'options.tieBreak');\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 ?? 0;\n let best: IndexedNoteEvent | undefined;\n for (let candidateIndex = index; candidateIndex >= 0; candidateIndex -= 1) {\n const indexed = notes[candidateIndex];\n if (indexed !== undefined && Math.abs(indexed.note.startBeat - onset) >= BEAT_EPS) 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 // Onsets inside one BEAT_EPS group are not in input order, so 'last' compares indices.\n if (tieBreak === 'last') {\n if (indexed.originalIndex > best.originalIndex) best = indexed;\n continue;\n }\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","import type { NoteEvent } from '../types.js';\nimport { dropSilentNotes } from '../validation/index.js';\n\n/**\n * Indices of the notes in canonical order: by onset, then by pitch, then by\n * position in the array.\n *\n * Every pass that reads a note against the one before or after it walks the\n * notes in this order, so two listings of the same notes give the same answer.\n *\n * @param notes Validated note events.\n * @returns Every index of `notes`, once.\n */\nexport function canonicalNoteOrder(notes: readonly NoteEvent[]): number[] {\n return notes\n .map((_, index) => index)\n .sort((a, b) => {\n const left = notes[a];\n const right = notes[b];\n if (!left || !right) {\n return a - b;\n }\n return left.startBeat - right.startBeat || left.pitch - right.pitch || a - b;\n });\n}\n\n/**\n * Indices of the notes {@link dropSilentNotes} keeps, in input order.\n *\n * For a pass that reports one entry per input note and so cannot drop the\n * silent ones from the array itself.\n *\n * @param notes Validated note events.\n * @returns The indices of the notes with a positive duration, ascending.\n */\nexport function soundingIndices(notes: readonly NoteEvent[]): number[] {\n const sounding = new Set(dropSilentNotes(notes));\n return notes.flatMap((note, index) => (sounding.has(note) ? [index] : []));\n}\n"]}