@sudobility/music_drawing 0.0.32 → 0.0.34

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 (47) hide show
  1. package/dist/cursor.d.ts +60 -0
  2. package/dist/cursor.d.ts.map +1 -0
  3. package/dist/cursor.js +157 -0
  4. package/dist/cursor.js.map +1 -0
  5. package/dist/index.d.ts +15 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +15 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/keyboard-geometry.d.ts +50 -2
  10. package/dist/keyboard-geometry.d.ts.map +1 -1
  11. package/dist/keyboard-geometry.js +71 -2
  12. package/dist/keyboard-geometry.js.map +1 -1
  13. package/dist/keyboard-keys.d.ts +93 -0
  14. package/dist/keyboard-keys.d.ts.map +1 -0
  15. package/dist/keyboard-keys.js +60 -0
  16. package/dist/keyboard-keys.js.map +1 -0
  17. package/dist/pagination.d.ts +6 -2
  18. package/dist/pagination.d.ts.map +1 -1
  19. package/dist/pagination.js +4 -0
  20. package/dist/pagination.js.map +1 -1
  21. package/dist/playback-binding.d.ts +57 -0
  22. package/dist/playback-binding.d.ts.map +1 -0
  23. package/dist/playback-binding.js +110 -0
  24. package/dist/playback-binding.js.map +1 -0
  25. package/dist/playhead.d.ts.map +1 -1
  26. package/dist/playhead.js +22 -3
  27. package/dist/playhead.js.map +1 -1
  28. package/dist/press.d.ts +66 -0
  29. package/dist/press.d.ts.map +1 -0
  30. package/dist/press.js +64 -0
  31. package/dist/press.js.map +1 -0
  32. package/dist/print-plan.d.ts +64 -0
  33. package/dist/print-plan.d.ts.map +1 -0
  34. package/dist/print-plan.js +101 -0
  35. package/dist/print-plan.js.map +1 -0
  36. package/dist/render-theme.d.ts.map +1 -1
  37. package/dist/render-theme.js +11 -2
  38. package/dist/render-theme.js.map +1 -1
  39. package/dist/score-canvas.d.ts +225 -0
  40. package/dist/score-canvas.d.ts.map +1 -0
  41. package/dist/score-canvas.js +541 -0
  42. package/dist/score-canvas.js.map +1 -0
  43. package/dist/types.d.ts +13 -0
  44. package/dist/types.d.ts.map +1 -1
  45. package/dist/types.js +1 -1
  46. package/dist/types.js.map +1 -1
  47. package/package.json +3 -3
