@sudobility/music_types 0.11.30 → 0.11.32

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 (251) hide show
  1. package/dist/domain/commands/edit-commands.d.ts +3 -3
  2. package/dist/domain/commands/edit-commands.d.ts.map +1 -1
  3. package/dist/domain/commands/edit-commands.js +25 -25
  4. package/dist/domain/commands/edit-commands.js.map +1 -1
  5. package/dist/domain/commands/history.d.ts +32 -0
  6. package/dist/domain/commands/history.d.ts.map +1 -0
  7. package/dist/domain/commands/history.js +63 -0
  8. package/dist/domain/commands/history.js.map +1 -0
  9. package/dist/domain/commands/note-commands.d.ts +9 -12
  10. package/dist/domain/commands/note-commands.d.ts.map +1 -1
  11. package/dist/domain/commands/note-commands.js +42 -72
  12. package/dist/domain/commands/note-commands.js.map +1 -1
  13. package/dist/domain/commands/note-marks.d.ts +171 -0
  14. package/dist/domain/commands/note-marks.d.ts.map +1 -0
  15. package/dist/domain/commands/note-marks.js +427 -0
  16. package/dist/domain/commands/note-marks.js.map +1 -0
  17. package/dist/domain/commands/reflow.d.ts +1 -1
  18. package/dist/domain/commands/reflow.js +17 -17
  19. package/dist/domain/commands/reflow.js.map +1 -1
  20. package/dist/domain/commands/region-commands.d.ts +4 -4
  21. package/dist/domain/commands/region-commands.d.ts.map +1 -1
  22. package/dist/domain/commands/region-commands.js +7 -7
  23. package/dist/domain/commands/region-commands.js.map +1 -1
  24. package/dist/domain/commands/relocate-commands.d.ts +3 -3
  25. package/dist/domain/commands/relocate-commands.js +15 -15
  26. package/dist/domain/commands/relocate-commands.js.map +1 -1
  27. package/dist/domain/commands/ripple-commands.d.ts +2 -2
  28. package/dist/domain/commands/ripple-commands.js +13 -13
  29. package/dist/domain/commands/ripple-commands.js.map +1 -1
  30. package/dist/domain/commands/snapshot.d.ts +3 -3
  31. package/dist/domain/commands/snapshot.js +5 -5
  32. package/dist/domain/commands/snapshot.js.map +1 -1
  33. package/dist/domain/commands/structure-commands.d.ts +86 -5
  34. package/dist/domain/commands/structure-commands.d.ts.map +1 -1
  35. package/dist/domain/commands/structure-commands.js +272 -36
  36. package/dist/domain/commands/structure-commands.js.map +1 -1
  37. package/dist/domain/commands/track-commands.d.ts +2 -2
  38. package/dist/domain/commands/track-commands.js +7 -7
  39. package/dist/domain/commands/track-commands.js.map +1 -1
  40. package/dist/domain/commands/types.d.ts +2 -2
  41. package/dist/domain/generation/replacement-region.d.ts +32 -0
  42. package/dist/domain/generation/replacement-region.d.ts.map +1 -0
  43. package/dist/domain/generation/replacement-region.js +79 -0
  44. package/dist/domain/generation/replacement-region.js.map +1 -0
  45. package/dist/domain/instruments/gm-icon.d.ts +12 -0
  46. package/dist/domain/instruments/gm-icon.d.ts.map +1 -0
  47. package/dist/domain/instruments/gm-icon.js +327 -0
  48. package/dist/domain/instruments/gm-icon.js.map +1 -0
  49. package/dist/domain/instruments/gm-kit.js +9 -9
  50. package/dist/domain/instruments/gm-kit.js.map +1 -1
  51. package/dist/domain/instruments/gm-percussion.js +48 -48
  52. package/dist/domain/instruments/gm-percussion.js.map +1 -1
  53. package/dist/domain/instruments/gm-polyphony.js +6 -6
  54. package/dist/domain/instruments/gm-range.js +6 -6
  55. package/dist/domain/instruments/gm-transposition.js +1 -1
  56. package/dist/domain/instruments/gm.d.ts +1 -1
  57. package/dist/domain/instruments/gm.js +161 -161
  58. package/dist/domain/instruments/gm.js.map +1 -1
  59. package/dist/domain/instruments/icon-art.d.ts +75 -0
  60. package/dist/domain/instruments/icon-art.d.ts.map +1 -0
  61. package/dist/domain/instruments/icon-art.js +93 -0
  62. package/dist/domain/instruments/icon-art.js.map +1 -0
  63. package/dist/domain/instruments/instrument-fit.d.ts +31 -0
  64. package/dist/domain/instruments/instrument-fit.d.ts.map +1 -0
  65. package/dist/domain/instruments/instrument-fit.js +117 -0
  66. package/dist/domain/instruments/instrument-fit.js.map +1 -0
  67. package/dist/domain/instruments/instrument-options.d.ts +62 -0
  68. package/dist/domain/instruments/instrument-options.d.ts.map +1 -0
  69. package/dist/domain/instruments/instrument-options.js +69 -0
  70. package/dist/domain/instruments/instrument-options.js.map +1 -0
  71. package/dist/domain/instruments/track-instrument.d.ts +80 -0
  72. package/dist/domain/instruments/track-instrument.d.ts.map +1 -0
  73. package/dist/domain/instruments/track-instrument.js +102 -0
  74. package/dist/domain/instruments/track-instrument.js.map +1 -0
  75. package/dist/domain/notation/chord-symbol.d.ts +26 -0
  76. package/dist/domain/notation/chord-symbol.d.ts.map +1 -0
  77. package/dist/domain/notation/chord-symbol.js +32 -0
  78. package/dist/domain/notation/chord-symbol.js.map +1 -0
  79. package/dist/domain/notation/lyric-syllables.d.ts +15 -0
  80. package/dist/domain/notation/lyric-syllables.d.ts.map +1 -0
  81. package/dist/domain/notation/lyric-syllables.js +15 -0
  82. package/dist/domain/notation/lyric-syllables.js.map +1 -0
  83. package/dist/domain/notation/music-vocabulary.d.ts +59 -0
  84. package/dist/domain/notation/music-vocabulary.d.ts.map +1 -0
  85. package/dist/domain/notation/music-vocabulary.js +164 -0
  86. package/dist/domain/notation/music-vocabulary.js.map +1 -0
  87. package/dist/domain/pitch/pitch.d.ts +1 -1
  88. package/dist/domain/pitch/pitch.js +29 -29
  89. package/dist/domain/pitch/transpose.d.ts +1 -1
  90. package/dist/domain/pitch/transpose.js +2 -2
  91. package/dist/domain/quantization/quantize.d.ts +2 -2
  92. package/dist/domain/quantization/quantize.js +9 -9
  93. package/dist/domain/quantization/quantize.js.map +1 -1
  94. package/dist/domain/score/articulation.d.ts +56 -0
  95. package/dist/domain/score/articulation.d.ts.map +1 -0
  96. package/dist/domain/score/articulation.js +56 -0
  97. package/dist/domain/score/articulation.js.map +1 -0
  98. package/dist/domain/score/bar-numbers.d.ts +38 -0
  99. package/dist/domain/score/bar-numbers.d.ts.map +1 -0
  100. package/dist/domain/score/bar-numbers.js +49 -0
  101. package/dist/domain/score/bar-numbers.js.map +1 -0
  102. package/dist/domain/score/collapse-rests.d.ts +23 -0
  103. package/dist/domain/score/collapse-rests.d.ts.map +1 -0
  104. package/dist/domain/score/collapse-rests.js +78 -0
  105. package/dist/domain/score/collapse-rests.js.map +1 -0
  106. package/dist/domain/score/cue-notes.d.ts +24 -0
  107. package/dist/domain/score/cue-notes.d.ts.map +1 -0
  108. package/dist/domain/score/cue-notes.js +105 -0
  109. package/dist/domain/score/cue-notes.js.map +1 -0
  110. package/dist/domain/score/dynamics.d.ts +62 -0
  111. package/dist/domain/score/dynamics.d.ts.map +1 -0
  112. package/dist/domain/score/dynamics.js +142 -0
  113. package/dist/domain/score/dynamics.js.map +1 -0
  114. package/dist/domain/score/effective-clef.d.ts +42 -0
  115. package/dist/domain/score/effective-clef.d.ts.map +1 -0
  116. package/dist/domain/score/effective-clef.js +39 -0
  117. package/dist/domain/score/effective-clef.js.map +1 -0
  118. package/dist/domain/score/extract-part.d.ts +10 -0
  119. package/dist/domain/score/extract-part.d.ts.map +1 -0
  120. package/dist/domain/score/extract-part.js +44 -0
  121. package/dist/domain/score/extract-part.js.map +1 -0
  122. package/dist/domain/score/factory.d.ts +1 -1
  123. package/dist/domain/score/factory.js +12 -12
  124. package/dist/domain/score/factory.js.map +1 -1
  125. package/dist/domain/score/fermata-tempo.d.ts +40 -0
  126. package/dist/domain/score/fermata-tempo.d.ts.map +1 -0
  127. package/dist/domain/score/fermata-tempo.js +95 -0
  128. package/dist/domain/score/fermata-tempo.js.map +1 -0
  129. package/dist/domain/score/flatten.d.ts +32 -0
  130. package/dist/domain/score/flatten.d.ts.map +1 -0
  131. package/dist/domain/score/flatten.js +132 -0
  132. package/dist/domain/score/flatten.js.map +1 -0
  133. package/dist/domain/score/fragment.d.ts +4 -8
  134. package/dist/domain/score/fragment.d.ts.map +1 -1
  135. package/dist/domain/score/fragment.js +8 -8
  136. package/dist/domain/score/fragment.js.map +1 -1
  137. package/dist/domain/score/ids.d.ts +1 -1
  138. package/dist/domain/score/ids.js +6 -6
  139. package/dist/domain/score/ottava.d.ts +41 -0
  140. package/dist/domain/score/ottava.d.ts.map +1 -0
  141. package/dist/domain/score/ottava.js +98 -0
  142. package/dist/domain/score/ottava.js.map +1 -0
  143. package/dist/domain/score/performance-timeline.d.ts +42 -0
  144. package/dist/domain/score/performance-timeline.d.ts.map +1 -0
  145. package/dist/domain/score/performance-timeline.js +78 -0
  146. package/dist/domain/score/performance-timeline.js.map +1 -0
  147. package/dist/domain/score/queries.d.ts +2 -2
  148. package/dist/domain/score/queries.js +7 -7
  149. package/dist/domain/score/queries.js.map +1 -1
  150. package/dist/domain/score/rehearsal-marks.d.ts +30 -0
  151. package/dist/domain/score/rehearsal-marks.d.ts.map +1 -0
  152. package/dist/domain/score/rehearsal-marks.js +126 -0
  153. package/dist/domain/score/rehearsal-marks.js.map +1 -0
  154. package/dist/domain/score/repeat-order.d.ts +21 -0
  155. package/dist/domain/score/repeat-order.d.ts.map +1 -0
  156. package/dist/domain/score/repeat-order.js +123 -0
  157. package/dist/domain/score/repeat-order.js.map +1 -0
  158. package/dist/domain/score/snapshot-tree.d.ts +23 -0
  159. package/dist/domain/score/snapshot-tree.d.ts.map +1 -0
  160. package/dist/domain/score/snapshot-tree.js +45 -0
  161. package/dist/domain/score/snapshot-tree.js.map +1 -0
  162. package/dist/domain/score/ties.d.ts +1 -1
  163. package/dist/domain/score/ties.js +7 -7
  164. package/dist/domain/score/ties.js.map +1 -1
  165. package/dist/domain/score/written-pitch.d.ts +33 -0
  166. package/dist/domain/score/written-pitch.d.ts.map +1 -0
  167. package/dist/domain/score/written-pitch.js +110 -0
  168. package/dist/domain/score/written-pitch.js.map +1 -0
  169. package/dist/domain/selection/range-select.d.ts +13 -0
  170. package/dist/domain/selection/range-select.d.ts.map +1 -0
  171. package/dist/domain/selection/range-select.js +40 -0
  172. package/dist/domain/selection/range-select.js.map +1 -0
  173. package/dist/domain/selection/selection-editing.d.ts +30 -0
  174. package/dist/domain/selection/selection-editing.d.ts.map +1 -0
  175. package/dist/domain/selection/selection-editing.js +31 -0
  176. package/dist/domain/selection/selection-editing.js.map +1 -0
  177. package/dist/domain/selection/selection.d.ts +2 -2
  178. package/dist/domain/selection/selection.js +7 -7
  179. package/dist/domain/selection/selection.js.map +1 -1
  180. package/dist/domain/selection/types.d.ts +2 -2
  181. package/dist/domain/selection/types.d.ts.map +1 -1
  182. package/dist/domain/time/duration-modifiers.d.ts +40 -0
  183. package/dist/domain/time/duration-modifiers.d.ts.map +1 -0
  184. package/dist/domain/time/duration-modifiers.js +50 -0
  185. package/dist/domain/time/duration-modifiers.js.map +1 -0
  186. package/dist/domain/time/duration-selection.d.ts +23 -0
  187. package/dist/domain/time/duration-selection.d.ts.map +1 -0
  188. package/dist/domain/time/duration-selection.js +50 -0
  189. package/dist/domain/time/duration-selection.js.map +1 -0
  190. package/dist/domain/time/durations.js +6 -6
  191. package/dist/domain/time/durations.js.map +1 -1
  192. package/dist/domain/time/fraction.d.ts +1 -1
  193. package/dist/domain/time/fraction.js +1 -1
  194. package/dist/domain/time/tap-to-note.d.ts +21 -0
  195. package/dist/domain/time/tap-to-note.d.ts.map +1 -0
  196. package/dist/domain/time/tap-to-note.js +43 -0
  197. package/dist/domain/time/tap-to-note.js.map +1 -0
  198. package/dist/domain/time/tempo-map.d.ts +1 -1
  199. package/dist/domain/time/ticks.d.ts +1 -1
  200. package/dist/domain/time/ticks.d.ts.map +1 -1
  201. package/dist/domain/time/ticks.js +14 -14
  202. package/dist/domain/time/ticks.js.map +1 -1
  203. package/dist/domain/time/tuplets.d.ts +42 -0
  204. package/dist/domain/time/tuplets.d.ts.map +1 -0
  205. package/dist/domain/time/tuplets.js +53 -0
  206. package/dist/domain/time/tuplets.js.map +1 -0
  207. package/dist/domain/validation/issues.d.ts +1 -1
  208. package/dist/domain/validation/issues.js +20 -20
  209. package/dist/domain/validation/validator.d.ts +2 -2
  210. package/dist/domain/validation/validator.d.ts.map +1 -1
  211. package/dist/domain/validation/validator.js +31 -31
  212. package/dist/domain/validation/validator.js.map +1 -1
  213. package/dist/domain/voicing/allocate.d.ts +2 -2
  214. package/dist/domain/voicing/allocate.js +6 -6
  215. package/dist/domain/voicing/allocate.js.map +1 -1
  216. package/dist/index.d.ts +72 -5535
  217. package/dist/index.d.ts.map +1 -1
  218. package/dist/index.js +79 -462
  219. package/dist/index.js.map +1 -1
  220. package/dist/model/api.d.ts +1696 -0
  221. package/dist/model/api.d.ts.map +1 -0
  222. package/dist/model/api.js +125 -0
  223. package/dist/model/api.js.map +1 -0
  224. package/dist/model/generation.d.ts +2178 -0
  225. package/dist/model/generation.d.ts.map +1 -0
  226. package/dist/model/generation.js +142 -0
  227. package/dist/model/generation.js.map +1 -0
  228. package/dist/model/schemas.d.ts +1200 -0
  229. package/dist/model/schemas.d.ts.map +1 -0
  230. package/dist/model/schemas.js +200 -0
  231. package/dist/model/schemas.js.map +1 -0
  232. package/dist/model/score.d.ts +462 -0
  233. package/dist/model/score.d.ts.map +1 -0
  234. package/dist/model/score.js +34 -0
  235. package/dist/model/score.js.map +1 -0
  236. package/dist/platform/audio.d.ts.map +1 -1
  237. package/dist/platform/file.d.ts.map +1 -1
  238. package/dist/platform/index.d.ts +7 -7
  239. package/dist/platform/index.js +7 -7
  240. package/dist/platform/midi-input.d.ts +2 -2
  241. package/dist/platform/midi.d.ts.map +1 -1
  242. package/dist/platform/mod.d.ts +2 -2
  243. package/dist/platform/playback.d.ts +7 -7
  244. package/dist/platform/xml.js +1 -1
  245. package/dist/services/regeneration/controller.d.ts +6 -6
  246. package/dist/services/regeneration/controller.js +12 -12
  247. package/dist/services/regeneration/controller.js.map +1 -1
  248. package/dist/test/fixtures.d.ts +1 -1
  249. package/dist/test/fixtures.js +103 -103
  250. package/dist/test/fixtures.js.map +1 -1
  251. package/package.json +1 -1
