@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,75 @@
1
+ /**
2
+ * The drawing vocabulary the instrument icons are authored in: a tiny,
3
+ * renderer-neutral shape list plus a parser for the path subset it allows.
4
+ *
5
+ * Two consumers draw the same art through different APIs — the canvas track
6
+ * gutter (`stroke-icon.ts`) and the app's `<svg>` component — so the art itself
7
+ * cannot be expressed in either one's terms. SVG path data is the natural
8
+ * authoring format, but handing it to canvas would mean `Path2D`, which jsdom
9
+ * does not implement; the gutter's drawing would then be untestable in the unit
10
+ * suite. Parsing the subset ourselves keeps one authored source and keeps both
11
+ * renderers testable.
12
+ *
13
+ * The subset is deliberately small: absolute `M`/`L`/`C`/`Q`/`Z` and explicit
14
+ * circles. No arcs (their SVG parameterisation is endpoint-based and converting
15
+ * it to canvas's centre-based `arc` is exactly the fiddly, bug-prone code this
16
+ * design avoids) and no relative commands (an icon is authored once and read
17
+ * many times; absolute coordinates stay readable). Anything else throws, so a
18
+ * malformed icon fails in the test suite rather than drawing silently wrong.
19
+ */
20
+ /** Icons are authored on a 24x24 grid, like most line-art sets. */
21
+ export declare const ICON_VIEWBOX = 24;
22
+ /**
23
+ * Stroke width in viewbox units. Scales with the icon, so the weight stays
24
+ * proportional at any size rather than going hairline when drawn small.
25
+ */
26
+ export declare const ICON_STROKE_WIDTH = 1.6;
27
+ export type IconShape = {
28
+ readonly kind: "path";
29
+ readonly d: string;
30
+ } | {
31
+ readonly kind: "circle";
32
+ readonly cx: number;
33
+ readonly cy: number;
34
+ readonly r: number;
35
+ };
36
+ /** One instrument's line art. `name` is a stable id — useful in tests and debugging, never shown. */
37
+ export type InstrumentIconArt = {
38
+ readonly name: string;
39
+ readonly shapes: readonly IconShape[];
40
+ };
41
+ export type PathSegment = {
42
+ kind: "move";
43
+ x: number;
44
+ y: number;
45
+ } | {
46
+ kind: "line";
47
+ x: number;
48
+ y: number;
49
+ } | {
50
+ kind: "cubic";
51
+ x1: number;
52
+ y1: number;
53
+ x2: number;
54
+ y2: number;
55
+ x: number;
56
+ y: number;
57
+ } | {
58
+ kind: "quad";
59
+ x1: number;
60
+ y1: number;
61
+ x: number;
62
+ y: number;
63
+ } | {
64
+ kind: "close";
65
+ };
66
+ /**
67
+ * Parses an authored path into segments any renderer can replay.
68
+ *
69
+ * Implements SVG's implicit-repetition rule (a command's operands may repeat
70
+ * without restating the letter) and its companion rule that the pairs after an
71
+ * `M` are line-tos, because both are how path data is normally written and an
72
+ * author would reasonably expect them to work.
73
+ */
74
+ export declare function parseIconPath(d: string): PathSegment[];
75
+ //# sourceMappingURL=icon-art.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icon-art.d.ts","sourceRoot":"","sources":["../../../src/domain/instruments/icon-art.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,mEAAmE;AACnE,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B;;;GAGG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEN,qGAAqG;AACrG,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GACtC;IACE,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX,GACD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9D;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AA4BtB;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAwCtD"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * The drawing vocabulary the instrument icons are authored in: a tiny,
3
+ * renderer-neutral shape list plus a parser for the path subset it allows.
4
+ *
5
+ * Two consumers draw the same art through different APIs — the canvas track
6
+ * gutter (`stroke-icon.ts`) and the app's `<svg>` component — so the art itself
7
+ * cannot be expressed in either one's terms. SVG path data is the natural
8
+ * authoring format, but handing it to canvas would mean `Path2D`, which jsdom
9
+ * does not implement; the gutter's drawing would then be untestable in the unit
10
+ * suite. Parsing the subset ourselves keeps one authored source and keeps both
11
+ * renderers testable.
12
+ *
13
+ * The subset is deliberately small: absolute `M`/`L`/`C`/`Q`/`Z` and explicit
14
+ * circles. No arcs (their SVG parameterisation is endpoint-based and converting
15
+ * it to canvas's centre-based `arc` is exactly the fiddly, bug-prone code this
16
+ * design avoids) and no relative commands (an icon is authored once and read
17
+ * many times; absolute coordinates stay readable). Anything else throws, so a
18
+ * malformed icon fails in the test suite rather than drawing silently wrong.
19
+ */
20
+ /** Icons are authored on a 24x24 grid, like most line-art sets. */
21
+ export const ICON_VIEWBOX = 24;
22
+ /**
23
+ * Stroke width in viewbox units. Scales with the icon, so the weight stays
24
+ * proportional at any size rather than going hairline when drawn small.
25
+ */
26
+ export const ICON_STROKE_WIDTH = 1.6;
27
+ const COMMAND_ARITY = { M: 2, L: 2, C: 6, Q: 4, Z: 0 };
28
+ /** Command letters and numbers; anything else in the string is ignored as separator. */
29
+ const TOKEN = /[A-Za-z]|-?\d*\.?\d+/g;
30
+ function segmentFor(command, n) {
31
+ switch (command) {
32
+ case "M":
33
+ return { kind: "move", x: n[0], y: n[1] };
34
+ case "L":
35
+ return { kind: "line", x: n[0], y: n[1] };
36
+ case "C":
37
+ return {
38
+ kind: "cubic",
39
+ x1: n[0],
40
+ y1: n[1],
41
+ x2: n[2],
42
+ y2: n[3],
43
+ x: n[4],
44
+ y: n[5],
45
+ };
46
+ default:
47
+ return { kind: "quad", x1: n[0], y1: n[1], x: n[2], y: n[3] };
48
+ }
49
+ }
50
+ /**
51
+ * Parses an authored path into segments any renderer can replay.
52
+ *
53
+ * Implements SVG's implicit-repetition rule (a command's operands may repeat
54
+ * without restating the letter) and its companion rule that the pairs after an
55
+ * `M` are line-tos, because both are how path data is normally written and an
56
+ * author would reasonably expect them to work.
57
+ */
58
+ export function parseIconPath(d) {
59
+ const tokens = d.match(TOKEN) ?? [];
60
+ const segments = [];
61
+ let command = null;
62
+ let i = 0;
63
+ while (i < tokens.length) {
64
+ if (/[A-Za-z]/.test(tokens[i])) {
65
+ command = tokens[i];
66
+ i += 1;
67
+ if (!(command in COMMAND_ARITY)) {
68
+ throw new Error(`Unsupported icon path command "${command}" in "${d}"`);
69
+ }
70
+ }
71
+ if (command == null)
72
+ throw new Error(`Icon path must start with a command: "${d}"`);
73
+ const arity = COMMAND_ARITY[command];
74
+ if (arity === 0) {
75
+ segments.push({ kind: "close" });
76
+ // Nothing may follow a Z but another command, so drop back to
77
+ // "expecting a letter" rather than repeating the close forever.
78
+ command = null;
79
+ continue;
80
+ }
81
+ const operands = tokens.slice(i, i + arity).map(Number);
82
+ if (operands.length < arity || operands.some(Number.isNaN)) {
83
+ throw new Error(`Icon path command "${command}" wants ${arity} numbers in "${d}"`);
84
+ }
85
+ i += arity;
86
+ segments.push(segmentFor(command, operands));
87
+ // SVG: the coordinate pairs following a moveto are implicit linetos.
88
+ if (command === "M")
89
+ command = "L";
90
+ }
91
+ return segments;
92
+ }
93
+ //# sourceMappingURL=icon-art.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icon-art.js","sourceRoot":"","sources":["../../../src/domain/instruments/icon-art.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { Score, UUID } from "../../index.js";
2
+ import type { ScoreCommand } from "../commands/types.js";
3
+ import { type MidiRange } from "./gm-range.js";
4
+ /** The instrument fields that move together — the name is stored, so it must not drift from the program. */
5
+ export type InstrumentPatch = {
6
+ midiProgram: number;
7
+ instrumentName: string;
8
+ };
9
+ /**
10
+ * The semitone shift that brings every pitch in `midis` inside `range`, or
11
+ * `null` if the span is simply too wide to fit.
12
+ *
13
+ * `0` when it already fits, so a caller can tell "no move needed" from
14
+ * "cannot be done" — the two are very different answers.
15
+ */
16
+ export declare function shiftToFitRange(midis: number[], range: MidiRange): number | null;
17
+ /**
18
+ * The shift `trackId`'s notes need to fit `midiProgram`, or `null` if they
19
+ * cannot. Ask this *before* dispatching, so a change that would be refused can
20
+ * be reported rather than silently doing nothing.
21
+ */
22
+ export declare function fitShiftForInstrument(score: Score, trackId: UUID, midiProgram: number): number | null;
23
+ /**
24
+ * Sets the instrument and moves the existing notes to suit it, as one command
25
+ * — so a single undo puts both back.
26
+ *
27
+ * A part that cannot be fitted leaves the score untouched; pair this with
28
+ * `fitShiftForInstrument` if you need to tell the user why nothing happened.
29
+ */
30
+ export declare function changeInstrumentCommand(trackId: UUID, patch: InstrumentPatch, label: string): ScoreCommand;
31
+ //# sourceMappingURL=instrument-fit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrument-fit.d.ts","sourceRoot":"","sources":["../../../src/domain/instruments/instrument-fit.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,KAAK,EAAS,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAMzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAElE,4GAA4G;AAC5G,MAAM,MAAM,eAAe,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC;AAO9E;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EAAE,EACf,KAAK,EAAE,SAAS,GACf,MAAM,GAAG,IAAI,CAwBf;AAcD;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,IAAI,EACb,WAAW,EAAE,MAAM,GAClB,MAAM,GAAG,IAAI,CAKf;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,IAAI,EACb,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE,MAAM,GACZ,YAAY,CAmCd"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Moving a track's music into a new instrument's compass.
3
+ *
4
+ * Changing the instrument on a track that already has notes is not just a
5
+ * relabelling: a bass line handed to a piccolo sits entirely below what a
6
+ * piccolo can play, and the result is a part that renders but cannot sound.
7
+ * So the notes come with it — shifted by whole **octaves** where that is
8
+ * enough, because an octave keeps the pitch class and a bassline still reads
9
+ * as that bassline. Only when no octave shift fits does it fall back to the
10
+ * smallest chromatic shift that does.
11
+ *
12
+ * A part wider than the instrument's own compass cannot be fitted at all, and
13
+ * that answer is returned rather than approximated — the caller decides what
14
+ * to say about it. Nothing here knows any user-facing words.
15
+ */
16
+ import { isNoteEvent } from "../../index.js";
17
+ import { transformCommand } from "../commands/snapshot.js";
18
+ import { withTracks } from "../commands/reflow.js";
19
+ import { findTrack } from "../score/queries.js";
20
+ import { pitchToMidi } from "../pitch/pitch.js";
21
+ import { transposePitch } from "../pitch/transpose.js";
22
+ import { gmInstrumentRange } from "./gm-range.js";
23
+ function closestToZero(min, max) {
24
+ if (min <= 0 && max >= 0)
25
+ return 0;
26
+ return min > 0 ? min : max;
27
+ }
28
+ /**
29
+ * The semitone shift that brings every pitch in `midis` inside `range`, or
30
+ * `null` if the span is simply too wide to fit.
31
+ *
32
+ * `0` when it already fits, so a caller can tell "no move needed" from
33
+ * "cannot be done" — the two are very different answers.
34
+ */
35
+ export function shiftToFitRange(midis, range) {
36
+ if (midis.length === 0)
37
+ return 0;
38
+ let min = Number.POSITIVE_INFINITY;
39
+ let max = Number.NEGATIVE_INFINITY;
40
+ for (const midi of midis) {
41
+ min = Math.min(min, midi);
42
+ max = Math.max(max, midi);
43
+ }
44
+ if (min >= range.min && max <= range.max)
45
+ return 0;
46
+ if (max - min > range.max - range.min)
47
+ return null;
48
+ // Octaves first, and the one closest to no move at all: transposing by an
49
+ // octave preserves the pitch class, so the part still reads the same.
50
+ const minOctaves = Math.ceil((range.min - min) / 12);
51
+ const maxOctaves = Math.floor((range.max - max) / 12);
52
+ if (minOctaves <= maxOctaves)
53
+ return closestToZero(minOctaves, maxOctaves) * 12;
54
+ const minSemitones = range.min - min;
55
+ const maxSemitones = range.max - max;
56
+ return minSemitones <= maxSemitones
57
+ ? closestToZero(minSemitones, maxSemitones)
58
+ : null;
59
+ }
60
+ function noteMidis(track) {
61
+ const midis = [];
62
+ for (const measure of track.measures) {
63
+ for (const voice of measure.voices) {
64
+ for (const event of voice.events) {
65
+ if (isNoteEvent(event))
66
+ midis.push(pitchToMidi(event.pitch));
67
+ }
68
+ }
69
+ }
70
+ return midis;
71
+ }
72
+ /**
73
+ * The shift `trackId`'s notes need to fit `midiProgram`, or `null` if they
74
+ * cannot. Ask this *before* dispatching, so a change that would be refused can
75
+ * be reported rather than silently doing nothing.
76
+ */
77
+ export function fitShiftForInstrument(score, trackId, midiProgram) {
78
+ const track = findTrack(score, trackId);
79
+ return track
80
+ ? shiftToFitRange(noteMidis(track), gmInstrumentRange(midiProgram))
81
+ : null;
82
+ }
83
+ /**
84
+ * Sets the instrument and moves the existing notes to suit it, as one command
85
+ * — so a single undo puts both back.
86
+ *
87
+ * A part that cannot be fitted leaves the score untouched; pair this with
88
+ * `fitShiftForInstrument` if you need to tell the user why nothing happened.
89
+ */
90
+ export function changeInstrumentCommand(trackId, patch, label) {
91
+ return transformCommand(label, (score) => {
92
+ const shift = fitShiftForInstrument(score, trackId, patch.midiProgram);
93
+ if (shift === null)
94
+ return score;
95
+ const tracks = score.tracks.map((candidate) => {
96
+ if (candidate.id !== trackId)
97
+ return candidate;
98
+ const measures = shift === 0
99
+ ? candidate.measures
100
+ : candidate.measures.map((measure) => ({
101
+ ...measure,
102
+ voices: measure.voices.map((voice) => ({
103
+ ...voice,
104
+ events: voice.events.map((event) => isNoteEvent(event)
105
+ ? {
106
+ ...event,
107
+ pitch: transposePitch(event.pitch, shift, measure.keySignature),
108
+ }
109
+ : event),
110
+ })),
111
+ }));
112
+ return { ...candidate, ...patch, measures };
113
+ });
114
+ return withTracks(score, tracks);
115
+ });
116
+ }
117
+ //# sourceMappingURL=instrument-fit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrument-fit.js","sourceRoot":"","sources":["../../../src/domain/instruments/instrument-fit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The General MIDI catalogue, shaped for a picker.
3
+ *
4
+ * Every caller that offers "which instrument?" — a generate dialog, a track
5
+ * property sheet — needs the same three things: the flattened list of melodic
6
+ * programs grouped by family, the eight drum kits, and a way to turn whichever
7
+ * one was chosen back into the fields a `Track` stores. Deriving those at each
8
+ * call site is how the two lists get confused, so they are derived once here.
9
+ *
10
+ * Kits are tagged rather than numbered, because a kit's program means
11
+ * something else entirely from a melodic one: on channel 10, program 8 is the
12
+ * Room Kit, not Celesta. That is what `KIT_PREFIX` is for, and why a bare
13
+ * program number must never be handed to a control offering `KIT_OPTIONS` —
14
+ * it matches nothing and the control renders empty while still selecting
15
+ * correctly, which is a bug that looks like a styling problem.
16
+ *
17
+ * No user-facing copy lives here beyond the GM names themselves, which are
18
+ * fixed identifiers rather than translatable strings — "Acoustic Grand Piano"
19
+ * is the program's name in every locale.
20
+ */
21
+ import type { Clef } from "../../index.js";
22
+ /** What a caller needs to write an instrument onto a track, or request one. */
23
+ export type InstrumentChoice = {
24
+ midiProgram: number;
25
+ instrumentName: string;
26
+ clef: Clef;
27
+ };
28
+ /** One entry in a picker: the value to round-trip, its label, and its family heading. */
29
+ export type InstrumentOption = {
30
+ value: string;
31
+ label: string;
32
+ group?: string;
33
+ };
34
+ /** The default selection: Piano, which is also the melody-carrying lead. */
35
+ export declare const DEFAULT_INSTRUMENT_VALUE = "0";
36
+ /**
37
+ * The `KIT_OPTIONS` value for a program.
38
+ *
39
+ * Through `gmKitAt`, so a track sitting at an address no kit is at still
40
+ * selects the kit it actually plays rather than showing an empty control.
41
+ */
42
+ export declare function kitOptionValue(program: number): string;
43
+ /** The eight GM drum kits, for a percussion track. */
44
+ export declare const KIT_OPTIONS: InstrumentOption[];
45
+ /**
46
+ * Every melodic program, grouped by GM family.
47
+ *
48
+ * Built once at module load: the catalogue is 128 fixed entries, so rebuilding
49
+ * it per render — which a track panel once did — was pure waste.
50
+ */
51
+ export declare const INSTRUMENT_OPTIONS: InstrumentOption[];
52
+ /** The same catalogue as nested groups, for a picker that draws its own headings. */
53
+ export declare const FAMILY_GROUPS: {
54
+ key: import("./gm.js").GmFamily;
55
+ label: string;
56
+ instruments: readonly import("./gm.js").GmInstrument[];
57
+ }[];
58
+ /** Turns a picker value — from either list — back into track fields. */
59
+ export declare function instrumentChoiceFor(value: string): InstrumentChoice;
60
+ /** The label shown for a catalogue value. */
61
+ export declare function instrumentLabelFor(value: string): string;
62
+ //# sourceMappingURL=instrument-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrument-options.d.ts","sourceRoot":"","sources":["../../../src/domain/instruments/instrument-options.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAS3C,+EAA+E;AAC/E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,IAAI,CAAC;CACZ,CAAC;AAEF,yFAAyF;AACzF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAIF,4EAA4E;AAC5E,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,sDAAsD;AACtD,eAAO,MAAM,WAAW,EAAE,gBAAgB,EAGvC,CAAC;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,gBAAgB,EAOhD,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,aAAa;;;;GAIvB,CAAC;AAYJ,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAiBnE;AAED,6CAA6C;AAC7C,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD"}
@@ -0,0 +1,69 @@
1
+ import { GM_FAMILIES, GM_FAMILY_LABELS, gmInstrument, gmInstrumentsByFamily, } from "./gm.js";
2
+ import { GM_KITS, gmKit, gmKitAt } from "./gm-kit.js";
3
+ const KIT_PREFIX = "kit:";
4
+ /** The default selection: Piano, which is also the melody-carrying lead. */
5
+ export const DEFAULT_INSTRUMENT_VALUE = "0";
6
+ /**
7
+ * The `KIT_OPTIONS` value for a program.
8
+ *
9
+ * Through `gmKitAt`, so a track sitting at an address no kit is at still
10
+ * selects the kit it actually plays rather than showing an empty control.
11
+ */
12
+ export function kitOptionValue(program) {
13
+ return `${KIT_PREFIX}${gmKitAt(program).program}`;
14
+ }
15
+ /** The eight GM drum kits, for a percussion track. */
16
+ export const KIT_OPTIONS = GM_KITS.map((kit) => ({
17
+ value: `${KIT_PREFIX}${kit.program}`,
18
+ label: kit.name,
19
+ }));
20
+ /**
21
+ * Every melodic program, grouped by GM family.
22
+ *
23
+ * Built once at module load: the catalogue is 128 fixed entries, so rebuilding
24
+ * it per render — which a track panel once did — was pure waste.
25
+ */
26
+ export const INSTRUMENT_OPTIONS = GM_FAMILIES.flatMap((family) => gmInstrumentsByFamily(family).map((instrument) => ({
27
+ value: String(instrument.program),
28
+ label: instrument.name,
29
+ group: GM_FAMILY_LABELS[family],
30
+ })));
31
+ /** The same catalogue as nested groups, for a picker that draws its own headings. */
32
+ export const FAMILY_GROUPS = GM_FAMILIES.map((family) => ({
33
+ key: family,
34
+ label: GM_FAMILY_LABELS[family],
35
+ instruments: gmInstrumentsByFamily(family),
36
+ }));
37
+ /**
38
+ * The clef a melodic program is written on.
39
+ *
40
+ * Only the Bass family reads better on the bass staff; everything else starts
41
+ * on treble, which can be changed on the track afterwards.
42
+ */
43
+ function clefForProgram(program) {
44
+ return program >= 32 && program <= 39 ? "bass" : "treble";
45
+ }
46
+ /** Turns a picker value — from either list — back into track fields. */
47
+ export function instrumentChoiceFor(value) {
48
+ if (value.startsWith(KIT_PREFIX)) {
49
+ // `gmKit` rather than the raw number: an address no kit sits on falls back
50
+ // to Standard instead of producing a track nothing can play.
51
+ const kit = gmKit(Number(value.slice(KIT_PREFIX.length))) ?? GM_KITS[0];
52
+ return {
53
+ midiProgram: kit.program,
54
+ instrumentName: kit.name,
55
+ clef: "percussion",
56
+ };
57
+ }
58
+ const program = Number(value);
59
+ return {
60
+ midiProgram: program,
61
+ instrumentName: gmInstrument(program)?.name ?? "Piano",
62
+ clef: clefForProgram(program),
63
+ };
64
+ }
65
+ /** The label shown for a catalogue value. */
66
+ export function instrumentLabelFor(value) {
67
+ return instrumentChoiceFor(value).instrumentName;
68
+ }
69
+ //# sourceMappingURL=instrument-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrument-options.js","sourceRoot":"","sources":["../../../src/domain/instruments/instrument-options.ts"],"names":[],"mappings":"AAqBA,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,qBAAqB,GACtB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAgBtD,MAAM,UAAU,GAAG,MAAM,CAAC;AAE1B,4EAA4E;AAC5E,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;AACpD,CAAC;AAED,sDAAsD;AACtD,MAAM,CAAC,MAAM,WAAW,GAAuB,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACnE,KAAK,EAAE,GAAG,UAAU,GAAG,GAAG,CAAC,OAAO,EAAE;IACpC,KAAK,EAAE,GAAG,CAAC,IAAI;CAChB,CAAC,CAAC,CAAC;AAEJ;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAuB,WAAW,CAAC,OAAO,CACvE,CAAC,MAAM,EAAE,EAAE,CACT,qBAAqB,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACjD,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC;IACjC,KAAK,EAAE,UAAU,CAAC,IAAI;IACtB,KAAK,EAAE,gBAAgB,CAAC,MAAM,CAAC;CAChC,CAAC,CAAC,CACN,CAAC;AAEF,qFAAqF;AACrF,MAAM,CAAC,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACxD,GAAG,EAAE,MAAM;IACX,KAAK,EAAE,gBAAgB,CAAC,MAAM,CAAC;IAC/B,WAAW,EAAE,qBAAqB,CAAC,MAAM,CAAC;CAC3C,CAAC,CAAC,CAAC;AAEJ;;;;;GAKG;AACH,SAAS,cAAc,CAAC,OAAe;IACrC,OAAO,OAAO,IAAI,EAAE,IAAI,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC5D,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,IAAI,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACjC,2EAA2E;QAC3E,6DAA6D;QAC7D,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;QACxE,OAAO;YACL,WAAW,EAAE,GAAG,CAAC,OAAO;YACxB,cAAc,EAAE,GAAG,CAAC,IAAI;YACxB,IAAI,EAAE,YAAY;SACnB,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,OAAO;QACL,WAAW,EAAE,OAAO;QACpB,cAAc,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,IAAI,IAAI,OAAO;QACtD,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC;KAC9B,CAAC;AACJ,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,OAAO,mBAAmB,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC;AACnD,CAAC"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Instrument lookups that take a **track**, not a program number.
3
+ *
4
+ * `Track.midiProgram` means one of two different things depending on the
5
+ * track's clef: a General MIDI instrument on a pitched track, a drum kit on a
6
+ * percussion one. Every table in this folder is keyed by program, so reading
7
+ * one without knowing the clef silently answers for the wrong thing — and the
8
+ * wrong answers were not obviously wrong, which is why they shipped:
9
+ *
10
+ * - Brush is kit 40, and program 40 is Violin, so `gmMaxPolyphony` capped a
11
+ * drum track at two simultaneous notes and the toolbar refused a three-piece
12
+ * crash-snare-kick hit.
13
+ * - Electronic and TR-808 are kits 24 and 25, and programs 24-25 are guitars,
14
+ * which are written an octave above where they sound — so in written mode a
15
+ * TR-808 part was drawn an octave off the staff position for its own drums.
16
+ * - Every kit is some melodic instrument's range, so the on-screen keyboard
17
+ * offered keys that could not sound a drum and hid the ones that could.
18
+ *
19
+ * These wrappers are the single place that distinction is encoded. Call them
20
+ * instead of the program-keyed tables anywhere a `Track` is in hand.
21
+ */
22
+ import type { Score, Track } from "../../index.js";
23
+ import { type MidiRange } from "./gm-range.js";
24
+ import type { InstrumentIconArt } from "./icon-art.js";
25
+ /** Whether `track`'s `midiProgram` addresses a drum kit rather than an instrument. */
26
+ export declare function isPercussionTrack(track: Pick<Track, "clef">): boolean;
27
+ /**
28
+ * The notes worth showing for `track` — General MIDI's drum range on a
29
+ * percussion track, the instrument's compass otherwise.
30
+ */
31
+ export declare function trackKeyboardRange(track: Pick<Track, "clef" | "midiProgram">): MidiRange;
32
+ /**
33
+ * How many notes `track` can sound at once.
34
+ *
35
+ * Unlimited on a drum track: the limit exists to stop a part being written that
36
+ * nobody could play, and a kit is played with two hands and two feet across
37
+ * pieces that are all struck separately. Nothing about a kit address says
38
+ * otherwise.
39
+ */
40
+ export declare function trackMaxPolyphony(track: Pick<Track, "clef" | "midiProgram">): number;
41
+ /**
42
+ * Semitones between `track`'s sounding and written pitch.
43
+ *
44
+ * Always zero on a drum track. A percussion staff's positions name drums, so
45
+ * transposing one does not move a part into a reader's key — it renames every
46
+ * drum in it.
47
+ */
48
+ export declare function trackWrittenTransposition(track: Pick<Track, "clef" | "midiProgram">): number;
49
+ /**
50
+ * `track`'s program, corrected to something its clef can actually mean.
51
+ *
52
+ * On a percussion track that is the kit whose region the address falls in; a
53
+ * MIDI file is free to set program 45 on channel 10, and the kit that resolves
54
+ * to is Brush. Off a percussion track the program is already an instrument and
55
+ * is returned untouched.
56
+ */
57
+ export declare function trackProgramForClef(track: Pick<Track, "clef" | "midiProgram">): number;
58
+ /** The icon for `track` — a kit on a percussion track, its instrument's art otherwise. */
59
+ export declare function trackInstrumentIcon(track: Pick<Track, "clef" | "midiProgram">): InstrumentIconArt;
60
+ /** What to call `track`'s sound: its kit's name on a percussion track, its instrument's otherwise. */
61
+ export declare function trackInstrumentLabel(track: Pick<Track, "clef" | "midiProgram">): string;
62
+ /**
63
+ * `score` with every percussion track's program resolved to a real kit.
64
+ *
65
+ * A drum track can arrive holding an address no kit sits at: a MIDI file sets
66
+ * whatever program it likes on channel 10, and nothing before this treated that
67
+ * number as a kit at all. The picker would then have to show something the
68
+ * score does not say, so the score is corrected instead of the display fudged.
69
+ *
70
+ * A track already on a kit address is returned untouched — including its
71
+ * `instrumentName`, which is the user's description of a sound that is not
72
+ * changing. Only a track being corrected has its name rewritten, because the
73
+ * old one described a different kit.
74
+ *
75
+ * Returns the **identical object** when nothing needs correcting, which is the
76
+ * usual case: `computeLayout` is cached by score identity, and a fresh object
77
+ * on every load would also read as a change to the autosaver.
78
+ */
79
+ export declare function scoreWithResolvedKits(score: Score): Score;
80
+ //# sourceMappingURL=track-instrument.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-instrument.d.ts","sourceRoot":"","sources":["../../../src/domain/instruments/track-instrument.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAMlE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGvD,sFAAsF;AACtF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,OAAO,CAErE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,GACzC,SAAS,CAGX;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,GACzC,MAAM,CAGR;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,GACzC,MAAM,CAGR;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,GACzC,MAAM,CAIR;AAED,0FAA0F;AAC1F,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,GACzC,iBAAiB,CAInB;AAED,sGAAsG;AACtG,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,GACzC,MAAM,CAGR;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAezD"}