@@ -0,0 +1,60 @@
1
+ import type { NotePositions } from './playhead.js';
2
+ import type { LayoutPlan } from './layout.js';
3
+ import type { Score } from '@sudobility/music_types';
4
+ /** The cursor at one tick, in content px. `x` is the centre of the line. */
5
+ export type CursorGeometry = {
6
+ x: number;
7
+ top: number;
8
+ height: number;
9
+ };
10
+ /**
11
+ * Tick → x for one system, as knots.
12
+ *
13
+ * `caretPositionForTick` is piecewise-linear between note onsets and bar
14
+ * boundaries (it interpolates between the drawn noteheads), so interpolating
15
+ * linearly between exactly those points reproduces it and the cursor passes
16
+ * through every notehead.
17
+ */
18
+ export type CursorPath = {
19
+ /** Which system of the plan this describes. */
20
+ systemIndex: number;
21
+ /** Ascending ticks; the first is the system's first tick, the last just inside its end. */
22
+ ticks: number[];
23
+ /** Content x of each knot; never decreasing. */
24
+ xs: number[];
25
+ top: number;
26
+ height: number;
27
+ /**
28
+ * The gutter's right edge in **view** px. The line is hidden while it is left
29
+ * of this: the track info is painted over the sheet there.
30
+ */
31
+ clipLeft: number;
32
+ };
33
+ /**
34
+ * How the cursor moves: at `performance.now() === atMs` the playhead was at
35
+ * `tick`, advancing `ticksPerSecond`. A rate of 0 holds it at `tick`.
36
+ */
37
+ export type CursorMotion = {
38
+ tick: number;
39
+ atMs: number;
40
+ ticksPerSecond: number;
41
+ };
42
+ /** The first tick of every measure, and where the score ends after them. */
43
+ export declare function measureStartTicks(score: Score): number[];
44
+ /** The system a tick is drawn in, or -1 for an empty plan. */
45
+ export declare function systemIndexForTick(plan: LayoutPlan, starts: readonly number[], tick: number): number;
46
+ /** The tick where a system's music ends (the next system's first tick). */
47
+ export declare function systemEndTick(plan: LayoutPlan, starts: readonly number[], systemIndex: number): number;
48
+ /** The cursor at a tick, in content px — expectation 1 of the spec. */
49
+ export declare function cursorGeometryAt(plan: LayoutPlan, score: Score, zoom: number, tick: number, positions: NotePositions | undefined): CursorGeometry | null;
50
+ /** The knots for the system `systemIndex`, in content px. */
51
+ export declare function cursorPathFor(plan: LayoutPlan, score: Score, zoom: number, starts: readonly number[], systemIndex: number, positions: NotePositions | undefined): CursorPath | null;
52
+ /** Where the playhead is at `nowMs`, per `motion`. Never before the anchor. */
53
+ export declare function cursorTickAt(motion: CursorMotion, nowMs: number): number;
54
+ /** Content x for a tick along a path, clamped to the path's ends. */
55
+ export declare function cursorXAt(path: CursorPath, tick: number): number;
56
+ /** Whether the line at content `x` shows, given the view's scroll — expectation 4. */
57
+ export declare function cursorVisible(path: CursorPath, x: number, scrollLeft: number): boolean;
58
+ /** Two paths draw the same line (within half a pixel). */
59
+ export declare function sameCursorPath(a: CursorPath | null, b: CursorPath | null): boolean;
60
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../src/cursor.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAErD,4EAA4E;AAC5E,MAAM,MAAM,cAAc,GAAG;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gDAAgD;IAChD,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAKF,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,CAUxD;AAED,8DAA8D;AAC9D,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,IAAI,EAAE,MAAM,GACX,MAAM,CASR;AAED,2EAA2E;AAC3E,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,WAAW,EAAE,MAAM,GAClB,MAAM,CAMR;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,aAAa,GAAG,SAAS,GACnC,cAAc,GAAG,IAAI,CAQvB;AAED,6DAA6D;AAC7D,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,aAAa,GAAG,SAAS,GACnC,UAAU,GAAG,IAAI,CAyCnB;AAED,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGxE;AAED,qEAAqE;AACrE,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAgBhE;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,CAAC,EAAE,MAAM,EACT,UAAU,EAAE,MAAM,GACjB,OAAO,CAET;AAED,0DAA0D;AAC1D,wBAAgB,cAAc,CAC5B,CAAC,EAAE,UAAU,GAAG,IAAI,EACpB,CAAC,EAAE,UAAU,GAAG,IAAI,GACnB,OAAO,CAiBT"}
package/dist/cursor.js ADDED
@@ -0,0 +1,157 @@
1
+ /**
2
+ * The playback cursor as data: where it is, and how it moves.
3
+ *
4
+ * `ScoreCanvas` (see `docs/score-canvas.md`) hands a platform surface a
5
+ * `CursorPath` and a `CursorMotion` rather than a position per frame, so the
6
+ * surface can move the line without JavaScript on every frame. These functions
7
+ * are the reference for what a surface does with them; a native surface that
8
+ * cannot call them re-implements exactly this and nothing more.
9
+ *
10
+ * Units are **content px** (logical × zoom, scroll not subtracted) throughout,
11
+ * except `cursorVisible`, which takes the scroll offset because the gutter it
12
+ * clips against is pinned to the view.
13
+ */
14
+ import { caretPositionForTick } from './playhead.js';
15
+ import { TRACK_INFO_WIDTH } from './layout.js';
16
+ /** Uniform samples between note onsets, for bars that have not been drawn. */
17
+ const SAMPLE_TICKS = 120;
18
+ /** The first tick of every measure, and where the score ends after them. */
19
+ export function measureStartTicks(score) {
20
+ const measures = score.tracks[0]?.measures ?? [];
21
+ const starts = measures.map(measure => measure.startTick);
22
+ const last = measures[measures.length - 1];
23
+ starts.push(last
24
+ ? last.startTick + last.durationTicks * (last.multiMeasureRestCount ?? 1)
25
+ : 0);
26
+ return starts;
27
+ }
28
+ /** The system a tick is drawn in, or -1 for an empty plan. */
29
+ export function systemIndexForTick(plan, starts, tick) {
30
+ for (let index = 0; index < plan.systems.length; index += 1) {
31
+ const indices = plan.systems[index].measureIndices;
32
+ const last = indices[indices.length - 1];
33
+ if (last === undefined)
34
+ continue;
35
+ const end = starts[last + 1] ?? Number.POSITIVE_INFINITY;
36
+ if (tick < end || index === plan.systems.length - 1)
37
+ return index;
38
+ }
39
+ return -1;
40
+ }
41
+ /** The tick where a system's music ends (the next system's first tick). */
42
+ export function systemEndTick(plan, starts, systemIndex) {
43
+ const indices = plan.systems[systemIndex]?.measureIndices ?? [];
44
+ const last = indices[indices.length - 1];
45
+ return last === undefined
46
+ ? 0
47
+ : (starts[last + 1] ?? Number.POSITIVE_INFINITY);
48
+ }
49
+ /** The cursor at a tick, in content px — expectation 1 of the spec. */
50
+ export function cursorGeometryAt(plan, score, zoom, tick, positions) {
51
+ const at = caretPositionForTick(plan, score, tick, positions);
52
+ if (!at)
53
+ return null;
54
+ return {
55
+ x: at.x * zoom,
56
+ top: at.yTop * zoom,
57
+ height: (at.yBottom - at.yTop) * zoom,
58
+ };
59
+ }
60
+ /** The knots for the system `systemIndex`, in content px. */
61
+ export function cursorPathFor(plan, score, zoom, starts, systemIndex, positions) {
62
+ const system = plan.systems[systemIndex];
63
+ const indices = system?.measureIndices ?? [];
64
+ const first = indices[0];
65
+ const last = indices[indices.length - 1];
66
+ if (!system || first === undefined || last === undefined)
67
+ return null;
68
+ const startTick = starts[first] ?? 0;
69
+ // Just inside the system: its closing barline is the next system's start.
70
+ const endTick = Math.max(startTick, (starts[last + 1] ?? startTick) - 0.5);
71
+ const ticks = new Set([startTick, endTick]);
72
+ const measures = plan.trackLayouts[0]?.track.measures ?? [];
73
+ for (const measureIndex of indices) {
74
+ const from = starts[measureIndex] ?? startTick;
75
+ const to = Math.min(endTick, starts[measureIndex + 1] ?? endTick);
76
+ ticks.add(from);
77
+ for (let tick = from + SAMPLE_TICKS; tick < to; tick += SAMPLE_TICKS) {
78
+ ticks.add(tick);
79
+ }
80
+ const id = measures[measureIndex]?.id;
81
+ for (const note of (id ? positions?.get(id)?.positions : undefined) ?? []) {
82
+ if (note.tick >= startTick && note.tick <= endTick)
83
+ ticks.add(note.tick);
84
+ }
85
+ }
86
+ const sorted = [...ticks].sort((a, b) => a - b);
87
+ const xs = [];
88
+ let previous = Number.NEGATIVE_INFINITY;
89
+ for (const tick of sorted) {
90
+ const at = caretPositionForTick(plan, score, tick, positions);
91
+ previous = Math.max(previous, (at?.x ?? system.xLeft) * zoom);
92
+ xs.push(previous);
93
+ }
94
+ return {
95
+ systemIndex,
96
+ ticks: sorted,
97
+ xs,
98
+ top: system.yTop * zoom,
99
+ height: (system.yBottom - system.yTop) * zoom,
100
+ clipLeft: TRACK_INFO_WIDTH * zoom,
101
+ };
102
+ }
103
+ /** Where the playhead is at `nowMs`, per `motion`. Never before the anchor. */
104
+ export function cursorTickAt(motion, nowMs) {
105
+ const elapsed = Math.max(0, (nowMs - motion.atMs) / 1000);
106
+ return motion.tick + motion.ticksPerSecond * elapsed;
107
+ }
108
+ /** Content x for a tick along a path, clamped to the path's ends. */
109
+ export function cursorXAt(path, tick) {
110
+ const { ticks, xs } = path;
111
+ if (ticks.length === 0)
112
+ return 0;
113
+ if (tick <= ticks[0])
114
+ return xs[0];
115
+ const last = ticks.length - 1;
116
+ if (tick >= ticks[last])
117
+ return xs[last];
118
+ let lo = 0;
119
+ let hi = last;
120
+ while (hi - lo > 1) {
121
+ const mid = (lo + hi) >> 1;
122
+ if (ticks[mid] <= tick)
123
+ lo = mid;
124
+ else
125
+ hi = mid;
126
+ }
127
+ const span = ticks[hi] - ticks[lo];
128
+ if (span <= 0)
129
+ return xs[hi];
130
+ return xs[lo] + ((tick - ticks[lo]) / span) * (xs[hi] - xs[lo]);
131
+ }
132
+ /** Whether the line at content `x` shows, given the view's scroll — expectation 4. */
133
+ export function cursorVisible(path, x, scrollLeft) {
134
+ return x - scrollLeft >= path.clipLeft;
135
+ }
136
+ /** Two paths draw the same line (within half a pixel). */
137
+ export function sameCursorPath(a, b) {
138
+ if (a === b)
139
+ return true;
140
+ if (!a || !b)
141
+ return false;
142
+ if (a.systemIndex !== b.systemIndex ||
143
+ a.ticks.length !== b.ticks.length ||
144
+ a.top !== b.top ||
145
+ a.height !== b.height ||
146
+ a.clipLeft !== b.clipLeft) {
147
+ return false;
148
+ }
149
+ for (let index = 0; index < a.ticks.length; index += 1) {
150
+ if (a.ticks[index] !== b.ticks[index])
151
+ return false;
152
+ if (Math.abs(a.xs[index] - b.xs[index]) > 0.5)
153
+ return false;
154
+ }
155
+ return true;
156
+ }
157
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../src/cursor.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -21,9 +21,11 @@ export * from './drawing-context.js';
21
21
  export * from './layout.js';