@@ -0,0 +1,142 @@
1
+ import { isNoteEvent } from "../../index.js";
2
+ /**
3
+ * The velocity each marking sounds at, for a note carrying no deviation.
4
+ *
5
+ * The usual MIDI ladder, evenly spread so each step is audibly a step. `mf` is
6
+ * `DEFAULT_VELOCITY`, which is what makes an unmarked score unchanged.
7
+ */
8
+ const DYNAMIC_VELOCITY = {
9
+ ppp: 16,
10
+ pp: 32,
11
+ p: 48,
12
+ mp: 64,
13
+ mf: 80,
14
+ f: 96,
15
+ ff: 112,
16
+ fff: 127,
17
+ };
18
+ /** The velocity a note is written at when nobody has said otherwise. */
19
+ export const DEFAULT_VELOCITY = 80;
20
+ export function velocityForDynamic(dynamic) {
21
+ return DYNAMIC_VELOCITY[dynamic];
22
+ }
23
+ /**
24
+ * The velocity a note actually sounds at under `dynamic`.
25
+ *
26
+ * `null` means no dynamic is in force, and the written velocity stands.
27
+ */
28
+ export function effectiveVelocity(note, dynamic) {
29
+ if (!dynamic)
30
+ return note.velocity;
31
+ const deviation = note.velocity - DEFAULT_VELOCITY;
32
+ return Math.max(1, Math.min(127, velocityForDynamic(dynamic) + deviation));
33
+ }
34
+ /**
35
+ * Walks one voice's events in order, reporting the dynamic in force at each.
36
+ *
37
+ * Takes an already-ordered channel — the caller has sorted and joined ties —
38
+ * because "the next dynamic" only means anything in tick order.
39
+ */
40
+ export function dynamicsInForce(events) {
41
+ const inForce = new Map();
42
+ let current = null;
43
+ for (const event of events) {
44
+ if (!isNoteEvent(event))
45
+ continue;
46
+ if (event.dynamic)
47
+ current = event.dynamic;
48
+ if (current)
49
+ inForce.set(event.id, current);
50
+ }
51
+ return inForce;
52
+ }
53
+ /**
54
+ * The velocity each note in a hairpin ramps to.
55
+ *
56
+ * A dynamic sets a level; a hairpin is the *change* between two of them, so
57
+ * the only honest way to sound one is to ramp. The span runs from the note
58
+ * carrying `hairpinStart` to the one carrying `hairpinStop`, and velocity
59
+ * moves linearly from the level in force at the opening to the level in force
60
+ * just **after** the close — which is how a player reads it: the wedge takes
61
+ * you to the next marking.
62
+ *
63
+ * **Where there is no next marking**, the wedge still has to arrive somewhere.
64
+ * A crescendo with nothing after it goes one dynamic step up from where it
65
+ * started, and a diminuendo one step down. That is what a player does with an
66
+ * unresolved hairpin, and it beats the alternatives: leaving it flat makes the
67
+ * marking silent, and ramping to full or to nothing turns an unmarked detail
68
+ * into the loudest or quietest thing in the piece.
69
+ *
70
+ * Returns an empty map for a score with no hairpin, so an unmarked passage
71
+ * costs one allocation and no arithmetic.
72
+ */
73
+ export function hairpinVelocities(events, inForce) {
74
+ const ramped = new Map();
75
+ const notes = events.filter(isNoteEvent);
76
+ /** The velocity a note would sound at from its dynamic alone. */
77
+ const levelOf = (index) => {
78
+ const note = notes[index];
79
+ if (!note)
80
+ return DEFAULT_VELOCITY;
81
+ const dynamic = inForce.get(note.id);
82
+ return dynamic ? velocityForDynamic(dynamic) : DEFAULT_VELOCITY;
83
+ };
84
+ for (let i = 0; i < notes.length; i += 1) {
85
+ if (!notes[i].hairpinStart)
86
+ continue;
87
+ const kind = notes[i].hairpinStart;
88
+ let stop = -1;
89
+ for (let j = i; j < notes.length; j += 1) {
90
+ if (notes[j].hairpinStop) {
91
+ stop = j;
92
+ break;
93
+ }
94
+ }
95
+ // An unclosed opening ramps nowhere, matching what the renderer draws.
96
+ if (stop < 0)
97
+ break;
98
+ const from = levelOf(i);
99
+ const afterIndex = stop + 1;
100
+ const next = afterIndex < notes.length ? inForce.get(notes[afterIndex].id) : undefined;
101
+ const startDynamic = inForce.get(notes[i].id);
102
+ const to = next && next !== startDynamic
103
+ ? velocityForDynamic(next)
104
+ : stepFrom(from, kind === "crescendo" ? 1 : -1);
105
+ const span = stop - i;
106
+ for (let k = 0; k <= span; k += 1) {
107
+ const t = span === 0 ? 1 : k / span;
108
+ ramped.set(notes[i + k].id, Math.round(from + (to - from) * t));
109
+ }
110
+ i = stop;
111
+ }
112
+ return ramped;
113
+ }
114
+ /**
115
+ * One dynamic step from `velocity`, in `direction`.
116
+ *
117
+ * Used only where a hairpin has nothing to resolve to. Walks the ladder rather
118
+ * than adding a fixed number so the step is the same size a written marking
119
+ * would have been.
120
+ */
121
+ function stepFrom(velocity, direction) {
122
+ const ladder = DYNAMIC_LADDER.map(velocityForDynamic);
123
+ const nearest = ladder.reduce((best, v) => Math.abs(v - velocity) < Math.abs(best - velocity) ? v : best);
124
+ const index = ladder.indexOf(nearest);
125
+ const target = ladder[Math.max(0, Math.min(ladder.length - 1, index + direction))];
126
+ // At the top of the ladder a crescendo still has to go somewhere.
127
+ return target === nearest
128
+ ? Math.max(1, Math.min(127, velocity + direction * 16))
129
+ : target;
130
+ }
131
+ /** Softest to loudest, for stepping one marking at a time. */
132
+ const DYNAMIC_LADDER = [
133
+ "ppp",
134
+ "pp",
135
+ "p",
136
+ "mp",
137
+ "mf",
138
+ "f",
139
+ "ff",
140
+ "fff",
141
+ ];
142
+ //# sourceMappingURL=dynamics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamics.js","sourceRoot":"","sources":["../../../src/domain/score/dynamics.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,gBAAgB,GAA4B;IAChD,GAAG,EAAE,EAAE;IACP,EAAE,EAAE,EAAE;IACN,CAAC,EAAE,EAAE;IACL,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,EAAE;IACN,CAAC,EAAE,EAAE;IACL,EAAE,EAAE,GAAG;IACP,GAAG,EAAE,GAAG;CACT,CAAC;AAEF,wEAAwE;AACxE,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAEnC,MAAM,UAAU,kBAAkB,CAAC,OAAgB;IACjD,OAAO,gBAAgB,CAAC,OAAO,CAAC,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAe,EACf,OAAuB;IAEvB,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;IACnC,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,GAAG,gBAAgB,CAAC;IACnD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,kBAAkB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,MAA+B;IAE/B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC3C,IAAI,OAAO,GAAmB,IAAI,CAAC;IACnC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;YAAE,SAAS;QAClC,IAAI,KAAK,CAAC,OAAO;YAAE,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC3C,IAAI,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAA+B,EAC/B,OAA6B;IAE7B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAEzC,iEAAiE;IACjE,MAAM,OAAO,GAAG,CAAC,KAAa,EAAU,EAAE;QACxC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,CAAC,IAAI;YAAE,OAAO,gBAAgB,CAAC;QACnC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrC,OAAO,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC;IAClE,CAAC,CAAC;IAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY;YAAE,SAAS;QACrC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;QAEnC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC;QACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;gBACzB,IAAI,GAAG,CAAC,CAAC;gBACT,MAAM;YACR,CAAC;QACH,CAAC;QACD,uEAAuE;QACvE,IAAI,IAAI,GAAG,CAAC;YAAE,MAAM;QAEpB,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC;QAC5B,MAAM,IAAI,GACR,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5E,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC9C,MAAM,EAAE,GACN,IAAI,IAAI,IAAI,KAAK,YAAY;YAC3B,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC;YAC1B,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAEpD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC;QACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,MAAM,CAAC,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;YACpC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAClE,CAAC;QACD,CAAC,GAAG,IAAI,CAAC;IACX,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,QAAQ,CAAC,QAAgB,EAAE,SAAiB;IACnD,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CACxC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC9D,CAAC;IACF,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,MAAM,GACV,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IACtE,kEAAkE;IAClE,OAAO,MAAM,KAAK,OAAO;QACvB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,GAAG,SAAS,GAAG,EAAE,CAAC,CAAC;QACvD,CAAC,CAAC,MAAM,CAAC;AACb,CAAC;AAED,8DAA8D;AAC9D,MAAM,cAAc,GAAuB;IACzC,KAAK;IACL,IAAI;IACJ,GAAG;IACH,IAAI;IACJ,IAAI;IACJ,GAAG;IACH,IAAI;IACJ,KAAK;CACN,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Which clef is in force at a given bar.
3
+ *
4
+ * `Measure.clef` stores only the *changes*, so almost every bar has none and
5
+ * the answer has to be looked back for. That shape is deliberate — see the
6
+ * field's own doc — but it means no consumer may read `measure.clef` directly
7
+ * and expect the truth. They all come here instead: the renderer to draw the
8
+ * stave, the hit test to turn a y coordinate into a pitch, the exporter to
9
+ * decide when to emit a `<clef>`.
10
+ *
11
+ * Keeping it in one function is what stops the notation and the note-entry
12
+ * geometry from disagreeing, which would place a clicked note on the line
13
+ * above or below the one under the pointer — a bug that looks like a rounding
14
+ * error and is not.
15
+ */
16
+ import type { Clef, Measure, Track } from "../../index.js";
17
+ /**
18
+ * The clef `measures[index]` is read in, given the track's own clef as the
19
+ * starting point.
20
+ *
21
+ * Walks back to the most recent change rather than forward from the start, so
22
+ * the common case — a track that never changes clef — costs one array read and
23
+ * a miss.
24
+ *
25
+ * An index outside the array answers the track clef, which is what an empty
26
+ * score and a caret past the last barline both need.
27
+ */
28
+ export declare function clefAtMeasure(measures: readonly Measure[], index: number, trackClef: Clef): Clef;
29
+ /** The clef in force at `index` on `track`. */
30
+ export declare function effectiveClef(track: Track, index: number): Clef;
31
+ /**
32
+ * Whether `index` is where the clef visibly changes — i.e. the first bar that
33
+ * reads in a different clef from the one before it.
34
+ *
35
+ * Bar 0 is never a change: it is where the clef is first *established*, and
36
+ * the renderer draws that as part of the system header rather than as a
37
+ * change. A `Measure.clef` equal to what was already in force is likewise not
38
+ * a change, so a redundant marking (which an import can produce) prints
39
+ * nothing.
40
+ */
41
+ export declare function clefChangesAt(track: Track, index: number): boolean;
42
+ //# sourceMappingURL=effective-clef.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effective-clef.d.ts","sourceRoot":"","sources":["../../../src/domain/score/effective-clef.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE3D;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,IAAI,GACd,IAAI,CAMN;AAED,+CAA+C;AAC/C,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAE/D;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAGlE"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The clef `measures[index]` is read in, given the track's own clef as the
3
+ * starting point.
4
+ *
5
+ * Walks back to the most recent change rather than forward from the start, so
6
+ * the common case — a track that never changes clef — costs one array read and
7
+ * a miss.
8
+ *
9
+ * An index outside the array answers the track clef, which is what an empty
10
+ * score and a caret past the last barline both need.
11
+ */
12
+ export function clefAtMeasure(measures, index, trackClef) {
13
+ for (let i = Math.min(index, measures.length - 1); i >= 0; i -= 1) {
14
+ const clef = measures[i]?.clef;
15
+ if (clef)
16
+ return clef;
17
+ }
18
+ return trackClef;
19
+ }
20
+ /** The clef in force at `index` on `track`. */
21
+ export function effectiveClef(track, index) {
22
+ return clefAtMeasure(track.measures, index, track.clef);
23
+ }
24
+ /**
25
+ * Whether `index` is where the clef visibly changes — i.e. the first bar that
26
+ * reads in a different clef from the one before it.
27
+ *
28
+ * Bar 0 is never a change: it is where the clef is first *established*, and
29
+ * the renderer draws that as part of the system header rather than as a
30
+ * change. A `Measure.clef` equal to what was already in force is likewise not
31
+ * a change, so a redundant marking (which an import can produce) prints
32
+ * nothing.
33
+ */
34
+ export function clefChangesAt(track, index) {
35
+ if (index <= 0 || index >= track.measures.length)
36
+ return false;
37
+ return effectiveClef(track, index) !== effectiveClef(track, index - 1);
38
+ }
39
+ //# sourceMappingURL=effective-clef.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effective-clef.js","sourceRoot":"","sources":["../../../src/domain/score/effective-clef.ts"],"names":[],"mappings":"AAiBA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAC3B,QAA4B,EAC5B,KAAa,EACb,SAAe;IAEf,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;QAC/B,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IACxB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,aAAa,CAAC,KAAY,EAAE,KAAa;IACvD,OAAO,aAAa,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,KAAY,EAAE,KAAa;IACvD,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC/D,OAAO,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;AACzE,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { Score } from "../../index.js";
2
+ /**
3
+ * The part for `trackId`: that track alone, transposed for its instrument.
4
+ *
5
+ * Returns `null` when the track is not in the score, rather than an empty
6
+ * score — an empty part and a missing one are different problems, and only one
7
+ * of them is a bug.
8
+ */
9
+ export declare function extractPart(score: Score, trackId: string): Score | null;
10
+ //# sourceMappingURL=extract-part.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-part.d.ts","sourceRoot":"","sources":["../../../src/domain/score/extract-part.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAsBvE"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * A single track, written the way its player reads it.
3
+ *
4
+ * **Print-only.** The returned score is never saved, never played and never
5
+ * edited — it is a notation of the music, not the music. Playback uses
6
+ * sounding pitch, and routing this into it would make a trumpet sound a tone
7
+ * sharp.
8
+ *
9
+ * Features 3 to 5 extend this same function with multi-measure rests,
10
+ * rehearsal marks and cue notes, which is why part extraction lives in one
11
+ * place rather than four.
12
+ */
13
+ import { trackWrittenTransposition } from "../instruments/track-instrument.js";
14
+ import { collapseRests } from "./collapse-rests.js";
15
+ import { applyRehearsalMarks, rehearsalMarks } from "./rehearsal-marks.js";
16
+ import { applyCues, measureCues } from "./cue-notes.js";
17
+ import { transposeMeasure } from "./written-pitch.js";
18
+ /**
19
+ * The part for `trackId`: that track alone, transposed for its instrument.
20
+ *
21
+ * Returns `null` when the track is not in the score, rather than an empty
22
+ * score — an empty part and a missing one are different problems, and only one
23
+ * of them is a bug.
24
+ */
25
+ export function extractPart(score, trackId) {
26
+ const track = score.tracks.find((t) => t.id === trackId);
27
+ if (!track)
28
+ return null;
29
+ // Cues first, at concert pitch: chosen from the other tracks as they sound,
30
+ // then carried through the transposition below into the player's own key.
31
+ const cued = applyCues(track.measures, measureCues(score, trackId));
32
+ const semitones = trackWrittenTransposition(track);
33
+ const written = semitones === 0
34
+ ? cued
35
+ : cued.map((measure) => transposeMeasure(measure, semitones));
36
+ // Marks come from the WHOLE score, not this track: "from B" has to mean the
37
+ // same bar in every part, and a per-track derivation would give each player
38
+ // different letters.
39
+ const marked = applyRehearsalMarks(written, rehearsalMarks(score));
40
+ // Collapse last. Marks and cues must already be on: both end a rest run, and
41
+ // either one hidden inside a multi-measure rest would point at nothing.
42
+ return { ...score, tracks: [{ ...track, measures: collapseRests(marked) }] };
43
+ }
44
+ //# sourceMappingURL=extract-part.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-part.js","sourceRoot":"","sources":["../../../src/domain/score/extract-part.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,KAAY,EAAE,OAAe;IACvD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;IACzD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,4EAA4E;IAC5E,0EAA0E;IAC1E,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IAEpE,MAAM,SAAS,GAAG,yBAAyB,CAAC,KAAK,CAAC,CAAC;IACnD,MAAM,OAAO,GACX,SAAS,KAAK,CAAC;QACb,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;IAElE,4EAA4E;IAC5E,4EAA4E;IAC5E,qBAAqB;IACrB,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;IAEnE,6EAA6E;IAC7E,wEAAwE;IACxE,OAAO,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;AAC/E,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { KeySignature, Score, TimeSignature, Track } from '../../index.js';
1
+ import type { KeySignature, Score, TimeSignature, Track } from "../../index.js";
2
2
  export type CreateTrackOptions = Partial<Track> & {
3
3
  name: string;
4
4
  };
