@sudobility/music_types 0.11.20 → 0.11.21

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 (149) hide show
  1. package/dist/domain/commands/edit-commands.d.ts +66 -0
  2. package/dist/domain/commands/edit-commands.d.ts.map +1 -0
  3. package/dist/domain/commands/edit-commands.js +185 -0
  4. package/dist/domain/commands/edit-commands.js.map +1 -0
  5. package/dist/domain/commands/note-commands.d.ts +73 -0
  6. package/dist/domain/commands/note-commands.d.ts.map +1 -0
  7. package/dist/domain/commands/note-commands.js +292 -0
  8. package/dist/domain/commands/note-commands.js.map +1 -0
  9. package/dist/domain/commands/reflow.d.ts +69 -0
  10. package/dist/domain/commands/reflow.d.ts.map +1 -0
  11. package/dist/domain/commands/reflow.js +395 -0
  12. package/dist/domain/commands/reflow.js.map +1 -0
  13. package/dist/domain/commands/region-commands.d.ts +21 -0
  14. package/dist/domain/commands/region-commands.d.ts.map +1 -0
  15. package/dist/domain/commands/region-commands.js +47 -0
  16. package/dist/domain/commands/region-commands.js.map +1 -0
  17. package/dist/domain/commands/relocate-commands.d.ts +18 -0
  18. package/dist/domain/commands/relocate-commands.d.ts.map +1 -0
  19. package/dist/domain/commands/relocate-commands.js +108 -0
  20. package/dist/domain/commands/relocate-commands.js.map +1 -0
  21. package/dist/domain/commands/ripple-commands.d.ts +44 -0
  22. package/dist/domain/commands/ripple-commands.d.ts.map +1 -0
  23. package/dist/domain/commands/ripple-commands.js +104 -0
  24. package/dist/domain/commands/ripple-commands.js.map +1 -0
  25. package/dist/domain/commands/snapshot.d.ts +38 -0
  26. package/dist/domain/commands/snapshot.d.ts.map +1 -0
  27. package/dist/domain/commands/snapshot.js +44 -0
  28. package/dist/domain/commands/snapshot.js.map +1 -0
  29. package/dist/domain/commands/structure-commands.d.ts +38 -0
  30. package/dist/domain/commands/structure-commands.d.ts.map +1 -0
  31. package/dist/domain/commands/structure-commands.js +220 -0
  32. package/dist/domain/commands/structure-commands.js.map +1 -0
  33. package/dist/domain/commands/track-commands.d.ts +15 -0
  34. package/dist/domain/commands/track-commands.d.ts.map +1 -0
  35. package/dist/domain/commands/track-commands.js +56 -0
  36. package/dist/domain/commands/track-commands.js.map +1 -0
  37. package/dist/domain/commands/types.d.ts +31 -0
  38. package/dist/domain/commands/types.d.ts.map +1 -0
  39. package/dist/domain/commands/types.js +2 -0
  40. package/dist/domain/commands/types.js.map +1 -0
  41. package/dist/domain/instruments/gm-kit.d.ts +45 -0
  42. package/dist/domain/instruments/gm-kit.d.ts.map +1 -0
  43. package/dist/domain/instruments/gm-kit.js +59 -0
  44. package/dist/domain/instruments/gm-kit.js.map +1 -0
  45. package/dist/domain/instruments/gm-percussion.d.ts +42 -0
  46. package/dist/domain/instruments/gm-percussion.d.ts.map +1 -0
  47. package/dist/domain/instruments/gm-percussion.js +85 -0
  48. package/dist/domain/instruments/gm-percussion.js.map +1 -0
  49. package/dist/domain/instruments/gm-polyphony.d.ts +13 -0
  50. package/dist/domain/instruments/gm-polyphony.d.ts.map +1 -0
  51. package/dist/domain/instruments/gm-polyphony.js +76 -0
  52. package/dist/domain/instruments/gm-polyphony.js.map +1 -0
  53. package/dist/domain/instruments/gm-range.d.ts +15 -0
  54. package/dist/domain/instruments/gm-range.d.ts.map +1 -0
  55. package/dist/domain/instruments/gm-range.js +97 -0
  56. package/dist/domain/instruments/gm-range.js.map +1 -0
  57. package/dist/domain/instruments/gm-transposition.d.ts +11 -0
  58. package/dist/domain/instruments/gm-transposition.d.ts.map +1 -0
  59. package/dist/domain/instruments/gm-transposition.js +69 -0
  60. package/dist/domain/instruments/gm-transposition.js.map +1 -0
  61. package/dist/domain/instruments/gm.d.ts +28 -0
  62. package/dist/domain/instruments/gm.d.ts.map +1 -0
  63. package/dist/domain/instruments/gm.js +217 -0
  64. package/dist/domain/instruments/gm.js.map +1 -0
  65. package/dist/domain/pitch/pitch.d.ts +15 -0
  66. package/dist/domain/pitch/pitch.d.ts.map +1 -0
  67. package/dist/domain/pitch/pitch.js +81 -0
  68. package/dist/domain/pitch/pitch.js.map +1 -0
  69. package/dist/domain/pitch/transpose.d.ts +37 -0
  70. package/dist/domain/pitch/transpose.d.ts.map +1 -0
  71. package/dist/domain/pitch/transpose.js +65 -0
  72. package/dist/domain/pitch/transpose.js.map +1 -0
  73. package/dist/domain/quantization/options.d.ts +55 -0
  74. package/dist/domain/quantization/options.d.ts.map +1 -0
  75. package/dist/domain/quantization/options.js +2 -0
  76. package/dist/domain/quantization/options.js.map +1 -0
  77. package/dist/domain/quantization/quantize.d.ts +13 -0
  78. package/dist/domain/quantization/quantize.d.ts.map +1 -0
  79. package/dist/domain/quantization/quantize.js +199 -0
  80. package/dist/domain/quantization/quantize.js.map +1 -0
  81. package/dist/domain/score/factory.d.ts +39 -0
  82. package/dist/domain/score/factory.d.ts.map +1 -0
  83. package/dist/domain/score/factory.js +148 -0
  84. package/dist/domain/score/factory.js.map +1 -0
  85. package/dist/domain/score/fragment.d.ts +28 -0
  86. package/dist/domain/score/fragment.d.ts.map +1 -0
  87. package/dist/domain/score/fragment.js +68 -0
  88. package/dist/domain/score/fragment.js.map +1 -0
  89. package/dist/domain/score/ids.d.ts +8 -0
  90. package/dist/domain/score/ids.d.ts.map +1 -0
  91. package/dist/domain/score/ids.js +38 -0
  92. package/dist/domain/score/ids.js.map +1 -0
  93. package/dist/domain/score/queries.d.ts +34 -0
  94. package/dist/domain/score/queries.d.ts.map +1 -0
  95. package/dist/domain/score/queries.js +132 -0
  96. package/dist/domain/score/queries.js.map +1 -0
  97. package/dist/domain/score/ties.d.ts +73 -0
  98. package/dist/domain/score/ties.d.ts.map +1 -0
  99. package/dist/domain/score/ties.js +200 -0
  100. package/dist/domain/score/ties.js.map +1 -0
  101. package/dist/domain/selection/selection.d.ts +63 -0
  102. package/dist/domain/selection/selection.d.ts.map +1 -0
  103. package/dist/domain/selection/selection.js +129 -0
  104. package/dist/domain/selection/selection.js.map +1 -0
  105. package/dist/domain/selection/types.d.ts +10 -0
  106. package/dist/domain/selection/types.d.ts.map +1 -0
  107. package/dist/domain/selection/types.js +5 -0
  108. package/dist/domain/selection/types.js.map +1 -0
  109. package/dist/domain/time/durations.d.ts +21 -0
  110. package/dist/domain/time/durations.d.ts.map +1 -0
  111. package/dist/domain/time/durations.js +53 -0
  112. package/dist/domain/time/durations.js.map +1 -0
  113. package/dist/domain/time/fraction.d.ts +19 -0
  114. package/dist/domain/time/fraction.d.ts.map +1 -0
  115. package/dist/domain/time/fraction.js +60 -0
  116. package/dist/domain/time/fraction.js.map +1 -0
  117. package/dist/domain/time/tempo-map.d.ts +24 -0
  118. package/dist/domain/time/tempo-map.d.ts.map +1 -0
  119. package/dist/domain/time/tempo-map.js +85 -0
  120. package/dist/domain/time/tempo-map.js.map +1 -0
  121. package/dist/domain/time/ticks.d.ts +34 -0
  122. package/dist/domain/time/ticks.d.ts.map +1 -0
  123. package/dist/domain/time/ticks.js +93 -0
  124. package/dist/domain/time/ticks.js.map +1 -0
  125. package/dist/domain/validation/issues.d.ts +44 -0
  126. package/dist/domain/validation/issues.d.ts.map +1 -0
  127. package/dist/domain/validation/issues.js +34 -0
  128. package/dist/domain/validation/issues.js.map +1 -0
  129. package/dist/domain/validation/validator.d.ts +11 -0
  130. package/dist/domain/validation/validator.d.ts.map +1 -0
  131. package/dist/domain/validation/validator.js +375 -0
  132. package/dist/domain/validation/validator.js.map +1 -0
  133. package/dist/domain/voicing/allocate.d.ts +19 -0
  134. package/dist/domain/voicing/allocate.d.ts.map +1 -0
  135. package/dist/domain/voicing/allocate.js +100 -0
  136. package/dist/domain/voicing/allocate.js.map +1 -0
  137. package/dist/index.d.ts +46 -4
  138. package/dist/index.d.ts.map +1 -1
  139. package/dist/index.js +50 -2
  140. package/dist/index.js.map +1 -1
  141. package/dist/services/regeneration/controller.d.ts +60 -0
  142. package/dist/services/regeneration/controller.d.ts.map +1 -0
  143. package/dist/services/regeneration/controller.js +202 -0
  144. package/dist/services/regeneration/controller.js.map +1 -0
  145. package/dist/test/fixtures.d.ts +45 -0
  146. package/dist/test/fixtures.d.ts.map +1 -0
  147. package/dist/test/fixtures.js +420 -0
  148. package/dist/test/fixtures.js.map +1 -0
  149. package/package.json +2 -1
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Semitones to add to `program`'s sounding pitch to get its written pitch.
3
+ *
4
+ * `0` for anything written where it sounds, and for any program outside
5
+ * 0-127: an unknown instrument is left alone rather than moved by a guess,
6
+ * which would be a silently wrong part.
7
+ */
8
+ export declare function gmWrittenTransposition(program: number): number;
9
+ /** Whether `program` is written anywhere other than where it sounds. */
10
+ export declare function gmIsTransposing(program: number): boolean;
11
+ //# sourceMappingURL=gm-transposition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gm-transposition.d.ts","sourceRoot":"","sources":["../../../src/domain/instruments/gm-transposition.ts"],"names":[],"mappings":"AA0DA;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAExD"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * How far a General MIDI program's written pitch sits from its sounding pitch.
3
+ *
4
+ * A transposing instrument is written in a key other than the one it sounds: a
5
+ * B♭ trumpet reads a tone above what comes out, so concert C is written D.
6
+ * This is a property of the instrument, not a preference — hand a clarinettist
7
+ * a part at concert pitch and every note is wrong.
8
+ *
9
+ * Curated like `gm-range.ts` and `gm-polyphony.ts`: a default with overrides
10
+ * for the programs that actually transpose. Most do not, so the table below is
11
+ * only the exceptions.
12
+ */
13
+ import { gmInstrument } from './gm.js';
14
+ /** Semitones to ADD to sounding pitch to get written pitch. */
15
+ const PROGRAM_TRANSPOSITION = {
16
+ // Octave-transposing tuned percussion: written an octave (or two) below
17
+ // where they sound, so the reader is not chasing ledger lines.
18
+ 8: -12, // Celesta
19
+ 9: -24, // Glockenspiel
20
+ 13: -12, // Xylophone
21
+ // Guitars and basses are written an octave above where they sound.
22
+ 24: 12, // Acoustic Guitar (nylon)
23
+ 25: 12, // Acoustic Guitar (steel)
24
+ 26: 12, // Electric Guitar (jazz)
25
+ 27: 12, // Electric Guitar (clean)
26
+ 28: 12, // Electric Guitar (muted)
27
+ 29: 12, // Overdriven Guitar
28
+ 30: 12, // Distortion Guitar
29
+ 31: 12, // Guitar Harmonics
30
+ 32: 12, // Acoustic Bass
31
+ 33: 12, // Electric Bass (finger)
32
+ 34: 12, // Electric Bass (pick)
33
+ 35: 12, // Fretless Bass
34
+ 36: 12, // Slap Bass 1
35
+ 37: 12, // Slap Bass 2
36
+ 38: 12, // Synth Bass 1
37
+ 39: 12, // Synth Bass 2
38
+ 43: 12, // Contrabass
39
+ // Brass.
40
+ 56: 2, // Trumpet — B♭
41
+ 60: 7, // French Horn — F
42
+ // Reeds. The saxophone family transposes by four different intervals, which
43
+ // is why they cannot share a family default.
44
+ 64: 2, // Soprano Sax — B♭
45
+ 65: 9, // Alto Sax — E♭
46
+ 66: 14, // Tenor Sax — B♭, a ninth below
47
+ 67: 21, // Baritone Sax — E♭, an octave and a sixth below
48
+ 69: 7, // English Horn — F
49
+ 71: 2, // Clarinet — B♭
50
+ // Pipes.
51
+ 72: -12, // Piccolo — sounds an octave above where it is written
52
+ };
53
+ /**
54
+ * Semitones to add to `program`'s sounding pitch to get its written pitch.
55
+ *
56
+ * `0` for anything written where it sounds, and for any program outside
57
+ * 0-127: an unknown instrument is left alone rather than moved by a guess,
58
+ * which would be a silently wrong part.
59
+ */
60
+ export function gmWrittenTransposition(program) {
61
+ if (!gmInstrument(program))
62
+ return 0;
63
+ return PROGRAM_TRANSPOSITION[program] ?? 0;
64
+ }
65
+ /** Whether `program` is written anywhere other than where it sounds. */
66
+ export function gmIsTransposing(program) {
67
+ return gmWrittenTransposition(program) !== 0;
68
+ }
69
+ //# sourceMappingURL=gm-transposition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gm-transposition.js","sourceRoot":"","sources":["../../../src/domain/instruments/gm-transposition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,+DAA+D;AAC/D,MAAM,qBAAqB,GAA2B;IACpD,wEAAwE;IACxE,+DAA+D;IAC/D,CAAC,EAAE,CAAC,EAAE,EAAE,UAAU;IAClB,CAAC,EAAE,CAAC,EAAE,EAAE,eAAe;IACvB,EAAE,EAAE,CAAC,EAAE,EAAE,YAAY;IAErB,mEAAmE;IACnE,EAAE,EAAE,EAAE,EAAE,0BAA0B;IAClC,EAAE,EAAE,EAAE,EAAE,0BAA0B;IAClC,EAAE,EAAE,EAAE,EAAE,yBAAyB;IACjC,EAAE,EAAE,EAAE,EAAE,0BAA0B;IAClC,EAAE,EAAE,EAAE,EAAE,0BAA0B;IAClC,EAAE,EAAE,EAAE,EAAE,oBAAoB;IAC5B,EAAE,EAAE,EAAE,EAAE,oBAAoB;IAC5B,EAAE,EAAE,EAAE,EAAE,mBAAmB;IAC3B,EAAE,EAAE,EAAE,EAAE,gBAAgB;IACxB,EAAE,EAAE,EAAE,EAAE,yBAAyB;IACjC,EAAE,EAAE,EAAE,EAAE,uBAAuB;IAC/B,EAAE,EAAE,EAAE,EAAE,gBAAgB;IACxB,EAAE,EAAE,EAAE,EAAE,cAAc;IACtB,EAAE,EAAE,EAAE,EAAE,cAAc;IACtB,EAAE,EAAE,EAAE,EAAE,eAAe;IACvB,EAAE,EAAE,EAAE,EAAE,eAAe;IACvB,EAAE,EAAE,EAAE,EAAE,aAAa;IAErB,SAAS;IACT,EAAE,EAAE,CAAC,EAAE,eAAe;IACtB,EAAE,EAAE,CAAC,EAAE,kBAAkB;IAEzB,4EAA4E;IAC5E,6CAA6C;IAC7C,EAAE,EAAE,CAAC,EAAE,mBAAmB;IAC1B,EAAE,EAAE,CAAC,EAAE,gBAAgB;IACvB,EAAE,EAAE,EAAE,EAAE,gCAAgC;IACxC,EAAE,EAAE,EAAE,EAAE,iDAAiD;IACzD,EAAE,EAAE,CAAC,EAAE,mBAAmB;IAC1B,EAAE,EAAE,CAAC,EAAE,gBAAgB;IAEvB,SAAS;IACT,EAAE,EAAE,CAAC,EAAE,EAAE,uDAAuD;CACjE,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAe;IACpD,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC;QAAE,OAAO,CAAC,CAAC;IACrC,OAAO,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,sBAAsB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The General MIDI Level 1 sound set: 128 programs, grouped into 16 families
3
+ * of 8. Pure data and lookups — no Tone, no VexFlow, no store.
4
+ *
5
+ * `Track.midiProgram` is exactly a program number from this table, and
6
+ * `music_types` already validates it to 0-127, so this module never has to
7
+ * defend against a bad value from the schema — only against a hand-edited
8
+ * score, which is why `gmInstrument` returns `null` rather than throwing.
9
+ */
10
+ export type GmFamily = 'piano' | 'chromatic-percussion' | 'organ' | 'guitar' | 'bass' | 'strings' | 'ensemble' | 'brass' | 'reed' | 'pipe' | 'synth-lead' | 'synth-pad' | 'synth-effects' | 'ethnic' | 'percussive' | 'sound-effects';
11
+ export type GmInstrument = {
12
+ /** 0-127, matching `Track.midiProgram`. */
13
+ program: number;
14
+ /** The General MIDI name, e.g. "Acoustic Grand Piano". */
15
+ name: string;
16
+ family: GmFamily;
17
+ };
18
+ /** In program order: family `i` covers programs `i*8 .. i*8+7`. */
19
+ export declare const GM_FAMILIES: readonly GmFamily[];
20
+ export declare const GM_FAMILY_LABELS: Record<GmFamily, string>;
21
+ /** The family a program belongs to. Arithmetic, because families are runs of eight. */
22
+ export declare function gmFamilyOf(program: number): GmFamily;
23
+ export declare const GM_INSTRUMENTS: readonly GmInstrument[];
24
+ /** `null` for anything that is not an integer in 0-127. */
25
+ export declare function gmInstrument(program: number): GmInstrument | null;
26
+ /** The eight programs of one family, in program order — what the picker groups by. */
27
+ export declare function gmInstrumentsByFamily(family: GmFamily): readonly GmInstrument[];
28
+ //# sourceMappingURL=gm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gm.d.ts","sourceRoot":"","sources":["../../../src/domain/instruments/gm.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,QAAQ,GAChB,OAAO,GACP,sBAAsB,GACtB,OAAO,GACP,QAAQ,GACR,MAAM,GACN,SAAS,GACT,UAAU,GACV,OAAO,GACP,MAAM,GACN,MAAM,GACN,YAAY,GACZ,WAAW,GACX,eAAe,GACf,QAAQ,GACR,YAAY,GACZ,eAAe,CAAC;AAEpB,MAAM,MAAM,YAAY,GAAG;IACzB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,CAAC;CAClB,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,WAAW,EAAE,SAAS,QAAQ,EAiB1C,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAiBrD,CAAC;AA0JF,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAEpD;AAED,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAMjD,CAAC;AAEF,2DAA2D;AAC3D,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAGjE;AAED,sFAAsF;AACtF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,QAAQ,GACf,SAAS,YAAY,EAAE,CAEzB"}
@@ -0,0 +1,217 @@
1
+ /**
2
+ * The General MIDI Level 1 sound set: 128 programs, grouped into 16 families
3
+ * of 8. Pure data and lookups — no Tone, no VexFlow, no store.
4
+ *
5
+ * `Track.midiProgram` is exactly a program number from this table, and
6
+ * `music_types` already validates it to 0-127, so this module never has to
7
+ * defend against a bad value from the schema — only against a hand-edited
8
+ * score, which is why `gmInstrument` returns `null` rather than throwing.
9
+ */
10
+ /** In program order: family `i` covers programs `i*8 .. i*8+7`. */
11
+ export const GM_FAMILIES = [
12
+ 'piano',
13
+ 'chromatic-percussion',
14
+ 'organ',
15
+ 'guitar',
16
+ 'bass',
17
+ 'strings',
18
+ 'ensemble',
19
+ 'brass',
20
+ 'reed',
21
+ 'pipe',
22
+ 'synth-lead',
23
+ 'synth-pad',
24
+ 'synth-effects',
25
+ 'ethnic',
26
+ 'percussive',
27
+ 'sound-effects',
28
+ ];
29
+ export const GM_FAMILY_LABELS = {
30
+ piano: 'Piano',
31
+ 'chromatic-percussion': 'Chromatic Percussion',
32
+ organ: 'Organ',
33
+ guitar: 'Guitar',
34
+ bass: 'Bass',
35
+ strings: 'Strings',
36
+ ensemble: 'Ensemble',
37
+ brass: 'Brass',
38
+ reed: 'Reed',
39
+ pipe: 'Pipe',
40
+ 'synth-lead': 'Synth Lead',
41
+ 'synth-pad': 'Synth Pad',
42
+ 'synth-effects': 'Synth Effects',
43
+ ethnic: 'Ethnic',
44
+ percussive: 'Percussive',
45
+ 'sound-effects': 'Sound Effects',
46
+ };
47
+ /**
48
+ * The 128 names in program order. Families are NOT stored per row: they are a
49
+ * contiguous run of eight, so `gmFamilyOf` derives them and the table cannot
50
+ * drift out of sync with `GM_FAMILIES`.
51
+ */
52
+ const GM_NAMES = [
53
+ // 0-7 Piano
54
+ 'Acoustic Grand Piano',
55
+ 'Bright Acoustic Piano',
56
+ 'Electric Grand Piano',
57
+ 'Honky-tonk Piano',
58
+ 'Electric Piano 1',
59
+ 'Electric Piano 2',
60
+ 'Harpsichord',
61
+ 'Clavinet',
62
+ // 8-15 Chromatic Percussion
63
+ 'Celesta',
64
+ 'Glockenspiel',
65
+ 'Music Box',
66
+ 'Vibraphone',
67
+ 'Marimba',
68
+ 'Xylophone',
69
+ 'Tubular Bells',
70
+ 'Dulcimer',
71
+ // 16-23 Organ
72
+ 'Drawbar Organ',
73
+ 'Percussive Organ',
74
+ 'Rock Organ',
75
+ 'Church Organ',
76
+ 'Reed Organ',
77
+ 'Accordion',
78
+ 'Harmonica',
79
+ 'Tango Accordion',
80
+ // 24-31 Guitar
81
+ 'Acoustic Guitar (nylon)',
82
+ 'Acoustic Guitar (steel)',
83
+ 'Electric Guitar (jazz)',
84
+ 'Electric Guitar (clean)',
85
+ 'Electric Guitar (muted)',
86
+ 'Overdriven Guitar',
87
+ 'Distortion Guitar',
88
+ 'Guitar Harmonics',
89
+ // 32-39 Bass
90
+ 'Acoustic Bass',
91
+ 'Electric Bass (finger)',
92
+ 'Electric Bass (pick)',
93
+ 'Fretless Bass',
94
+ 'Slap Bass 1',
95
+ 'Slap Bass 2',
96
+ 'Synth Bass 1',
97
+ 'Synth Bass 2',
98
+ // 40-47 Strings
99
+ 'Violin',
100
+ 'Viola',
101
+ 'Cello',
102
+ 'Contrabass',
103
+ 'Tremolo Strings',
104
+ 'Pizzicato Strings',
105
+ 'Orchestral Harp',
106
+ 'Timpani',
107
+ // 48-55 Ensemble
108
+ 'String Ensemble 1',
109
+ 'String Ensemble 2',
110
+ 'Synth Strings 1',
111
+ 'Synth Strings 2',
112
+ 'Choir Aahs',
113
+ 'Voice Oohs',
114
+ 'Synth Voice',
115
+ 'Orchestra Hit',
116
+ // 56-63 Brass
117
+ 'Trumpet',
118
+ 'Trombone',
119
+ 'Tuba',
120
+ 'Muted Trumpet',
121
+ 'French Horn',
122
+ 'Brass Section',
123
+ 'Synth Brass 1',
124
+ 'Synth Brass 2',
125
+ // 64-71 Reed
126
+ 'Soprano Sax',
127
+ 'Alto Sax',
128
+ 'Tenor Sax',
129
+ 'Baritone Sax',
130
+ 'Oboe',
131
+ 'English Horn',
132
+ 'Bassoon',
133
+ 'Clarinet',
134
+ // 72-79 Pipe
135
+ 'Piccolo',
136
+ 'Flute',
137
+ 'Recorder',
138
+ 'Pan Flute',
139
+ 'Blown Bottle',
140
+ 'Shakuhachi',
141
+ 'Whistle',
142
+ 'Ocarina',
143
+ // 80-87 Synth Lead
144
+ 'Lead 1 (square)',
145
+ 'Lead 2 (sawtooth)',
146
+ 'Lead 3 (calliope)',
147
+ 'Lead 4 (chiff)',
148
+ 'Lead 5 (charang)',
149
+ 'Lead 6 (voice)',
150
+ 'Lead 7 (fifths)',
151
+ 'Lead 8 (bass + lead)',
152
+ // 88-95 Synth Pad
153
+ 'Pad 1 (new age)',
154
+ 'Pad 2 (warm)',
155
+ 'Pad 3 (polysynth)',
156
+ 'Pad 4 (choir)',
157
+ 'Pad 5 (bowed)',
158
+ 'Pad 6 (metallic)',
159
+ 'Pad 7 (halo)',
160
+ 'Pad 8 (sweep)',
161
+ // 96-103 Synth Effects
162
+ 'FX 1 (rain)',
163
+ 'FX 2 (soundtrack)',
164
+ 'FX 3 (crystal)',
165
+ 'FX 4 (atmosphere)',
166
+ 'FX 5 (brightness)',
167
+ 'FX 6 (goblins)',
168
+ 'FX 7 (echoes)',
169
+ 'FX 8 (sci-fi)',
170
+ // 104-111 Ethnic
171
+ 'Sitar',
172
+ 'Banjo',
173
+ 'Shamisen',
174
+ 'Koto',
175
+ 'Kalimba',
176
+ 'Bagpipe',
177
+ 'Fiddle',
178
+ 'Shanai',
179
+ // 112-119 Percussive
180
+ 'Tinkle Bell',
181
+ 'Agogo',
182
+ 'Steel Drums',
183
+ 'Woodblock',
184
+ 'Taiko Drum',
185
+ 'Melodic Tom',
186
+ 'Synth Drum',
187
+ 'Reverse Cymbal',
188
+ // 120-127 Sound Effects
189
+ 'Guitar Fret Noise',
190
+ 'Breath Noise',
191
+ 'Seashore',
192
+ 'Bird Tweet',
193
+ 'Telephone Ring',
194
+ 'Helicopter',
195
+ 'Applause',
196
+ 'Gunshot',
197
+ ];
198
+ /** The family a program belongs to. Arithmetic, because families are runs of eight. */
199
+ export function gmFamilyOf(program) {
200
+ return GM_FAMILIES[Math.floor(program / 8)];
201
+ }
202
+ export const GM_INSTRUMENTS = GM_NAMES.map((name, program) => ({
203
+ program,
204
+ name,
205
+ family: gmFamilyOf(program),
206
+ }));
207
+ /** `null` for anything that is not an integer in 0-127. */
208
+ export function gmInstrument(program) {
209
+ if (!Number.isInteger(program) || program < 0 || program > 127)
210
+ return null;
211
+ return GM_INSTRUMENTS[program];
212
+ }
213
+ /** The eight programs of one family, in program order — what the picker groups by. */
214
+ export function gmInstrumentsByFamily(family) {
215
+ return GM_INSTRUMENTS.filter(instrument => instrument.family === family);
216
+ }
217
+ //# sourceMappingURL=gm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gm.js","sourceRoot":"","sources":["../../../src/domain/instruments/gm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { KeySignature, Pitch } from '../../index.js';
2
+ /** Converts a `Pitch` to its MIDI note number (C4 = 60, A4 = 69). */
3
+ export declare function pitchToMidi(p: Pitch): number;
4
+ /**
5
+ * Converts a MIDI note number to a `Pitch`, choosing an enharmonic spelling
6
+ * appropriate to the given key signature: sharps for sharp/neutral keys
7
+ * (`fifths >= 0`), flats for flat keys (`fifths < 0`). Defaults to sharp
8
+ * spelling when no key signature is given.
9
+ */
10
+ export declare function midiToPitch(midi: number, keySignature?: KeySignature): Pitch;
11
+ /** Formats a `Pitch` as a string, e.g. `C4`, `F#3`, `Bb4`, `F##4`. */
12
+ export declare function pitchToString(p: Pitch): string;
13
+ /** Whether a pitch's MIDI value falls within an inclusive range. */
14
+ export declare function isPitchInRange(p: Pitch, lowestMidi: number, highestMidi: number): boolean;
15
+ //# sourceMappingURL=pitch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pitch.d.ts","sourceRoot":"","sources":["../../../src/domain/pitch/pitch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,YAAY,EACZ,KAAK,EAEN,MAAM,gBAAgB,CAAC;AAiBxB,qEAAqE;AACrE,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM,CAO5C;AAkCD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,YAAY,GAAG,KAAK,CAS5E;AAUD,sEAAsE;AACtE,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM,CAE9C;AAED,oEAAoE;AACpE,wBAAgB,cAAc,CAC5B,CAAC,EAAE,KAAK,EACR,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAGT"}
@@ -0,0 +1,81 @@
1
+ /** Semitone offset from C for each natural pitch step (no accidental). */
2
+ const NATURAL_STEP_SEMITONES = {
3
+ C: 0,
4
+ D: 2,
5
+ E: 4,
6
+ F: 5,
7
+ G: 7,
8
+ A: 9,
9
+ B: 11,
10
+ };
11
+ /** MIDI note number for C4 ("middle C"), per spec §4 (C4 = 60). */
12
+ const MIDI_C4 = 60;
13
+ const C4_OCTAVE = 4;
14
+ /** Converts a `Pitch` to its MIDI note number (C4 = 60, A4 = 69). */
15
+ export function pitchToMidi(p) {
16
+ return (MIDI_C4 +
17
+ (p.octave - C4_OCTAVE) * 12 +
18
+ NATURAL_STEP_SEMITONES[p.step] +
19
+ p.accidental);
20
+ }
21
+ /** step/accidental spelling for each pitch class 0-11, preferring sharps. */
22
+ const SHARP_SPELLING = [
23
+ { step: 'C', accidental: 0 },
24
+ { step: 'C', accidental: 1 },
25
+ { step: 'D', accidental: 0 },
26
+ { step: 'D', accidental: 1 },
27
+ { step: 'E', accidental: 0 },
28
+ { step: 'F', accidental: 0 },
29
+ { step: 'F', accidental: 1 },
30
+ { step: 'G', accidental: 0 },
31
+ { step: 'G', accidental: 1 },
32
+ { step: 'A', accidental: 0 },
33
+ { step: 'A', accidental: 1 },
34
+ { step: 'B', accidental: 0 },
35
+ ];
36
+ /** step/accidental spelling for each pitch class 0-11, preferring flats. */
37
+ const FLAT_SPELLING = [
38
+ { step: 'C', accidental: 0 },
39
+ { step: 'D', accidental: -1 },
40
+ { step: 'D', accidental: 0 },
41
+ { step: 'E', accidental: -1 },
42
+ { step: 'E', accidental: 0 },
43
+ { step: 'F', accidental: 0 },
44
+ { step: 'G', accidental: -1 },
45
+ { step: 'G', accidental: 0 },
46
+ { step: 'A', accidental: -1 },
47
+ { step: 'A', accidental: 0 },
48
+ { step: 'B', accidental: -1 },
49
+ { step: 'B', accidental: 0 },
50
+ ];
51
+ /**
52
+ * Converts a MIDI note number to a `Pitch`, choosing an enharmonic spelling
53
+ * appropriate to the given key signature: sharps for sharp/neutral keys
54
+ * (`fifths >= 0`), flats for flat keys (`fifths < 0`). Defaults to sharp
55
+ * spelling when no key signature is given.
56
+ */
57
+ export function midiToPitch(midi, keySignature) {
58
+ const useSharps = keySignature === undefined || keySignature.fifths >= 0;
59
+ const spelling = useSharps ? SHARP_SPELLING : FLAT_SPELLING;
60
+ const pitchClass = ((midi % 12) + 12) % 12;
61
+ const octave = Math.floor(midi / 12) - 1;
62
+ const { step, accidental } = spelling[pitchClass];
63
+ return { step, accidental, octave };
64
+ }
65
+ const ACCIDENTAL_SYMBOLS = {
66
+ [-2]: 'bb',
67
+ [-1]: 'b',
68
+ [0]: '',
69
+ [1]: '#',
70
+ [2]: '##',
71
+ };
72
+ /** Formats a `Pitch` as a string, e.g. `C4`, `F#3`, `Bb4`, `F##4`. */
73
+ export function pitchToString(p) {
74
+ return `${p.step}${ACCIDENTAL_SYMBOLS[p.accidental]}${p.octave}`;
75
+ }
76
+ /** Whether a pitch's MIDI value falls within an inclusive range. */
77
+ export function isPitchInRange(p, lowestMidi, highestMidi) {
78
+ const midi = pitchToMidi(p);
79
+ return midi >= lowestMidi && midi <= highestMidi;
80
+ }
81
+ //# sourceMappingURL=pitch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pitch.js","sourceRoot":"","sources":["../../../src/domain/pitch/pitch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { KeySignature, Pitch } from '../../index.js';
2
+ /**
3
+ * Transposes a pitch by a number of semitones (may be negative), re-spelling
4
+ * the result according to the given key signature (see `midiToPitch`).
5
+ */
6
+ export declare function transposePitch(p: Pitch, semitones: number, key?: KeySignature): Pitch;
7
+ /**
8
+ * Shifts a pitch by whole octaves, preserving its step and accidental
9
+ * (diatonic spelling never changes for an octave-only transposition).
10
+ */
11
+ export declare function transposeDiatonicOctave(p: Pitch, octaves: number): Pitch;
12
+ /**
13
+ * Moves `p` by `steps` positions on the staff — one step per line-to-adjacent-
14
+ * space — keeping its accidental.
15
+ *
16
+ * Diatonic, not chromatic, because this exists for dragging a note up and down
17
+ * a staff: the reader is moving it to a *staff position*, and one position is
18
+ * a tone in some places and a semitone in others. Transposing by semitones
19
+ * would make the note refuse to follow the pointer across E-F and B-C.
20
+ *
21
+ * The accidental rides along unchanged, which is what a drag should do: moving
22
+ * F# up one step gives G#, not G. Respelling is the caller's business.
23
+ */
24
+ export declare function shiftDiatonic(p: Pitch, steps: number): Pitch;
25
+ /**
26
+ * The key `key` becomes when the music is transposed by `semitones`.
27
+ *
28
+ * Moving up a fifth adds one sharp, so a shift of `s` semitones moves the key
29
+ * by `s × 7` fifths — the circle of fifths advances 7 semitones per step. The
30
+ * result is folded into -6..6 so a minor third up reads as three flats rather
31
+ * than nine sharps: both name the same key, but only one is printable.
32
+ *
33
+ * An octave leaves the key untouched, which falls out of the arithmetic rather
34
+ * than needing a special case.
35
+ */
36
+ export declare function transposeKeySignature(key: KeySignature, semitones: number): KeySignature;
37
+ //# sourceMappingURL=transpose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transpose.d.ts","sourceRoot":"","sources":["../../../src/domain/pitch/transpose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAa,MAAM,gBAAgB,CAAC;AAGrE;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,CAAC,EAAE,KAAK,EACR,SAAS,EAAE,MAAM,EACjB,GAAG,CAAC,EAAE,YAAY,GACjB,KAAK,CAEP;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAExE;AAKD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAW5D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,YAAY,EACjB,SAAS,EAAE,MAAM,GAChB,YAAY,CAWd"}
@@ -0,0 +1,65 @@
1
+ import { midiToPitch, pitchToMidi } from './pitch.js';
2
+ /**
3
+ * Transposes a pitch by a number of semitones (may be negative), re-spelling
4
+ * the result according to the given key signature (see `midiToPitch`).
5
+ */
6
+ export function transposePitch(p, semitones, key) {
7
+ return midiToPitch(pitchToMidi(p) + semitones, key);
8
+ }
9
+ /**
10
+ * Shifts a pitch by whole octaves, preserving its step and accidental
11
+ * (diatonic spelling never changes for an octave-only transposition).
12
+ */
13
+ export function transposeDiatonicOctave(p, octaves) {
14
+ return { ...p, octave: p.octave + octaves };
15
+ }
16
+ /** The seven letter names, in order, for diatonic movement. */
17
+ const STEPS = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];
18
+ /**
19
+ * Moves `p` by `steps` positions on the staff — one step per line-to-adjacent-
20
+ * space — keeping its accidental.
21
+ *
22
+ * Diatonic, not chromatic, because this exists for dragging a note up and down
23
+ * a staff: the reader is moving it to a *staff position*, and one position is
24
+ * a tone in some places and a semitone in others. Transposing by semitones
25
+ * would make the note refuse to follow the pointer across E-F and B-C.
26
+ *
27
+ * The accidental rides along unchanged, which is what a drag should do: moving
28
+ * F# up one step gives G#, not G. Respelling is the caller's business.
29
+ */
30
+ export function shiftDiatonic(p, steps) {
31
+ if (steps === 0)
32
+ return p;
33
+ const index = STEPS.indexOf(p.step);
34
+ if (index < 0)
35
+ return p;
36
+ const absolute = index + steps;
37
+ // Floor division, so it works for downward moves as well as upward.
38
+ const octaveShift = Math.floor(absolute / STEPS.length);
39
+ const wrapped = ((absolute % STEPS.length) + STEPS.length) % STEPS.length;
40
+ return { ...p, step: STEPS[wrapped], octave: p.octave + octaveShift };
41
+ }
42
+ /**
43
+ * The key `key` becomes when the music is transposed by `semitones`.
44
+ *
45
+ * Moving up a fifth adds one sharp, so a shift of `s` semitones moves the key
46
+ * by `s × 7` fifths — the circle of fifths advances 7 semitones per step. The
47
+ * result is folded into -6..6 so a minor third up reads as three flats rather
48
+ * than nine sharps: both name the same key, but only one is printable.
49
+ *
50
+ * An octave leaves the key untouched, which falls out of the arithmetic rather
51
+ * than needing a special case.
52
+ */
53
+ export function transposeKeySignature(key, semitones) {
54
+ const raw = (((semitones * 7) % 12) + 12) % 12;
55
+ const delta = raw > 6 ? raw - 12 : raw;
56
+ let fifths = key.fifths + delta;
57
+ // The sum can still land outside the printable range when the starting key
58
+ // is already remote; fold it back the same way.
59
+ while (fifths > 7)
60
+ fifths -= 12;
61
+ while (fifths < -7)
62
+ fifths += 12;
63
+ return { fifths, mode: key.mode };
64
+ }
65
+ //# sourceMappingURL=transpose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transpose.js","sourceRoot":"","sources":["../../../src/domain/pitch/transpose.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEtD;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,CAAQ,EACR,SAAiB,EACjB,GAAkB;IAElB,OAAO,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,GAAG,CAAC,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,CAAQ,EAAE,OAAe;IAC/D,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,OAAO,EAAE,CAAC;AAC9C,CAAC;AAED,+DAA+D;AAC/D,MAAM,KAAK,GAAyB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAExE;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,CAAQ,EAAE,KAAa;IACnD,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IAExB,MAAM,QAAQ,GAAG,KAAK,GAAG,KAAK,CAAC;IAC/B,oEAAoE;IACpE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,CAAC,CAAC,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;IAE1E,OAAO,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,WAAW,EAAE,CAAC;AACxE,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB,CACnC,GAAiB,EACjB,SAAiB;IAEjB,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;IAC/C,MAAM,KAAK,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IAEvC,IAAI,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC;IAChC,2EAA2E;IAC3E,gDAAgD;IAChD,OAAO,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,EAAE,CAAC;IAChC,OAAO,MAAM,GAAG,CAAC,CAAC;QAAE,MAAM,IAAI,EAAE,CAAC;IAEjC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;AACpC,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Options for `quantizeEvents` (spec §24). All fields except `grid`,
3
+ * `quantizeStarts`, and `quantizeDurations` are opt-in: omitting one of the
4
+ * optional fields disables that behavior entirely (no implicit defaults are
5
+ * substituted for them by `quantize.ts`, except a hard, undocumented-by-the-
6
+ * caller floor of 1 tick so a returned event's `durationTicks` is never
7
+ * <= 0 regardless of `minDurationTicks`).
8
+ */
9
+ export type QuantizeOptions = {
10
+ /** Grid unit in ticks (e.g. 120 ticks = a sixteenth note at 480 ppq). */
11
+ grid: number;
12
+ /** Snap each event's `startTick` to the grid (see `tripletGrid`/`swing`/`humanizeToleranceTicks`). */
13
+ quantizeStarts: boolean;
14
+ /** Snap each event's `durationTicks` to a multiple of the grid. */
15
+ quantizeDurations: boolean;
16
+ /**
17
+ * Fraction (0..1) of one grid unit that every second grid slot (odd
18
+ * slot index: 1, 3, 5, ...) is delayed by. 0 = straight (no shift);
19
+ * 0.5 = classic swung-eighths feel; 1 = fully delayed into the next
20
+ * slot. Only affects start quantization, and only takes effect when
21
+ * `quantizeStarts` is true.
22
+ */
23
+ swing?: number;
24
+ /** Use a triplet subdivision of `grid` (2/3 of `grid`) instead of `grid` itself when snapping starts/durations. */
25
+ tripletGrid?: boolean;
26
+ /**
27
+ * Minimum allowed `durationTicks`. A `NoteEvent` whose *original*
28
+ * duration is shorter than this is dropped entirely (`RestEvent`s are
29
+ * never dropped). A duration that becomes shorter than this purely as a
30
+ * quantization rounding artifact is clamped up to this floor instead of
31
+ * being dropped.
32
+ */
33
+ minDurationTicks?: number;
34
+ /**
35
+ * If an event's original `startTick` is already within this many ticks
36
+ * of where start-quantization would snap it, its original `startTick`
37
+ * is kept instead of being forced onto the grid (a deterministic
38
+ * "already close enough" tolerance; `quantize.ts` is a pure function, so
39
+ * this is not randomized humanization).
40
+ */
41
+ humanizeToleranceTicks?: number;
42
+ /**
43
+ * Events (within the same track+voice) whose quantized start ticks fall
44
+ * within this many ticks of one another are clustered onto a single
45
+ * shared start tick (grouped as a chord).
46
+ */
47
+ chordToleranceTicks?: number;
48
+ /** Extend each event's end to meet the next event's start in the same track+voice, closing small gaps. */
49
+ legatoCleanup?: boolean;
50
+ /** Trim an event that overlaps the next event's start in the same track+voice, resolving the overlap. */
51
+ resolveOverlaps?: boolean;
52
+ /** Insert `RestEvent`s to fill any remaining silent gaps within each track+voice. */
53
+ fillGaps?: boolean;
54
+ };
55
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/domain/quantization/options.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,sGAAsG;IACtG,cAAc,EAAE,OAAO,CAAC;IACxB,mEAAmE;IACnE,iBAAiB,EAAE,OAAO,CAAC;IAC3B;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mHAAmH;IACnH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,0GAA0G;IAC1G,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,yGAAyG;IACzG,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qFAAqF;IACrF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../../../src/domain/quantization/options.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import type { MusicalEvent } from '../../index.js';
2
+ import type { QuantizeOptions } from './options.js';
3
+ /**
4
+ * Quantizes `events` per `opts` (spec §24): drops undersized notes, snaps
5
+ * starts (grid/triplet/swing/humanize tolerance) and durations (grid,
6
+ * clamped to a minimum), then — per (trackId, voiceId) group — clusters
7
+ * near-simultaneous onsets into chords, resolves overlaps, closes gaps
8
+ * (legato), and fills remaining silence with rests. Every stage after
9
+ * dropping is opt-in via `opts`; omitting a field disables that stage.
10
+ * Returns a new array; never mutates `events` or its elements.
11
+ */
12
+ export declare function quantizeEvents(events: MusicalEvent[], opts: QuantizeOptions): MusicalEvent[];
13
+ //# sourceMappingURL=quantize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quantize.d.ts","sourceRoot":"","sources":["../../../src/domain/quantization/quantize.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AA2MpD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,YAAY,EAAE,EACtB,IAAI,EAAE,eAAe,GACpB,YAAY,EAAE,CAqBhB"}