22
22
  export * from './pagination.js';
23
23
  export * from './print-layout.js';
24
+ export * from './print-plan.js';
24
25
  export * from './note-colors.js';
25
26
  export * from './out-of-range.js';
26
27
  export * from './hit-test.js';
28
+ export * from './press.js';
27
29
  export * from './track-gutter.js';
28
30
  export * from './note-drag.js';
29
31
  export * from './playback-scroll.js';
@@ -34,6 +36,14 @@ export * from './measure-content.js';
34
36
  export * from './canvas-renderer.js';
35
37
  export * from './note-color.js';
36
38
  export * from './playhead.js';
39
+ /**
40
+ * The score canvas interface (`docs/score-canvas.md`): the one contract between
41
+ * the drawn score and what drives it, with the cursor as data and the playback
42
+ * binding both apps share.
43
+ */
44
+ export * from './cursor.js';
45
+ export * from './score-canvas.js';
46
+ export * from './playback-binding.js';
37
47
  export * from './percussion.js';
38
48
  export * from './icon-canvas.js';
39
49
  /**
@@ -47,4 +57,9 @@ export * from './keyboard-geometry.js';
47
57
  * where its player reads them.
48
58
  */
49
59
  export * from './keyboard-labels.js';
60
+ /**
61
+ * The two composed: a track's keys sized into a box, named, lettered for the
62
+ * pitch display, with label rows and fills — so neither app composes them.
63
+ */
64
+ export * from './keyboard-keys.js';
50
65
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AAEjC;;;GAGG;AACH,cAAc,wBAAwB,CAAC;AAEvC;;;;GAIG;AACH,cAAc,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAE9B;;;;GAIG;AACH,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AAEjC;;;GAGG;AACH,cAAc,wBAAwB,CAAC;AAEvC;;;;GAIG;AACH,cAAc,sBAAsB,CAAC;AAErC;;;GAGG;AACH,cAAc,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -21,9 +21,11 @@ export * from './drawing-context.js';
21
21
  export * from './layout.js';