@@ -1,20 +1,20 @@
1
- import { createId } from './ids.js';
2
- import { measureDurationTicks } from '../time/ticks.js';
1
+ import { createId } from "./ids.js";
2
+ import { measureDurationTicks } from "../time/ticks.js";
3
3
  const DEFAULT_PPQ = 480;
4
4
  const DEFAULT_TEMPO_BPM = 120;
5
5
  const DEFAULT_TIME_SIGNATURE = { numerator: 4, denominator: 4 };
6
- const DEFAULT_KEY_SIGNATURE = { fifths: 0, mode: 'major' };
6
+ const DEFAULT_KEY_SIGNATURE = { fifths: 0, mode: "major" };
7
7
  const DEFAULT_MEASURE_COUNT = 1;
8
- const DEFAULT_VOICE_NAME = 'Voice 1';
8
+ const DEFAULT_VOICE_NAME = "Voice 1";
9
9
  /** Builds a `Track` with sensible defaults, overridden by any fields in `opts`. */
10
10
  export function createTrack(opts) {
11
11
  return {
12
12
  id: opts.id ?? createId(),
13
13
  name: opts.name,
14
- instrumentName: opts.instrumentName ?? 'Piano',
14
+ instrumentName: opts.instrumentName ?? "Piano",
15
15
  midiProgram: opts.midiProgram ?? 0,
16
16
  midiChannel: opts.midiChannel ?? 0,
17
- clef: opts.clef ?? 'treble',
17
+ clef: opts.clef ?? "treble",
18
18
  volume: opts.volume ?? 1,
19
19
  pan: opts.pan ?? 0,
20
20
  muted: opts.muted ?? false,
@@ -63,9 +63,9 @@ export function createEmptyScore(opts) {
63
63
  const keySignature = opts.keySignature ?? DEFAULT_KEY_SIGNATURE;
64
64
  const measureCount = opts.measures ?? DEFAULT_MEASURE_COUNT;
65
65
  const trackOptions = opts.tracks ?? [
66
- { name: 'Piano', instrumentName: 'Piano', clef: 'treble' },
66
+ { name: "Piano", instrumentName: "Piano", clef: "treble" },
67
67
  ];
68
- const tracks = trackOptions.map(trackOpts => {
68
+ const tracks = trackOptions.map((trackOpts) => {
69
69
  const track = createTrack(trackOpts);
70
70
  return {
71
71
  ...track,
@@ -95,7 +95,7 @@ export function createEmptyScore(opts) {
95
95
  * (falling back to 4/4 C major for a track with no existing measures).
96
96
  */
97
97
  export function appendMeasure(score) {
98
- const tracks = score.tracks.map(track => {
98
+ const tracks = score.tracks.map((track) => {
99
99
  const lastMeasure = track.measures[track.measures.length - 1];
100
100
  const timeSignature = lastMeasure?.timeSignature ?? DEFAULT_TIME_SIGNATURE;
101
101
  const keySignature = lastMeasure?.keySignature ?? DEFAULT_KEY_SIGNATURE;
@@ -121,7 +121,7 @@ export function appendMeasure(score) {
121
121
  * (referentially equal) so unaffected structure is preserved.
122
122
  */
123
123
  export function rebuildMeasureTicks(score) {
124
- const tracks = score.tracks.map(track => {
124
+ const tracks = score.tracks.map((track) => {
125
125
  let cursor = 0;
126
126
  const measures = track.measures.map((measure, index) => {
127
127
  const startTick = cursor;
@@ -132,9 +132,9 @@ export function rebuildMeasureTicks(score) {
132
132
  }
133
133
  const voices = delta === 0
134
134
  ? measure.voices
135
- : measure.voices.map(voice => ({
135
+ : measure.voices.map((voice) => ({
136
136
  ...voice,
137
- events: voice.events.map(event => ({
137
+ events: voice.events.map((event) => ({
138
138
  ...event,
139
139
  startTick: event.startTick + delta,
140
140
  })),
@@ -1 +1 @@
1
- {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/domain/score/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AASpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,GAAG,GAAG,CAAC;AACxB,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAC9B,MAAM,sBAAsB,GAAkB,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;AAC/E,MAAM,qBAAqB,GAAiB,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACzE,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAChC,MAAM,kBAAkB,GAAG,SAAS,CAAC;AAIrC,mFAAmF;AACnF,MAAM,UAAU,WAAW,CAAC,IAAwB;IAClD,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE,IAAI,QAAQ,EAAE;QACzB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,OAAO;QAC9C,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,CAAC;QAClC,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,CAAC;QAClC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,QAAQ;QAC3B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC;QACxB,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC;QAClB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,KAAK;QAC1B,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,KAAK;QACxB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;KAC9B,CAAC;AACJ,CAAC;AAED,yEAAyE;AACzE,SAAS,gBAAgB,CACvB,KAAa,EACb,SAAiB,EACjB,aAA4B,EAC5B,YAA0B,EAC1B,aAAqB,EACrB,OAAe;IAEf,MAAM,OAAO,GAAG,QAAQ,EAAE,CAAC;IAC3B,OAAO;QACL,EAAE,EAAE,QAAQ,EAAE;QACd,KAAK;QACL,SAAS;QACT,aAAa;QACb,aAAa;QACb,YAAY;QACZ,MAAM,EAAE;YACN;gBACE,EAAE,EAAE,OAAO;gBACX,IAAI,EAAE,kBAAkB;gBACxB,MAAM,EAAE;oBACN,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE;iBAC/D;aACF;SACF;KACF,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,SAAS,aAAa,CACpB,KAAa,EACb,aAA4B,EAC5B,YAA0B,EAC1B,GAAW,EACX,OAAe;IAEf,MAAM,aAAa,GAAG,oBAAoB,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC9C,QAAQ,CAAC,IAAI,CACX,gBAAgB,CACd,KAAK,EACL,KAAK,GAAG,aAAa,EACrB,aAAa,EACb,YAAY,EACZ,aAAa,EACb,OAAO,CACR,CACF,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAYD;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAA6B;IAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,WAAW,CAAC;IACpC,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,sBAAsB,CAAC;IACnE,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,qBAAqB,CAAC;IAChE,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,IAAI,qBAAqB,CAAC;IAC5D,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,IAAI;QAClC,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,QAAiB,EAAE;KACpE,CAAC;IAEF,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;QAC1C,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,OAAO;YACL,GAAG,KAAK;YACR,QAAQ,EAAE,aAAa,CACrB,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,GAAG,EACH,KAAK,CAAC,EAAE,CACT;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAkB;QAC9B,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,SAAS,EAAE,GAAG;QACd,SAAS,EAAE,GAAG;KACf,CAAC;IAEF,OAAO;QACL,EAAE,EAAE,QAAQ,EAAE;QACd,OAAO,EAAE,CAAC;QACV,GAAG;QACH,QAAQ;QACR,QAAQ,EAAE;YACR,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,IAAI,iBAAiB,EAAE;SAClE;QACD,MAAM;KACP,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAY;IACxC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;QACtC,MAAM,WAAW,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CACvC,CAAC;QACtB,MAAM,aAAa,GAAG,WAAW,EAAE,aAAa,IAAI,sBAAsB,CAAC;QAC3E,MAAM,YAAY,GAAG,WAAW,EAAE,YAAY,IAAI,qBAAqB,CAAC;QACxE,MAAM,aAAa,GAAG,oBAAoB,CAAC,aAAa,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QACrE,MAAM,SAAS,GAAG,WAAW;YAC3B,CAAC,CAAC,WAAW,CAAC,SAAS,GAAG,WAAW,CAAC,aAAa;YACnD,CAAC,CAAC,CAAC,CAAC;QACN,MAAM,UAAU,GAAG,gBAAgB,CACjC,KAAK,CAAC,QAAQ,CAAC,MAAM,EACrB,SAAS,EACT,aAAa,EACb,YAAY,EACZ,aAAa,EACb,KAAK,CAAC,EAAE,CACT,CAAC;QACF,OAAO,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,GAAG,KAAK;QACR,MAAM;QACN,QAAQ,EAAE,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE;KACrE,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAY;IAC9C,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;QACtC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;YACrD,MAAM,SAAS,GAAG,MAAM,CAAC;YACzB,MAAM,KAAK,GAAG,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;YAC5C,MAAM,IAAI,OAAO,CAAC,aAAa,CAAC;YAEhC,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC;gBAC3C,OAAO,OAAO,CAAC;YACjB,CAAC;YAED,MAAM,MAAM,GACV,KAAK,KAAK,CAAC;gBACT,CAAC,CAAC,OAAO,CAAC,MAAM;gBAChB,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;oBAC3B,GAAG,KAAK;oBACR,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;wBACjC,GAAG,KAAK;wBACR,SAAS,EAAE,KAAK,CAAC,SAAS,GAAG,KAAK;qBACnC,CAAC,CAAC;iBACJ,CAAC,CAAC,CAAC;YAEV,OAAO,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QAClD,CAAC,CAAC,CAAC;QACH,OAAO,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,CAAC;AAC9B,CAAC"}
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/domain/score/factory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Making a fermata audible, as a local slowing rather than a longer note.
3
+ *
4
+ * A pause holds a note and delays everything after it. The obvious model —
5
+ * stretch the note and shift the rest — is the wrong one here, because the
6
+ * score tick *is* the playback tick: the caret, the following-scroll, "play
7
+ * from here" and the position scrubber all rest on that identity, and moving
8
+ * notes in time would break every one of them.
9
+ *
10
+ * A tempo dip changes none of it. The notes keep their written ticks; the
11
+ * ticks simply take longer to elapse across the held note, which is what a
12
+ * fermata means to a player anyway — the beat stretches, the notation does
13
+ * not. **And it is the reason the caret needed no work at all**: it
14
+ * dead-reckons between position reports through a `TempoMap`, so a map that
15
+ * slows down slows the caret down with it. Building the caret's map from
16
+ * anything else would make it glide past the pause and snap back, which is
17
+ * exactly the class of bug its interpolation exists to avoid.
18
+ *
19
+ * So this is the single derivation, and `playbackPlan`, `renderEvents` and the
20
+ * caret all read it. Live playback, the exported audio file and the thing on
21
+ * screen therefore cannot disagree about how long a pause lasts.
22
+ *
23
+ * A score with no fermata returns its own `tempoMap` array **unchanged, by
24
+ * identity**, so nothing about an unmarked score's playback is even
25
+ * recomputed.
26
+ */
27
+ import type { Score, TempoEvent } from "../../index.js";
28
+ /**
29
+ * `score.tempoMap` with a slowing written across every held span.
30
+ *
31
+ * Two events per span: one entering it at the reduced tempo, one leaving it
32
+ * that restores whatever was in force at that point. The restoring event reads
33
+ * the tempo from the *original* map, so a tempo change that happens to fall
34
+ * inside a pause is still honoured on the way out rather than being flattened
35
+ * to whatever the pause started at.
36
+ *
37
+ * Returns the score's own array by identity when nothing is held.
38
+ */
39
+ export declare function fermataTempoMap(score: Score): TempoEvent[];
40
+ //# sourceMappingURL=fermata-tempo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fermata-tempo.d.ts","sourceRoot":"","sources":["../../../src/domain/score/fermata-tempo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAa,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AA8DnE;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,UAAU,EAAE,CA2B1D"}
@@ -0,0 +1,95 @@
1
+ import { isNoteEvent } from "../../index.js";
2
+ import { createId } from "./ids.js";
3
+ import { TempoMap } from "../time/tempo-map.js";
4
+ /**
5
+ * How much longer a held note lasts than its written value.
6
+ *
7
+ * Two is the conventional reading and the one most notation software uses. It
8
+ * is deliberately not configurable per fermata: how long to hold is the
9
+ * performer's judgement, and a score that recorded a number would be claiming
10
+ * an authority the marking does not have.
11
+ */
12
+ const HOLD_FACTOR = 2;
13
+ /**
14
+ * Every span a fermata holds, merged into non-overlapping runs.
15
+ *
16
+ * A fermata is observed by the whole ensemble at once, so overlapping spans
17
+ * are one pause. Merging is not about compounding — each span reads its
18
+ * reduced tempo from the *original* map, so two identical spans produce the
19
+ * same slowing twice and cancel out harmlessly. It is about spans of
20
+ * **different lengths**: a quarter note held in one part and a whole note held
21
+ * in another both start the pause, but without merging the quarter's restoring
22
+ * event lands in the middle of the whole note's hold and cuts it short. The
23
+ * players wait for each other; the pause ends when the longest one ends.
24
+ */
25
+ function holdSpans(score) {
26
+ const spans = [];
27
+ for (const track of score.tracks) {
28
+ for (const measure of track.measures) {
29
+ for (const voice of measure.voices) {
30
+ for (const event of voice.events) {
31
+ if (!isNoteEvent(event))
32
+ continue;
33
+ const note = event;
34
+ if (!note.fermata)
35
+ continue;
36
+ spans.push({
37
+ start: note.startTick,
38
+ end: note.startTick + note.durationTicks,
39
+ });
40
+ }
41
+ }
42
+ }
43
+ }
44
+ if (spans.length === 0)
45
+ return spans;
46
+ spans.sort((a, b) => a.start - b.start || a.end - b.end);
47
+ const merged = [spans[0]];
48
+ for (const span of spans.slice(1)) {
49
+ const last = merged[merged.length - 1];
50
+ if (span.start <= last.end) {
51
+ last.end = Math.max(last.end, span.end);
52
+ }
53
+ else {
54
+ merged.push(span);
55
+ }
56
+ }
57
+ return merged;
58
+ }
59
+ /**
60
+ * `score.tempoMap` with a slowing written across every held span.
61
+ *
62
+ * Two events per span: one entering it at the reduced tempo, one leaving it
63
+ * that restores whatever was in force at that point. The restoring event reads
64
+ * the tempo from the *original* map, so a tempo change that happens to fall
65
+ * inside a pause is still honoured on the way out rather than being flattened
66
+ * to whatever the pause started at.
67
+ *
68
+ * Returns the score's own array by identity when nothing is held.
69
+ */
70
+ export function fermataTempoMap(score) {
71
+ const spans = holdSpans(score);
72
+ if (spans.length === 0)
73
+ return score.tempoMap;
74
+ const source = new TempoMap(score.tempoMap, score.ppq);
75
+ const added = [];
76
+ for (const span of spans) {
77
+ if (span.end <= span.start)
78
+ continue;
79
+ added.push({
80
+ id: createId(),
81
+ tick: span.start,
82
+ bpm: source.bpmAt(span.start) / HOLD_FACTOR,
83
+ });
84
+ added.push({
85
+ id: createId(),
86
+ tick: span.end,
87
+ bpm: source.bpmAt(span.end),
88
+ });
89
+ }
90
+ // The added events win at a tick an original also sits on: a starting tempo
91
+ // written at the same beat as a pause still has to be paused through.
92
+ const kept = score.tempoMap.filter((event) => !added.some((a) => a.tick === event.tick));
93
+ return [...kept, ...added].sort((a, b) => a.tick - b.tick);
94
+ }
95
+ //# sourceMappingURL=fermata-tempo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fermata-tempo.js","sourceRoot":"","sources":["../../../src/domain/score/fermata-tempo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { MusicalEvent, Score, Track } from "../../index.js";
2
+ /**
3
+ * One sounding note, in score ticks, with the ids it came from.
4
+ *
5
+ * Structurally `PlaybackNote` in music_types, which is what the engine takes —
6
+ * so a `FlatNote[]` is assignable to it with no mapping step. Declared here
7
+ * rather than imported because this is domain data: a note the score sounds,
8
+ * independent of who is going to play it.
9
+ */
10
+ export type FlatNote = {
11
+ tick: number;
12
+ durTicks: number;
13
+ midi: number;
14
+ /** As stored on the event, 0-127. Callers that want 0..1 divide. */
15
+ velocity: number;
16
+ trackId: string;
17
+ noteId: string;
18
+ };
19
+ /**
20
+ * Every voice-ordinal channel of a track: across all measures in tick order,
21
+ * the events at that ordinal position.
22
+ *
23
+ * Voice ids are not stable across a barline, so the ordinal stands in for "the
24
+ * same voice" from bar to bar — which is what lets `joinTiedNotes` see a tie
25
+ * that crosses one. `ties.ts` exports a `voiceChannel`, but that takes a single
26
+ * ordinal and returns measure-annotated notes for tie-*partner* lookup, where
27
+ * this needs every ordinal at once with rests included.
28
+ */
29
+ export declare function trackVoiceChannels(track: Track): MusicalEvent[][];
30
+ /** Every note the score sounds, ties joined, in tick order. */
31
+ export declare function flattenScoreNotes(score: Score): FlatNote[];
32
+ //# sourceMappingURL=flatten.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flatten.d.ts","sourceRoot":"","sources":["../../../src/domain/score/flatten.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAIjE;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,YAAY,EAAE,EAAE,CAgBjE;AAED,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,EAAE,CA8F1D"}