22
22
  export * from './pagination.js';
23
23
  export * from './print-layout.js';
24
+ export * from './print-plan.js';
24
25
  export * from './note-colors.js';
25
26
  export * from './out-of-range.js';
26
27
  export * from './hit-test.js';
28
+ export * from './press.js';
27
29
  export * from './track-gutter.js';
28
30
  export * from './note-drag.js';
29
31
  export * from './playback-scroll.js';
@@ -34,6 +36,14 @@ export * from './measure-content.js';
34
36
  export * from './canvas-renderer.js';
35
37
  export * from './note-color.js';
36
38
  export * from './playhead.js';
39
+ /**
40
+ * The score canvas interface (`docs/score-canvas.md`): the one contract between
41
+ * the drawn score and what drives it, with the cursor as data and the playback
42
+ * binding both apps share.
43
+ */
44
+ export * from './cursor.js';
45
+ export * from './score-canvas.js';
46
+ export * from './playback-binding.js';
37
47
  export * from './percussion.js';
38
48
  export * from './icon-canvas.js';
39
49
  /**
@@ -47,4 +57,9 @@ export * from './keyboard-geometry.js';
47
57
  * where its player reads them.
48
58
  */
49
59
  export * from './keyboard-labels.js';
60
+ /**
61
+ * The two composed: a track's keys sized into a box, named, lettered for the
62
+ * pitch display, with label rows and fills — so neither app composes them.
63
+ */
64
+ export * from './keyboard-keys.js';
50
65
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AAEjC;;;GAGG;AACH,cAAc,wBAAwB,CAAC;AAEvC;;;;GAIG;AACH,cAAc,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAE9B;;;;GAIG;AACH,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AAEjC;;;GAGG;AACH,cAAc,wBAAwB,CAAC;AAEvC;;;;GAIG;AACH,cAAc,sBAAsB,CAAC;AAErC;;;GAGG;AACH,cAAc,oBAAoB,CAAC"}
@@ -17,7 +17,7 @@
17
17
  * │C │D │E │F │G │
18
18
  * └──┴──┴──┴──┴──┘
19
19
  */
20
- import { type MidiRange } from '@sudobility/music_types';
20
+ import { type MidiRange, type Track } from '@sudobility/music_types';
21
21
  /**
22
22
  * The keyboard is drawn over a caller-chosen range, not always all 88 keys:
23
23
  * the active track's instrument decides it, so a piccolo part does not present
@@ -104,7 +104,31 @@ export type PianoKey = {
104
104
  * its neighbours'.
105
105
  */
106
106
  labelTop: number;
107
+ /**
108
+ * Outside what the instrument can play.
109
+ *
110
+ * Such a key is drawn only because the track holds notes there — an import
111
+ * can bring notes a real instrument cannot reach — and it cannot be played or
112
+ * written: a person can import a sub-octave bass layer, but not type one.
113
+ * Drawn dimmed (see `KEYBOARD_OUT_OF_RANGE_WHITE` / `KEYBOARD_OUT_OF_RANGE_BLACK`) so the playable span
114
+ * stays visible inside the widened keyboard.
115
+ */
116
+ outOfRange: boolean;
107
117
  };
118
+ /**
119
+ * A white key outside the instrument's compass: pale enough to read as a key,
120
+ * grey enough to tell apart from the playable ones beside it.
121
+ */
122
+ export declare const KEYBOARD_OUT_OF_RANGE_WHITE = "#e4e4e7";
123
+ /**
124
+ * A black key outside the instrument's compass.
125
+ *
126
+ * Its own colour because the white one alone left half the dimmed span looking
127
+ * playable: an out-of-range octave read as pale whites with ordinary black keys
128
+ * between them, and the blacks are exactly as unplayable. Still dark enough to
129
+ * read as a black key, so the keyboard's pattern is not broken.
130
+ */
131
+ export declare const KEYBOARD_OUT_OF_RANGE_BLACK = "#63636b";
108
132
  /** Height of one row of key labels, and so of the gutter a keyboard needs per row. */
109
133
  export declare const LABEL_ROW_HEIGHT = 12;
110
134
  /**
@@ -128,5 +152,29 @@ export declare function keyboardWidth(whiteKeyWidth: number, range?: KeyboardRan
128
152
  * and the blacks land on top of the whites they overlap, with no z-index
129
153
  * bookkeeping.
130
154
  */
131
- export declare function computeKeys(whiteKeyWidth: number, whiteKeyHeight: number, range?: KeyboardRange, naming?: KeyNaming): PianoKey[];
155
+ export declare function computeKeys(whiteKeyWidth: number, whiteKeyHeight: number, range?: KeyboardRange, naming?: KeyNaming,
156
+ /** What the instrument can play; keys outside it are `outOfRange`. Null marks none. */
157
+ playable?: MidiRange | null): PianoKey[];
158
+ export type TrackKeyboardSpan = {
159
+ /** The keys to draw, snapped to whole white keys. */
160
+ range: KeyboardRange;
161
+ /**
162
+ * What the instrument can play, or null when its compass is not one the app
163
+ * enforces (`trackRangeIsBinding`) — in which case no key is marked, just as
164
+ * no note is flagged and no input is refused.
165
+ */
166
+ playable: MidiRange | null;
167
+ };
168
+ /**
169
+ * The keyboard for a track: its instrument's compass, widened to reach every
170
+ * note the track actually holds.
171
+ *
172
+ * Widened because a part can hold notes its instrument cannot play — an
173
+ * imported sub-octave bass layer is the usual one — and a keyboard that stopped
174
+ * at the compass lit no key at all for them. The widened keys are marked
175
+ * `outOfRange` through `playable`, which does not widen: they show what is
176
+ * there, they are not an invitation to write more of it. Both apps call this,
177
+ * so they draw the same keys and refuse the same presses.
178
+ */
179
+ export declare function trackKeyboardSpan(track: Pick<Track, 'clef' | 'midiProgram' | 'measures'>): TrackKeyboardSpan;
132
180
  //# sourceMappingURL=keyboard-geometry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"keyboard-geometry.d.ts","sourceRoot":"","sources":["../src/keyboard-geometry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;GAUG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC;AAEtC;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,aAA6B,CAAC;AAEvD,sDAAsD;AACtD,eAAO,MAAM,iBAAiB,QAAoB,CAAC;AACnD,wBAAwB;AACxB,eAAO,MAAM,iBAAiB,QAAoB,CAAC;AACnD,4EAA4E;AAC5E,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,CASnE;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,KAAK,GAAE,aAA0B,GAAG,MAAM,CAKvE;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,oDAAoD;AACpD,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,uDAAuD;IACvD,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB;;;;;;;OAOG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;OAQG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,YAAY,CAAC;AAE/C,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGhD;AAED,kIAAkI;AAClI,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,8DAA8D;AAC9D,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,aAA0B,GAChC,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,KAAK,GAAE,aAA0B,EACjC,MAAM,GAAE,SAAmB,GAC1B,QAAQ,EAAE,CAwDZ"}
1
+ {"version":3,"file":"keyboard-geometry.d.ts","sourceRoot":"","sources":["../src/keyboard-geometry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAWL,KAAK,SAAS,EACd,KAAK,KAAK,EACX,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;GAUG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC;AAEtC;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,aAA6B,CAAC;AAEvD,sDAAsD;AACtD,eAAO,MAAM,iBAAiB,QAAoB,CAAC;AACnD,wBAAwB;AACxB,eAAO,MAAM,iBAAiB,QAAoB,CAAC;AACnD,4EAA4E;AAC5E,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,CASnE;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,KAAK,GAAE,aAA0B,GAAG,MAAM,CAKvE;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,oDAAoD;AACpD,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,uDAAuD;IACvD,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB;;;;;;;OAOG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;OAQG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;;OAQG;IACH,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,2BAA2B,YAAY,CAAC;AAErD;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,YAAY,CAAC;AAErD,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,YAAY,CAAC;AAE/C,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGhD;AAED,kIAAkI;AAClI,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,8DAA8D;AAC9D,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,aAA0B,GAChC,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,KAAK,GAAE,aAA0B,EACjC,MAAM,GAAE,SAAmB;AAC3B,uFAAuF;AACvF,QAAQ,GAAE,SAAS,GAAG,IAAW,GAChC,QAAQ,EAAE,CA2DZ;AAMD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,qDAAqD;IACrD,KAAK,EAAE,aAAa,CAAC;IACrB;;;;OAIG;IACH,QAAQ,EAAE,SAAS,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,UAAU,CAAC,GACtD,iBAAiB,CA8BnB"}
@@ -17,7 +17,7 @@
17
17
  * │C │D │E │F │G │
18
18
  * └──┴──┴──┴──┴──┘
19
19
  */
20
- import { FULL_KEYBOARD, gmPercussion, gmPercussionName, midiToPitch, pitchToString, } from '@sudobility/music_types';
20
+ import { FULL_KEYBOARD, gmPercussion, gmPercussionName, isNoteEvent, midiIsInRange, midiToPitch, pitchToMidi, pitchToString, trackKeyboardRange, trackRangeIsBinding, } from '@sudobility/music_types';
21
21
  /**
22
22
  * The 88-key piano — `FULL_KEYBOARD` **by identity**, not a second pair of
23
23
  * numbers that agrees with it today.
@@ -76,6 +76,20 @@ export const MIN_WHITE_KEY_WIDTH = 14;
76
76
  /** Black-key size, as a fraction of a white key. */
77
77
  export const BLACK_KEY_WIDTH_RATIO = 0.6;
78
78
  export const BLACK_KEY_HEIGHT_RATIO = 0.62;
79
+ /**
80
+ * A white key outside the instrument's compass: pale enough to read as a key,
81
+ * grey enough to tell apart from the playable ones beside it.
82
+ */
83
+ export const KEYBOARD_OUT_OF_RANGE_WHITE = '#e4e4e7';
84
+ /**
85
+ * A black key outside the instrument's compass.
86
+ *
87
+ * Its own colour because the white one alone left half the dimmed span looking
88
+ * playable: an out-of-range octave read as pale whites with ordinary black keys
89
+ * between them, and the blacks are exactly as unplayable. Still dark enough to
90
+ * read as a black key, so the keyboard's pattern is not broken.
91
+ */
92
+ export const KEYBOARD_OUT_OF_RANGE_BLACK = '#63636b';
79
93
  /** Height of one row of key labels, and so of the gutter a keyboard needs per row. */
80
94
  export const LABEL_ROW_HEIGHT = 12;
81
95
  /** True for the five black keys per octave (pitch classes 1, 3, 6, 8, 10). */
@@ -98,7 +112,9 @@ export function keyboardWidth(whiteKeyWidth, range = FULL_RANGE) {
98
112
  * and the blacks land on top of the whites they overlap, with no z-index
99
113
  * bookkeeping.
100
114
  */
101
- export function computeKeys(whiteKeyWidth, whiteKeyHeight, range = FULL_RANGE, naming = 'pitch') {
115
+ export function computeKeys(whiteKeyWidth, whiteKeyHeight, range = FULL_RANGE, naming = 'pitch',
116
+ /** What the instrument can play; keys outside it are `outOfRange`. Null marks none. */
117
+ playable = null) {
102
118
  const whites = [];
103
119
  const blacks = [];
104
120
  const blackWidth = whiteKeyWidth * BLACK_KEY_WIDTH_RATIO;
@@ -120,6 +136,7 @@ export function computeKeys(whiteKeyWidth, whiteKeyHeight, range = FULL_RANGE, n
120
136
  ? noteLabel(midi)
121
137
  : null;
122
138
  const name = drums ? gmPercussionName(midi) : noteLabel(midi);
139
+ const outOfRange = playable !== null && !midiIsInRange(midi, playable);
123
140
  if (isBlackKey(midi)) {
124
141
  blacks.push({
125
142
  midi,
@@ -134,6 +151,7 @@ export function computeKeys(whiteKeyWidth, whiteKeyHeight, range = FULL_RANGE, n
134
151
  name,
135
152
  // Second row: clear of the white keys and of their labels.
136
153
  labelTop: whiteKeyHeight + 1 + LABEL_ROW_HEIGHT,
154
+ outOfRange,
137
155
  });
138
156
  }
139
157
  else {
@@ -146,10 +164,61 @@ export function computeKeys(whiteKeyWidth, whiteKeyHeight, range = FULL_RANGE, n
146
164
  label,
147
165
  name,
148
166
  labelTop: whiteKeyHeight + 1,
167
+ outOfRange,
149
168
  });
150
169
  whiteIndex += 1;
151
170
  }
152
171
  }
153
172
  return [...whites, ...blacks];
154
173
  }
174
+ /** The whole MIDI note range, which an imported part may use past the piano's ends. */
175
+ const MIDI_MIN = 0;
176
+ const MIDI_MAX = 127;
177
+ /**
178
+ * The keyboard for a track: its instrument's compass, widened to reach every
179
+ * note the track actually holds.
180
+ *
181
+ * Widened because a part can hold notes its instrument cannot play — an
182
+ * imported sub-octave bass layer is the usual one — and a keyboard that stopped
183
+ * at the compass lit no key at all for them. The widened keys are marked
184
+ * `outOfRange` through `playable`, which does not widen: they show what is
185
+ * there, they are not an invitation to write more of it. Both apps call this,
186
+ * so they draw the same keys and refuse the same presses.
187
+ */
188
+ export function trackKeyboardSpan(track) {
189
+ const compass = trackKeyboardRange(track);
190
+ let min = compass.min;
191
+ let max = compass.max;
192
+ for (const measure of track.measures) {
193
+ for (const voice of measure.voices) {
194
+ for (const event of voice.events) {
195
+ if (!isNoteEvent(event))
196
+ continue;
197
+ const midi = pitchToMidi(event.pitch);
198
+ if (midi < min)
199
+ min = midi;
200
+ if (midi > max)
201
+ max = midi;
202
+ }
203
+ }
204
+ }
205
+ // The compass itself keeps the 88-key snapping. Past it, the keyboard follows
206
+ // the notes as far as MIDI goes: an imported sub-bass reaches below A0, and
207
+ // stopping at the piano's end would light nothing for exactly those notes.
208
+ let { min: from, max: to } = snapToWhiteKeys(compass);
209
+ if (min < from) {
210
+ from = min;
211
+ while (from > MIDI_MIN && isBlackKey(from))
212
+ from -= 1;
213
+ }
214
+ if (max > to) {
215
+ to = max;
216
+ while (to < MIDI_MAX && isBlackKey(to))
217
+ to += 1;
218
+ }
219
+ return {
220
+ range: { min: from, max: to },
221
+ playable: trackRangeIsBinding(track) ? compass : null,
222
+ };
223
+ }
155
224
  //# sourceMappingURL=keyboard-geometry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"keyboard-geometry.js","sourceRoot":"","sources":["../src/keyboard-geometry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"keyboard-geometry.js","sourceRoot":"","sources":["../src/keyboard-geometry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EACL,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,WAAW,EACX,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,mBAAmB,GAGpB,MAAM,yBAAyB,CAAC;AAejC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAkB,aAAa,CAAC;AAEvD,sDAAsD;AACtD,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAC,GAAG,CAAC;AACnD,wBAAwB;AACxB,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAC,GAAG,CAAC;AACnD,4EAA4E;AAC5E,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAElC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,KAAoB;IAClD,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,OAAO,GAAG,GAAG,aAAa,CAAC,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,GAAG,IAAI,CAAC,CAAC;IAC5D,OAAO,GAAG,GAAG,aAAa,CAAC,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,GAAG,IAAI,CAAC,CAAC;IAC5D,4EAA4E;IAC5E,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,GAAG;QAAE,GAAG,IAAI,CAAC,CAAC;IAC9C,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,GAAG;QAAE,GAAG,IAAI,CAAC,CAAC;IAC9C,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACtB,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,aAAa,CAAC,QAAuB,UAAU;IAC7D,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,IAAI,KAAK,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC;QACrD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,KAAK,IAAI,CAAC,CAAC;IACpC,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,oDAAoD;AACpD,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AACzC,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAiD3C;;;GAGG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,SAAS,CAAC;AAErD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,SAAS,CAAC;AAErD,sFAAsF;AACtF,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAWnC,8EAA8E;AAC9E,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;IAC3C,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAC/C,CAAC;AAED,kIAAkI;AAClI,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,aAAa,CAC3B,aAAqB,EACrB,QAAuB,UAAU;IAEjC,OAAO,aAAa,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC;AAC9C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CACzB,aAAqB,EACrB,cAAsB,EACtB,QAAuB,UAAU,EACjC,SAAoB,OAAO;AAC3B,uFAAuF;AACvF,WAA6B,IAAI;IAEjC,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,MAAM,UAAU,GAAG,aAAa,GAAG,qBAAqB,CAAC;IACzD,MAAM,WAAW,GAAG,cAAc,GAAG,sBAAsB,CAAC;IAC5D,MAAM,KAAK,GAAG,MAAM,KAAK,YAAY,CAAC;IAEtC,2EAA2E;IAC3E,uEAAuE;IACvE,6CAA6C;IAC7C,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,IAAI,KAAK,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QACxD,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,UAAU,GACd,KAAK,CAAC,UAAU,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;QACvE,2EAA2E;QAC3E,2EAA2E;QAC3E,4CAA4C;QAC5C,MAAM,KAAK,GAAG,KAAK;YACjB,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC;YACrC,CAAC,CAAC,UAAU;gBACV,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC;gBACjB,CAAC,CAAC,IAAI,CAAC;QACX,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9D,MAAM,UAAU,GAAG,QAAQ,KAAK,IAAI,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAEvE,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI;gBACJ,OAAO,EAAE,IAAI;gBACb,CAAC,EAAE,UAAU,GAAG,aAAa,GAAG,UAAU,GAAG,CAAC;gBAC9C,KAAK,EAAE,UAAU;gBACjB,MAAM,EAAE,WAAW;gBACnB,sEAAsE;gBACtE,uEAAuE;gBACvE,8CAA8C;gBAC9C,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;gBAC3B,IAAI;gBACJ,2DAA2D;gBAC3D,QAAQ,EAAE,cAAc,GAAG,CAAC,GAAG,gBAAgB;gBAC/C,UAAU;aACX,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI;gBACJ,OAAO,EAAE,KAAK;gBACd,CAAC,EAAE,UAAU,GAAG,aAAa;gBAC7B,KAAK,EAAE,aAAa;gBACpB,MAAM,EAAE,cAAc;gBACtB,KAAK;gBACL,IAAI;gBACJ,QAAQ,EAAE,cAAc,GAAG,CAAC;gBAC5B,UAAU;aACX,CAAC,CAAC;YACH,UAAU,IAAI,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC;AAChC,CAAC;AAED,uFAAuF;AACvF,MAAM,QAAQ,GAAG,CAAC,CAAC;AACnB,MAAM,QAAQ,GAAG,GAAG,CAAC;AAarB;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAuD;IAEvD,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC1C,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACtB,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACtB,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACrC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;oBAAE,SAAS;gBAClC,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACtC,IAAI,IAAI,GAAG,GAAG;oBAAE,GAAG,GAAG,IAAI,CAAC;gBAC3B,IAAI,IAAI,GAAG,GAAG;oBAAE,GAAG,GAAG,IAAI,CAAC;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;IACD,8EAA8E;IAC9E,4EAA4E;IAC5E,2EAA2E;IAC3E,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACtD,IAAI,GAAG,GAAG,IAAI,EAAE,CAAC;QACf,IAAI,GAAG,GAAG,CAAC;QACX,OAAO,IAAI,GAAG,QAAQ,IAAI,UAAU,CAAC,IAAI,CAAC;YAAE,IAAI,IAAI,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,GAAG,GAAG,EAAE,EAAE,CAAC;QACb,EAAE,GAAG,GAAG,CAAC;QACT,OAAO,EAAE,GAAG,QAAQ,IAAI,UAAU,CAAC,EAAE,CAAC;YAAE,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC;IACD,OAAO;QACL,KAAK,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE;QAC7B,QAAQ,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;KACtD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * The keyboard for a track, in one call: which keys, how big, what they are
3
+ * called, and where their labels go.
4
+ *
5
+ * Each of those was already a shared function — `trackKeyboardSpan`,
6
+ * `computeKeys`, `keyboardLabelSemitones`/`relabelKeys` — but the *composition*
7
+ * was written out in both apps, and the two compositions disagreed: the native
8
+ * app never relabelled a transposing part in written pitch, laid its labels
9
+ * inside the keys rather than in a gutter, recomputed black-key heights from
10
+ * the ratio, and fitted the width where the web scrolled. A keyboard is the
11
+ * sum of those decisions, so the sum lives here too.
12
+ *
13
+ * Web's rules are the reference: the height handed in is the whole panel, the
14
+ * label gutter comes out of it, and a drum kit's gutter is two rows because its
15
+ * black keys are named as well.
16
+ */
17
+ import type { PitchDisplay, Track } from '@sudobility/music_types';
18
+ import { type KeyNaming, type KeyboardRange, type PianoKey } from './keyboard-geometry.js';
19
+ import type { MidiRange } from '@sudobility/music_types';
20
+ import type { RenderTheme } from './types.js';
21
+ /**
22
+ * Room below the keys for a pitched keyboard's one row of C/F labels.
23
+ *
24
+ * A little more than `LABEL_ROW_HEIGHT`, so the label clears the key's bottom
25
+ * edge instead of touching it.
26
+ */
27
+ export declare const PITCH_LABEL_GUTTER = 14;
28
+ /** A playable white key. */
29
+ export declare const KEYBOARD_WHITE = "#fbfbfd";
30
+ /** A playable black key. */
31
+ export declare const KEYBOARD_BLACK = "#1f1f23";
32
+ /**
33
+ * How the keys meet the width they are given.
34
+ *
35
+ * - `'scroll'` (web, the default): keys fill the width but never shrink below
36
+ * `MIN_WHITE_KEY_WIDTH`; past that the keyboard is wider than its panel and
37
+ * the panel scrolls. On a desktop a narrow window should not turn 88 keys
38
+ * into slivers nobody can click.
39
+ * - `'width'` (native): the whole range always fits. On a phone a keyboard you
40
+ * have to scroll is one you cannot play a two-handed chord on, and a finger
41
+ * presses a sliver more reliably than a pointer does.
42
+ */
43
+ export type KeyboardFit = 'scroll' | 'width';
44
+ export type KeyboardKeysOptions = {
45
+ /** Available width. */
46
+ width: number;
47
+ /** The whole panel's height, label gutter included. */
48
+ height: number;
49
+ /** The active track; null or absent draws the whole piano with nothing marked. */
50
+ track: Pick<Track, 'clef' | 'midiProgram' | 'measures'> | null | undefined;
51
+ /** What the staff shows; moves the lettering of a transposing part. Default concert. */
52
+ pitchDisplay?: PitchDisplay;
53
+ fit?: KeyboardFit;
54
+ };
55
+ export type KeyboardKeys = {
56
+ /** The keys drawn, snapped to whole white keys. */
57
+ range: KeyboardRange;
58
+ /** What the instrument can play, or null when nothing is marked. */
59
+ playable: MidiRange | null;
60
+ naming: KeyNaming;
61
+ /** Whites first, then blacks — render in order and the blacks land on top. Each carries its own height and label position. */
62
+ keys: PianoKey[];
63
+ /** Height reserved under the white keys for labels. */
64
+ labelGutter: number;
65
+ /** A white key's height: `height − labelGutter`. */
66
+ keyHeight: number;
67
+ whiteKeyWidth: number;
68
+ /** The keyboard's total width — of `range`, not of the 88-key piano. */
69
+ width: number;
70
+ };
71
+ /**
72
+ * Everything a keyboard view needs to draw `track`'s keys into a box.
73
+ *
74
+ * Cheap enough to call per render (it walks the track's notes once) but it
75
+ * returns fresh arrays, so memoize on its inputs where the keys are reconciled.
76
+ */
77
+ export declare function keyboardKeys({ width, height, track, pitchDisplay, fit, }: KeyboardKeysOptions): KeyboardKeys;
78
+ /** What is happening to a key right now. */
79
+ export type KeyState = {
80
+ /** Sounding in playback, or held down. */
81
+ lit: boolean;
82
+ /** A pitch of the one selected chord. */
83
+ selected: boolean;
84
+ };
85
+ /**
86
+ * The fill for `key`.
87
+ *
88
+ * Sounding wins over selected — it is the more transient signal, and a key
89
+ * that is both should show what is happening now — and either wins over the
90
+ * out-of-range dimming, which is a standing fact about the instrument.
91
+ */
92
+ export declare function keyboardKeyFill(key: Pick<PianoKey, 'isBlack' | 'outOfRange'>, state: KeyState, theme: RenderTheme): string;
93
+ //# sourceMappingURL=keyboard-keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard-keys.d.ts","sourceRoot":"","sources":["../src/keyboard-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAWL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,QAAQ,EACd,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,4BAA4B;AAC5B,eAAO,MAAM,cAAc,YAAY,CAAC;AACxC,4BAA4B;AAC5B,eAAO,MAAM,cAAc,YAAY,CAAC;AAExC;;;;;;;;;;GAUG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE7C,MAAM,MAAM,mBAAmB,GAAG;IAChC,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,UAAU,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAC3E,wFAAwF;IACxF,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,GAAG,CAAC,EAAE,WAAW,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,mDAAmD;IACnD,KAAK,EAAE,aAAa,CAAC;IACrB,oEAAoE;IACpE,QAAQ,EAAE,SAAS,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,SAAS,CAAC;IAClB,8HAA8H;IAC9H,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,MAAM,EACN,KAAK,EACL,YAAwB,EACxB,GAAc,GACf,EAAE,mBAAmB,GAAG,YAAY,CAiCpC;AAED,4CAA4C;AAC5C,MAAM,MAAM,QAAQ,GAAG;IACrB,0CAA0C;IAC1C,GAAG,EAAE,OAAO,CAAC;IACb,yCAAyC;IACzC,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,GAAG,YAAY,CAAC,EAC7C,KAAK,EAAE,QAAQ,EACf,KAAK,EAAE,WAAW,GACjB,MAAM,CAMR"}