@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.
- package/dist/cursor.d.ts +60 -0
- package/dist/cursor.d.ts.map +1 -0
- package/dist/cursor.js +157 -0
- package/dist/cursor.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/keyboard-geometry.d.ts +50 -2
- package/dist/keyboard-geometry.d.ts.map +1 -1
- package/dist/keyboard-geometry.js +71 -2
- package/dist/keyboard-geometry.js.map +1 -1
- package/dist/keyboard-keys.d.ts +93 -0
- package/dist/keyboard-keys.d.ts.map +1 -0
- package/dist/keyboard-keys.js +60 -0
- package/dist/keyboard-keys.js.map +1 -0
- package/dist/pagination.d.ts +6 -2
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +4 -0
- package/dist/pagination.js.map +1 -1
- package/dist/playback-binding.d.ts +57 -0
- package/dist/playback-binding.d.ts.map +1 -0
- package/dist/playback-binding.js +110 -0
- package/dist/playback-binding.js.map +1 -0
- package/dist/playhead.d.ts.map +1 -1
- package/dist/playhead.js +22 -3
- package/dist/playhead.js.map +1 -1
- package/dist/press.d.ts +66 -0
- package/dist/press.d.ts.map +1 -0
- package/dist/press.js +64 -0
- package/dist/press.js.map +1 -0
- package/dist/print-plan.d.ts +64 -0
- package/dist/print-plan.d.ts.map +1 -0
- package/dist/print-plan.js +101 -0
- package/dist/print-plan.js.map +1 -0
- package/dist/render-theme.d.ts.map +1 -1
- package/dist/render-theme.js +11 -2
- package/dist/render-theme.js.map +1 -1
- package/dist/score-canvas.d.ts +225 -0
- package/dist/score-canvas.d.ts.map +1 -0
- package/dist/score-canvas.js +541 -0
- package/dist/score-canvas.js.map +1 -0
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { FULL_RANGE, KEYBOARD_OUT_OF_RANGE_BLACK, KEYBOARD_OUT_OF_RANGE_WHITE, LABEL_ROW_HEIGHT, MIN_WHITE_KEY_WIDTH, computeKeys, keyboardWidth, snapToWhiteKeys, trackKeyboardSpan, whiteKeyCount, } from './keyboard-geometry.js';
|
|
2
|
+
import { keyboardLabelSemitones, relabelKeys } from './keyboard-labels.js';
|
|
3
|
+
/**
|
|
4
|
+
* Room below the keys for a pitched keyboard's one row of C/F labels.
|
|
5
|
+
*
|
|
6
|
+
* A little more than `LABEL_ROW_HEIGHT`, so the label clears the key's bottom
|
|
7
|
+
* edge instead of touching it.
|
|
8
|
+
*/
|
|
9
|
+
export const PITCH_LABEL_GUTTER = 14;
|
|
10
|
+
/** A playable white key. */
|
|
11
|
+
export const KEYBOARD_WHITE = '#fbfbfd';
|
|
12
|
+
/** A playable black key. */
|
|
13
|
+
export const KEYBOARD_BLACK = '#1f1f23';
|
|
14
|
+
/**
|
|
15
|
+
* Everything a keyboard view needs to draw `track`'s keys into a box.
|
|
16
|
+
*
|
|
17
|
+
* Cheap enough to call per render (it walks the track's notes once) but it
|
|
18
|
+
* returns fresh arrays, so memoize on its inputs where the keys are reconciled.
|
|
19
|
+
*/
|
|
20
|
+
export function keyboardKeys({ width, height, track, pitchDisplay = 'concert', fit = 'scroll', }) {
|
|
21
|
+
const span = track
|
|
22
|
+
? trackKeyboardSpan(track)
|
|
23
|
+
: { range: snapToWhiteKeys(FULL_RANGE), playable: null };
|
|
24
|
+
const { range, playable } = span;
|
|
25
|
+
// On a percussion track `midiProgram` is a kit and every key is a drum.
|
|
26
|
+
const naming = track?.clef === 'percussion' ? 'percussion' : 'pitch';
|
|
27
|
+
// A kit names its black keys too, on a second row under the first.
|
|
28
|
+
const labelGutter = naming === 'percussion' ? LABEL_ROW_HEIGHT * 2 : PITCH_LABEL_GUTTER;
|
|
29
|
+
const keyHeight = Math.max(1, height - labelGutter);
|
|
30
|
+
const fitted = width / Math.max(1, whiteKeyCount(range));
|
|
31
|
+
const whiteKeyWidth = fit === 'width' ? fitted : Math.max(MIN_WHITE_KEY_WIDTH, fitted);
|
|
32
|
+
const keys = relabelKeys(computeKeys(whiteKeyWidth, keyHeight, range, naming, playable), keyboardLabelSemitones(track, pitchDisplay));
|
|
33
|
+
return {
|
|
34
|
+
range,
|
|
35
|
+
playable,
|
|
36
|
+
naming,
|
|
37
|
+
keys,
|
|
38
|
+
labelGutter,
|
|
39
|
+
keyHeight,
|
|
40
|
+
whiteKeyWidth,
|
|
41
|
+
width: keyboardWidth(whiteKeyWidth, range),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The fill for `key`.
|
|
46
|
+
*
|
|
47
|
+
* Sounding wins over selected — it is the more transient signal, and a key
|
|
48
|
+
* that is both should show what is happening now — and either wins over the
|
|
49
|
+
* out-of-range dimming, which is a standing fact about the instrument.
|
|
50
|
+
*/
|
|
51
|
+
export function keyboardKeyFill(key, state, theme) {
|
|
52
|
+
if (state.lit)
|
|
53
|
+
return theme.keyboardLit ?? theme.notePlaying;
|
|
54
|
+
if (state.selected)
|
|
55
|
+
return theme.keyboardSelected ?? theme.noteSelected;
|
|
56
|
+
if (key.isBlack)
|
|
57
|
+
return key.outOfRange ? KEYBOARD_OUT_OF_RANGE_BLACK : KEYBOARD_BLACK;
|
|
58
|
+
return key.outOfRange ? KEYBOARD_OUT_OF_RANGE_WHITE : KEYBOARD_WHITE;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=keyboard-keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard-keys.js","sourceRoot":"","sources":["../src/keyboard-keys.ts"],"names":[],"mappings":"AAiBA,OAAO,EACL,UAAU,EACV,2BAA2B,EAC3B,2BAA2B,EAC3B,gBAAgB,EAChB,mBAAmB,EACnB,WAAW,EACX,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,aAAa,GAId,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAI3E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAErC,4BAA4B;AAC5B,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAC;AACxC,4BAA4B;AAC5B,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAC;AA4CxC;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,EAC3B,KAAK,EACL,MAAM,EACN,KAAK,EACL,YAAY,GAAG,SAAS,EACxB,GAAG,GAAG,QAAQ,GACM;IACpB,MAAM,IAAI,GAAG,KAAK;QAChB,CAAC,CAAC,iBAAiB,CAAC,KAAK,CAAC;QAC1B,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3D,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAEjC,wEAAwE;IACxE,MAAM,MAAM,GACV,KAAK,EAAE,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;IACxD,mEAAmE;IACnE,MAAM,WAAW,GACf,MAAM,KAAK,YAAY,CAAC,CAAC,CAAC,gBAAgB,GAAG,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC;IACtE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC;IAEpD,MAAM,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IACzD,MAAM,aAAa,GACjB,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;IAEnE,MAAM,IAAI,GAAG,WAAW,CACtB,WAAW,CAAC,aAAa,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAC9D,sBAAsB,CAAC,KAAK,EAAE,YAAY,CAAC,CAC5C,CAAC;IAEF,OAAO;QACL,KAAK;QACL,QAAQ;QACR,MAAM;QACN,IAAI;QACJ,WAAW;QACX,SAAS;QACT,aAAa;QACb,KAAK,EAAE,aAAa,CAAC,aAAa,EAAE,KAAK,CAAC;KAC3C,CAAC;AACJ,CAAC;AAUD;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,GAA6C,EAC7C,KAAe,EACf,KAAkB;IAElB,IAAI,KAAK,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,CAAC;IAC7D,IAAI,KAAK,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC,gBAAgB,IAAI,KAAK,CAAC,YAAY,CAAC;IACxE,IAAI,GAAG,CAAC,OAAO;QACb,OAAO,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,cAAc,CAAC;IACvE,OAAO,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,cAAc,CAAC;AACvE,CAAC"}
|
package/dist/pagination.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { Track } from '@sudobility/music_types';
|
|
2
2
|
import type { LayoutPlan } from './layout.js';
|
|
3
|
-
|
|
4
|
-
export
|
|
3
|
+
/** The papers a score prints on, in the order a picker offers them. */
|
|
4
|
+
export declare const PAPER_SIZES: readonly ["a4", "letter", "legal"];
|
|
5
|
+
export type PaperSize = (typeof PAPER_SIZES)[number];
|
|
6
|
+
/** Portrait first: it is the default and what almost every part is. */
|
|
7
|
+
export declare const PAPER_ORIENTATIONS: readonly ["portrait", "landscape"];
|
|
8
|
+
export type PaperOrientation = (typeof PAPER_ORIENTATIONS)[number];
|
|
5
9
|
/** Portrait dimensions in millimetres; landscape swaps them. */
|
|
6
10
|
export declare const PAPER_DIMENSIONS_MM: Record<PaperSize, {
|
|
7
11
|
width: number;
|
package/dist/pagination.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAW,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,aAAa,CAAC;AAE5D,MAAM,MAAM,SAAS,GAAG,
|
|
1
|
+
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAW,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,aAAa,CAAC;AAE5D,uEAAuE;AACvE,eAAO,MAAM,WAAW,oCAAqC,CAAC;AAC9D,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,uEAAuE;AACvE,eAAO,MAAM,kBAAkB,oCAAqC,CAAC;AACrE,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnE,gEAAgE;AAChE,eAAO,MAAM,mBAAmB,EAAE,MAAM,CACtC,SAAS,EACT;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAKlC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,gBAAgB,EAC7B,YAAY,EAAE,MAAM,EACpB,QAAQ,GAAE,MAAuB,GAChC,MAAM,CAOR;AAED,2EAA2E;AAC3E,MAAM,MAAM,SAAS,GAAG;IAAE,aAAa,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAOpD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,IAAI,CAAC;AA6B/B;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,KAAK,EACZ,eAAe,EAAE,MAAM,GACtB,MAAM,CAQR;AA8BD;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,KAAK,GAChB,SAAS,EAAE,CA+Bb"}
|
package/dist/pagination.js
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
* of a phrase costs a real player a hand.
|
|
11
11
|
*/
|
|
12
12
|
import { isSilentMeasure } from '@sudobility/music_types';
|
|
13
|
+
/** The papers a score prints on, in the order a picker offers them. */
|
|
14
|
+
export const PAPER_SIZES = ['a4', 'letter', 'legal'];
|
|
15
|
+
/** Portrait first: it is the default and what almost every part is. */
|
|
16
|
+
export const PAPER_ORIENTATIONS = ['portrait', 'landscape'];
|
|
13
17
|
/** Portrait dimensions in millimetres; landscape swaps them. */
|
|
14
18
|
export const PAPER_DIMENSIONS_MM = {
|
|
15
19
|
a4: { width: 210, height: 297 },
|
package/dist/pagination.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playback → the score canvas, in one place both apps share.
|
|
3
|
+
*
|
|
4
|
+
* Watches the shared playhead (`IMusicPosition`) and the player's sounding
|
|
5
|
+
* notes and drives a `ScoreCanvas` with them: the cursor, the lit notes, and
|
|
6
|
+
* following the music. The rules below used to live separately in the web
|
|
7
|
+
* app's caret and in the native app's, and disagreed.
|
|
8
|
+
*
|
|
9
|
+
* **The cursor is described, not written per frame.** While playing, the
|
|
10
|
+
* binding hands the canvas a motion (a tick, when it was true, and a rate) and
|
|
11
|
+
* re-describes it only when the reports disagree with it by more than
|
|
12
|
+
* `DRIFT_SECONDS`, or the tempo has measurably changed. Between those the
|
|
13
|
+
* surface moves the line on its own.
|
|
14
|
+
*/
|
|
15
|
+
import type { IMusicPosition, SoundingNote } from '@sudobility/music_types';
|
|
16
|
+
import type { CursorMotion } from './cursor.js';
|
|
17
|
+
/** Re-describe the motion when the line would be further than this from the reports. */
|
|
18
|
+
export declare const DRIFT_SECONDS = 0.04;
|
|
19
|
+
/** What the binding needs from the canvas — `ScoreCanvas` satisfies it. */
|
|
20
|
+
export type PlaybackCanvasTarget = {
|
|
21
|
+
setCursorTick(tick: number): void;
|
|
22
|
+
animateCursor(motion: CursorMotion): void;
|
|
23
|
+
setPlayingNotes(notes: readonly SoundingNote[]): void;
|
|
24
|
+
scrollToFollow(tick: number): void;
|
|
25
|
+
measureIndexAt(tick: number): number | null;
|
|
26
|
+
writtenTicksPerSecond(tick: number): number;
|
|
27
|
+
/** How long lit notes take to draw here; see `ScoreCanvas.highlightLatencySeconds`. */
|
|
28
|
+
subscribeHighlightLatency?(listener: (seconds: number) => void): () => void;
|
|
29
|
+
};
|
|
30
|
+
export type PlaybackBindingOptions = {
|
|
31
|
+
position: IMusicPosition;
|
|
32
|
+
/** The player's sounding-notes subscription; returns an unsubscribe. */
|
|
33
|
+
onSounding?: (listener: (notes: readonly SoundingNote[]) => void) => () => void;
|
|
34
|
+
/**
|
|
35
|
+
* Run `work` once the current burst of notifications is over — a microtask
|
|
36
|
+
* (`queueMicrotask`) on every host.
|
|
37
|
+
*
|
|
38
|
+
* A single engine call can notify several times, and only the end state means
|
|
39
|
+
* anything: stopping homes the position to 0 while it still reports playing,
|
|
40
|
+
* then reports stopped. Decided notification by notification, that homing
|
|
41
|
+
* reads as the music moving to bar 1 and the view scrolls there; decided once
|
|
42
|
+
* the call is over, it is a stop, and stopping is not a navigation.
|
|
43
|
+
*/
|
|
44
|
+
defer: (work: () => void) => void;
|
|
45
|
+
/**
|
|
46
|
+
* The player's `setSoundingRenderDelay`: told how long lit notes take to
|
|
47
|
+
* draw on this host, so it publishes them that far ahead and they land with
|
|
48
|
+
* the sound rather than behind it.
|
|
49
|
+
*/
|
|
50
|
+
setSoundingRenderDelay?: (seconds: number) => void;
|
|
51
|
+
/** Whether the view follows the playhead. Default true. */
|
|
52
|
+
follow?: boolean;
|
|
53
|
+
/** `performance.now()` — passed in, since this package names no host global. */
|
|
54
|
+
now: () => number;
|
|
55
|
+
};
|
|
56
|
+
export declare function bindPlaybackToCanvas(canvas: PlaybackCanvasTarget, { position, defer, onSounding, setSoundingRenderDelay, follow, now, }: PlaybackBindingOptions): () => void;
|
|
57
|
+
//# sourceMappingURL=playback-binding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playback-binding.d.ts","sourceRoot":"","sources":["../src/playback-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wFAAwF;AACxF,eAAO,MAAM,aAAa,OAAO,CAAC;AAQlC,2EAA2E;AAC3E,MAAM,MAAM,oBAAoB,GAAG;IACjC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC1C,eAAe,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI,CAAC;IACtD,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5C,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5C,uFAAuF;IACvF,yBAAyB,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAC7E,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,cAAc,CAAC;IACzB,wEAAwE;IACxE,UAAU,CAAC,EAAE,CACX,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,KAAK,IAAI,KAC/C,MAAM,IAAI,CAAC;IAChB;;;;;;;;;OASG;IACH,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IAClC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,2DAA2D;IAC3D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gFAAgF;IAChF,GAAG,EAAE,MAAM,MAAM,CAAC;CACnB,CAAC;AAEF,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,oBAAoB,EAC5B,EACE,QAAQ,EACR,KAAK,EACL,UAAU,EACV,sBAAsB,EACtB,MAAa,EACb,GAAG,GACJ,EAAE,sBAAsB,GACxB,MAAM,IAAI,CA6GZ"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/** Re-describe the motion when the line would be further than this from the reports. */
|
|
2
|
+
export const DRIFT_SECONDS = 0.04;
|
|
3
|
+
/** The shortest span a rate measurement is trusted over. */
|
|
4
|
+
const RATE_WINDOW_SECONDS = 0.25;
|
|
5
|
+
/** A tempo change smaller than this is measurement noise… */
|
|
6
|
+
const RATE_CHANGE = 0.05;
|
|
7
|
+
/** …and it is judged only over a span long enough to tell. */
|
|
8
|
+
const RATE_CHANGE_WINDOW_SECONDS = 1;
|
|
9
|
+
export function bindPlaybackToCanvas(canvas, { position, defer, onSounding, setSoundingRenderDelay, follow = true, now, }) {
|
|
10
|
+
let motion = null;
|
|
11
|
+
let wasPlaying = position.isPlaying;
|
|
12
|
+
let rateWindow = null;
|
|
13
|
+
let rate = 0;
|
|
14
|
+
let followedMeasure = null;
|
|
15
|
+
const describe = (next) => {
|
|
16
|
+
motion = next;
|
|
17
|
+
canvas.animateCursor(next);
|
|
18
|
+
};
|
|
19
|
+
const followTo = (tick) => {
|
|
20
|
+
if (!follow)
|
|
21
|
+
return;
|
|
22
|
+
const measure = canvas.measureIndexAt(tick);
|
|
23
|
+
// 14. Once per bar.
|
|
24
|
+
if (measure === null || measure === followedMeasure)
|
|
25
|
+
return;
|
|
26
|
+
followedMeasure = measure;
|
|
27
|
+
canvas.scrollToFollow(tick);
|
|
28
|
+
};
|
|
29
|
+
const wake = () => {
|
|
30
|
+
const tick = position.tick;
|
|
31
|
+
const at = now();
|
|
32
|
+
const playing = position.isPlaying;
|
|
33
|
+
const transportChanged = playing !== wasPlaying;
|
|
34
|
+
wasPlaying = playing;
|
|
35
|
+
if (!playing) {
|
|
36
|
+
rateWindow = null;
|
|
37
|
+
if (!motion || motion.ticksPerSecond !== 0 || motion.tick !== tick) {
|
|
38
|
+
motion = { tick, atMs: at, ticksPerSecond: 0 };
|
|
39
|
+
canvas.setCursorTick(tick);
|
|
40
|
+
}
|
|
41
|
+
// 15. Stopping or pausing is not a navigation; a seek while stopped is.
|
|
42
|
+
if (transportChanged)
|
|
43
|
+
followedMeasure = null;
|
|
44
|
+
else
|
|
45
|
+
followTo(tick);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
followTo(tick);
|
|
49
|
+
if (transportChanged || !rateWindow || tick < rateWindow.tick) {
|
|
50
|
+
// Just started, or jumped back: nothing measured yet belongs to this run.
|
|
51
|
+
rate = canvas.writtenTicksPerSecond(tick);
|
|
52
|
+
rateWindow = { tick, at };
|
|
53
|
+
describe({ tick, atMs: at, ticksPerSecond: rate });
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const span = (at - rateWindow.at) / 1000;
|
|
57
|
+
const measured = span >= RATE_WINDOW_SECONDS ? (tick - rateWindow.tick) / span : 0;
|
|
58
|
+
if (rate <= 0 || !motion) {
|
|
59
|
+
if (measured <= 0)
|
|
60
|
+
return;
|
|
61
|
+
rate = measured;
|
|
62
|
+
rateWindow = { tick, at };
|
|
63
|
+
describe({ tick, atMs: at, ticksPerSecond: rate });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const expected = motion.tick + (motion.ticksPerSecond * (at - motion.atMs)) / 1000;
|
|
67
|
+
const drifted = Math.abs(expected - tick) > rate * DRIFT_SECONDS;
|
|
68
|
+
const retimed = span >= RATE_CHANGE_WINDOW_SECONDS &&
|
|
69
|
+
measured > 0 &&
|
|
70
|
+
Math.abs(measured - rate) > rate * RATE_CHANGE;
|
|
71
|
+
if (drifted || retimed) {
|
|
72
|
+
// The measurement window restarts only once it has produced a rate:
|
|
73
|
+
// restarting it on every correction would keep it too short to measure,
|
|
74
|
+
// and a transport running at another speed would be re-anchored at the
|
|
75
|
+
// wrong rate forever.
|
|
76
|
+
if (measured > 0) {
|
|
77
|
+
rate = measured;
|
|
78
|
+
rateWindow = { tick, at };
|
|
79
|
+
}
|
|
80
|
+
describe({ tick, atMs: at, ticksPerSecond: rate });
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
let queued = false;
|
|
84
|
+
let disposed = false;
|
|
85
|
+
const unsubscribePosition = position.subscribe(() => {
|
|
86
|
+
if (queued)
|
|
87
|
+
return;
|
|
88
|
+
queued = true;
|
|
89
|
+
defer(() => {
|
|
90
|
+
queued = false;
|
|
91
|
+
if (!disposed)
|
|
92
|
+
wake();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
const unsubscribeLatency = setSoundingRenderDelay && canvas.subscribeHighlightLatency
|
|
96
|
+
? canvas.subscribeHighlightLatency(setSoundingRenderDelay)
|
|
97
|
+
: undefined;
|
|
98
|
+
const unsubscribeSounding = onSounding?.(notes => canvas.setPlayingNotes(notes));
|
|
99
|
+
// The cursor starts where the playhead is, without scrolling anywhere.
|
|
100
|
+
canvas.setCursorTick(position.tick);
|
|
101
|
+
motion = { tick: position.tick, atMs: now(), ticksPerSecond: 0 };
|
|
102
|
+
followedMeasure = canvas.measureIndexAt(position.tick);
|
|
103
|
+
return () => {
|
|
104
|
+
disposed = true;
|
|
105
|
+
unsubscribePosition();
|
|
106
|
+
unsubscribeSounding?.();
|
|
107
|
+
unsubscribeLatency?.();
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=playback-binding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playback-binding.js","sourceRoot":"","sources":["../src/playback-binding.ts"],"names":[],"mappings":"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"}
|
package/dist/playhead.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playhead.d.ts","sourceRoot":"","sources":["../src/playhead.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,KAAK,EACV,oBAAoB,EAGrB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"playhead.d.ts","sourceRoot":"","sources":["../src/playhead.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,KAAK,EACV,oBAAoB,EAGrB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,aAAa,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;AAEtE,MAAM,MAAM,aAAa,GAAG;IAC1B,mCAAmC;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,yGAAyG;IACzG,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AA8NF;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,MAAM,EACZ,aAAa,CAAC,EAAE,aAAa,GAC5B,aAAa,GAAG,IAAI,CAkDtB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,KAAK,EACZ,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,aAAa,CAAC,EAAE,aAAa,GAC5B,MAAM,GAAG,IAAI,CA6Cf"}
|
package/dist/playhead.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { measureAtXInSystem, systemAtY } from './layout.js';
|
|
2
|
+
import { headerWidthForMeasure } from './stave-header.js';
|
|
2
3
|
/** The score's shared measure grid (first track — every track shares it once `rebuildMeasureTicks` has run; same convention as `store/selectors.ts`). */
|
|
3
4
|
function measureTimings(score) {
|
|
4
5
|
const measures = score.tracks[0]?.measures ?? [];
|
|
@@ -49,6 +50,21 @@ function systemForMeasureIndex(plan, measureIndex) {
|
|
|
49
50
|
}
|
|
50
51
|
return null;
|
|
51
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Where a bar's music can begin and end when nothing has been drawn yet.
|
|
55
|
+
*
|
|
56
|
+
* The stave box runs barline to barline, but its left edge holds whatever
|
|
57
|
+
* header the layout reserved — the clef, key and time signature a system
|
|
58
|
+
* restates, and a change mid-system. Spreading the bar across the whole box put
|
|
59
|
+
* tick 0 on the barline, left of the clef, for the frame before the first paint
|
|
60
|
+
* and anywhere off the drawn window. The header width is the layout's own
|
|
61
|
+
* figure, so this and the stave agree about where the notes start.
|
|
62
|
+
*/
|
|
63
|
+
function undrawnSpan(plan, system, measureIndex, box) {
|
|
64
|
+
const header = headerWidthForMeasure(plan.tracks, measureIndex, system.measureIndices[0] === measureIndex);
|
|
65
|
+
const inset = Math.max(0, Math.min(header, box.width));
|
|
66
|
+
return { startX: box.x + inset, width: box.width - inset };
|
|
67
|
+
}
|
|
52
68
|
/** Straight-line interpolation between two known points. */
|
|
53
69
|
function lerp(tick, a, b) {
|
|
54
70
|
if (b.tick === a.tick)
|
|
@@ -205,8 +221,9 @@ export function caretPositionForTick(plan, score, tick, notePositions) {
|
|
|
205
221
|
const fraction = timing.durationTicks > 0
|
|
206
222
|
? Math.min(1, Math.max(0, (tick - timing.startTick) / timing.durationTicks))
|
|
207
223
|
: 0;
|
|
224
|
+
const span = undrawnSpan(plan, system, measureIndex, layout.box);
|
|
208
225
|
return {
|
|
209
|
-
x:
|
|
226
|
+
x: span.startX + fraction * span.width,
|
|
210
227
|
yTop: system.yTop,
|
|
211
228
|
yBottom: system.yBottom,
|
|
212
229
|
};
|
|
@@ -247,8 +264,10 @@ export function tickForPoint(plan, score, x, y, notePositions) {
|
|
|
247
264
|
const endX = endAnchorFor(plan, system, hit.measureIndex, drawn.area, notePositions);
|
|
248
265
|
return Math.round(tickForX(x, drawn.positions, drawn.area, timing.startTick, endTick, endX));
|
|
249
266
|
}
|
|
250
|
-
|
|
251
|
-
|
|
267
|
+
// The inverse of the undrawn caret, over the same span.
|
|
268
|
+
const span = undrawnSpan(plan, system, hit.measureIndex, hit.box);
|
|
269
|
+
const fraction = span.width > 0
|
|
270
|
+
? Math.min(1, Math.max(0, (x - span.startX) / span.width))
|
|
252
271
|
: 0;
|
|
253
272
|
return Math.round(timing.startTick + fraction * timing.durationTicks);
|
|
254
273
|
}
|
package/dist/playhead.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playhead.js","sourceRoot":"","sources":["../src/playhead.ts"],"names":[],"mappings":"AA6CA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"playhead.js","sourceRoot":"","sources":["../src/playhead.ts"],"names":[],"mappings":"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"}
|
package/dist/press.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a press on the score was: a tap, a long press, or a drag.
|
|
3
|
+
*
|
|
4
|
+
* Both apps answer this at the end of every press, and they answered it with
|
|
5
|
+
* different numbers in different files — the web's `DRAG_THRESHOLD` inside the
|
|
6
|
+
* notation view, the native app's `LONG_PRESS_MS`/`LONG_PRESS_SLOP` inside its
|
|
7
|
+
* scrolling score. The question is about the canvas (did the press stay on the
|
|
8
|
+
* thing under it?), so it lives beside the hit-testing that answers the next
|
|
9
|
+
* question.
|
|
10
|
+
*
|
|
11
|
+
* ## Mouse and touch differ, on purpose
|
|
12
|
+
*
|
|
13
|
+
* A mouse is still: a click that moved more than 3px was meant to move, which
|
|
14
|
+
* is exactly where the web's box select has always started. A finger is not —
|
|
15
|
+
* a deliberate tap on a phone routinely lands several points from where it
|
|
16
|
+
* touched down, and holding a finger to 3px would turn a good share of taps
|
|
17
|
+
* into drags. So a touch gets `TOUCH_TAP_SLOP`, the same allowance a long press
|
|
18
|
+
* already had: a press that stays put long enough to be a long press would
|
|
19
|
+
* otherwise have been a *drag* had it been released a moment sooner, which is
|
|
20
|
+
* not a distinction a thumb can make.
|
|
21
|
+
*
|
|
22
|
+
* A mouse never long-presses. It has a secondary button for the context menu,
|
|
23
|
+
* and a click held while the reader looks at something is not a request for
|
|
24
|
+
* one — reading it as one would change what an ordinary slow click does on the
|
|
25
|
+
* web. React Native on macOS passes `'touch'`, because its gesture model is the
|
|
26
|
+
* touch one: the long press is how it opens the menu.
|
|
27
|
+
*
|
|
28
|
+
* ## Distance
|
|
29
|
+
*
|
|
30
|
+
* Measured per axis (the larger of |dx| and |dy|), which is the web's existing
|
|
31
|
+
* rule to the pixel. The native app used the straight-line distance; per axis
|
|
32
|
+
* lets a diagonal wander up to √2 further, which is under five points at a
|
|
33
|
+
* 12pt slop and not something a hand can aim at.
|
|
34
|
+
*
|
|
35
|
+
* The caller latches: once a press has classified as a drag it stays one even
|
|
36
|
+
* if the pointer comes back, which is how the web's `drag.moved` behaves. This
|
|
37
|
+
* function answers for one moment and keeps no state.
|
|
38
|
+
*/
|
|
39
|
+
/** What produced the press. */
|
|
40
|
+
export type PointerKind = 'mouse' | 'touch';
|
|
41
|
+
export type PressKind = 'tap' | 'longPress' | 'drag';
|
|
42
|
+
/** How far a mouse press may move and still be a click, in px (web). */
|
|
43
|
+
export declare const TAP_SLOP = 3;
|
|
44
|
+
/** How far a finger may move and still be a tap, in pt. */
|
|
45
|
+
export declare const TOUCH_TAP_SLOP = 12;
|
|
46
|
+
/** How long a press is held to be a long press — iOS's and Android's own threshold. */
|
|
47
|
+
export declare const LONG_PRESS_MS = 500;
|
|
48
|
+
/** How far a long press may wander — about a thumb's width. */
|
|
49
|
+
export declare const LONG_PRESS_SLOP = 12;
|
|
50
|
+
/** The tap slop for a pointer kind. */
|
|
51
|
+
export declare function tapSlopFor(pointer: PointerKind): number;
|
|
52
|
+
export type PressMeasure = {
|
|
53
|
+
/** Movement since the press began, either sign. */
|
|
54
|
+
dx: number;
|
|
55
|
+
dy: number;
|
|
56
|
+
/** How long the press has been (or was) held. */
|
|
57
|
+
heldMs: number;
|
|
58
|
+
/** Defaults to `'mouse'`, the web's rule. */
|
|
59
|
+
pointer?: PointerKind;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* `'longPress'` if a touch was held `LONG_PRESS_MS` within `LONG_PRESS_SLOP`;
|
|
63
|
+
* otherwise `'tap'` if it moved no more than the tap slop; otherwise `'drag'`.
|
|
64
|
+
*/
|
|
65
|
+
export declare function classifyPress({ dx, dy, heldMs, pointer, }: PressMeasure): PressKind;
|
|
66
|
+
//# sourceMappingURL=press.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"press.d.ts","sourceRoot":"","sources":["../src/press.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,+BAA+B;AAC/B,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC;AAE5C,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,WAAW,GAAG,MAAM,CAAC;AAErD,wEAAwE;AACxE,eAAO,MAAM,QAAQ,IAAI,CAAC;AAE1B,2DAA2D;AAC3D,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,uFAAuF;AACvF,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC,+DAA+D;AAC/D,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC,uCAAuC;AACvC,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAEvD;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAC5B,EAAE,EACF,EAAE,EACF,MAAM,EACN,OAAiB,GAClB,EAAE,YAAY,GAAG,SAAS,CAU1B"}
|
package/dist/press.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a press on the score was: a tap, a long press, or a drag.
|
|
3
|
+
*
|
|
4
|
+
* Both apps answer this at the end of every press, and they answered it with
|
|
5
|
+
* different numbers in different files — the web's `DRAG_THRESHOLD` inside the
|
|
6
|
+
* notation view, the native app's `LONG_PRESS_MS`/`LONG_PRESS_SLOP` inside its
|
|
7
|
+
* scrolling score. The question is about the canvas (did the press stay on the
|
|
8
|
+
* thing under it?), so it lives beside the hit-testing that answers the next
|
|
9
|
+
* question.
|
|
10
|
+
*
|
|
11
|
+
* ## Mouse and touch differ, on purpose
|
|
12
|
+
*
|
|
13
|
+
* A mouse is still: a click that moved more than 3px was meant to move, which
|
|
14
|
+
* is exactly where the web's box select has always started. A finger is not —
|
|
15
|
+
* a deliberate tap on a phone routinely lands several points from where it
|
|
16
|
+
* touched down, and holding a finger to 3px would turn a good share of taps
|
|
17
|
+
* into drags. So a touch gets `TOUCH_TAP_SLOP`, the same allowance a long press
|
|
18
|
+
* already had: a press that stays put long enough to be a long press would
|
|
19
|
+
* otherwise have been a *drag* had it been released a moment sooner, which is
|
|
20
|
+
* not a distinction a thumb can make.
|
|
21
|
+
*
|
|
22
|
+
* A mouse never long-presses. It has a secondary button for the context menu,
|
|
23
|
+
* and a click held while the reader looks at something is not a request for
|
|
24
|
+
* one — reading it as one would change what an ordinary slow click does on the
|
|
25
|
+
* web. React Native on macOS passes `'touch'`, because its gesture model is the
|
|
26
|
+
* touch one: the long press is how it opens the menu.
|
|
27
|
+
*
|
|
28
|
+
* ## Distance
|
|
29
|
+
*
|
|
30
|
+
* Measured per axis (the larger of |dx| and |dy|), which is the web's existing
|
|
31
|
+
* rule to the pixel. The native app used the straight-line distance; per axis
|
|
32
|
+
* lets a diagonal wander up to √2 further, which is under five points at a
|
|
33
|
+
* 12pt slop and not something a hand can aim at.
|
|
34
|
+
*
|
|
35
|
+
* The caller latches: once a press has classified as a drag it stays one even
|
|
36
|
+
* if the pointer comes back, which is how the web's `drag.moved` behaves. This
|
|
37
|
+
* function answers for one moment and keeps no state.
|
|
38
|
+
*/
|
|
39
|
+
/** How far a mouse press may move and still be a click, in px (web). */
|
|
40
|
+
export const TAP_SLOP = 3;
|
|
41
|
+
/** How far a finger may move and still be a tap, in pt. */
|
|
42
|
+
export const TOUCH_TAP_SLOP = 12;
|
|
43
|
+
/** How long a press is held to be a long press — iOS's and Android's own threshold. */
|
|
44
|
+
export const LONG_PRESS_MS = 500;
|
|
45
|
+
/** How far a long press may wander — about a thumb's width. */
|
|
46
|
+
export const LONG_PRESS_SLOP = 12;
|
|
47
|
+
/** The tap slop for a pointer kind. */
|
|
48
|
+
export function tapSlopFor(pointer) {
|
|
49
|
+
return pointer === 'touch' ? TOUCH_TAP_SLOP : TAP_SLOP;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* `'longPress'` if a touch was held `LONG_PRESS_MS` within `LONG_PRESS_SLOP`;
|
|
53
|
+
* otherwise `'tap'` if it moved no more than the tap slop; otherwise `'drag'`.
|
|
54
|
+
*/
|
|
55
|
+
export function classifyPress({ dx, dy, heldMs, pointer = 'mouse', }) {
|
|
56
|
+
const moved = Math.max(Math.abs(dx), Math.abs(dy));
|
|
57
|
+
if (pointer === 'touch' &&
|
|
58
|
+
heldMs >= LONG_PRESS_MS &&
|
|
59
|
+
moved <= LONG_PRESS_SLOP) {
|
|
60
|
+
return 'longPress';
|
|
61
|
+
}
|
|
62
|
+
return moved <= tapSlopFor(pointer) ? 'tap' : 'drag';
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=press.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"press.js","sourceRoot":"","sources":["../src/press.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAOH,wEAAwE;AACxE,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAC;AAE1B,2DAA2D;AAC3D,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AAEjC,uFAAuF;AACvF,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AAEjC,+DAA+D;AAC/D,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAElC,uCAAuC;AACvC,MAAM,UAAU,UAAU,CAAC,OAAoB;IAC7C,OAAO,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC;AACzD,CAAC;AAYD;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,EAC5B,EAAE,EACF,EAAE,EACF,MAAM,EACN,OAAO,GAAG,OAAO,GACJ;IACb,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACnD,IACE,OAAO,KAAK,OAAO;QACnB,MAAM,IAAI,aAAa;QACvB,KAAK,IAAI,eAAe,EACxB,CAAC;QACD,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,OAAO,KAAK,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Score } from '@sudobility/music_types';
|
|
2
|
+
import type { LayoutPlan } from './layout.js';
|
|
3
|
+
import type { PaperOrientation, PaperSize, PrintPage } from './pagination.js';
|
|
4
|
+
import { printRenderOptions } from './print-layout.js';
|
|
5
|
+
import type { PrintSystemSlice } from './print-layout.js';
|
|
6
|
+
/**
|
|
7
|
+
* The paper picker. `css` is the `@page` size keyword; an orientation's value
|
|
8
|
+
* is its own CSS keyword already.
|
|
9
|
+
*
|
|
10
|
+
* A4 has a key like the others even though it reads "A4" in both languages —
|
|
11
|
+
* one shape for every entry is what lets a picker map straight through, and the
|
|
12
|
+
* locales list it as shared by design.
|
|
13
|
+
*/
|
|
14
|
+
export declare const PAPER_OPTIONS: ReadonlyArray<{
|
|
15
|
+
value: PaperSize;
|
|
16
|
+
labelKey: string;
|
|
17
|
+
css: string;
|
|
18
|
+
}>;
|
|
19
|
+
export declare const ORIENTATION_OPTIONS: ReadonlyArray<{
|
|
20
|
+
value: PaperOrientation;
|
|
21
|
+
labelKey: string;
|
|
22
|
+
}>;
|
|
23
|
+
/** The whole score, as against one track's id. */
|
|
24
|
+
export declare const WHOLE_SCORE = "score";
|
|
25
|
+
export type PrintPlanOptions = {
|
|
26
|
+
/** `'score'` (default) or a track id to print that player's part. */
|
|
27
|
+
scope?: string;
|
|
28
|
+
/** What the whole score prints; empty or absent means every track. Ignored for a part. */
|
|
29
|
+
visibleTrackIds?: readonly string[];
|
|
30
|
+
paper?: PaperSize;
|
|
31
|
+
orientation?: PaperOrientation;
|
|
32
|
+
};
|
|
33
|
+
export type PrintPlan = {
|
|
34
|
+
/** The score drawn onto the pages — a part or the marked full score, lenses applied. Draw slices from this, not the stored score. */
|
|
35
|
+
score: Score;
|
|
36
|
+
/** Passed to the renderer as the track filter; empty means every track. */
|
|
37
|
+
trackIds: readonly string[];
|
|
38
|
+
plan: LayoutPlan;
|
|
39
|
+
slices: PrintSystemSlice[];
|
|
40
|
+
/** Never an empty page. */
|
|
41
|
+
pages: PrintPage[];
|
|
42
|
+
/** In layout units, not millimetres. */
|
|
43
|
+
pageHeight: number;
|
|
44
|
+
renderOptions: ReturnType<typeof printRenderOptions>;
|
|
45
|
+
/** The player page turns were laid out for, or null for a whole score. */
|
|
46
|
+
turnTrackId: string | null;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Plan the printout of `score` (the **stored** score).
|
|
50
|
+
*
|
|
51
|
+
* - A part goes through `extractPart`: that track alone, written for its
|
|
52
|
+
* instrument (a clarinet a tone up), with the whole score's rehearsal marks,
|
|
53
|
+
* collapsed rests and cues. Page turns are laid out for that player.
|
|
54
|
+
* - The whole score stays in concert pitch — a conductor compares parts, so
|
|
55
|
+
* none may be transposed — gains rehearsal marks, and prints the visible
|
|
56
|
+
* tracks. Nobody turns for a dozen staves at once, so it has no turn track.
|
|
57
|
+
* - Both are drawn through `displayScore` in concert mode, which applies the
|
|
58
|
+
* octave-bracket lens: an `8va` is part of the notation, not a reading mode.
|
|
59
|
+
* (A part is already written pitch; the lens adds only the bracket.)
|
|
60
|
+
*
|
|
61
|
+
* Null when `scope` names a track the score does not have.
|
|
62
|
+
*/
|
|
63
|
+
export declare function printPlan(score: Score, options?: PrintPlanOptions): PrintPlan | null;
|
|
64
|
+
//# sourceMappingURL=print-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"print-plan.d.ts","sourceRoot":"","sources":["../src/print-plan.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAO9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAEL,kBAAkB,EAEnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAoB1D;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,aAAa,CAAC;IACxC,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,CAIE,CAAC;AAEJ,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC;IAC9C,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAGE,CAAC;AAEJ,kDAAkD;AAClD,eAAO,MAAM,WAAW,UAAU,CAAC;AAEnC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0FAA0F;IAC1F,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,qIAAqI;IACrI,KAAK,EAAE,KAAK,CAAC;IACb,2EAA2E;IAC3E,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,2BAA2B;IAC3B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,wCAAwC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;IACrD,0EAA0E;IAC1E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,KAAK,EACZ,OAAO,GAAE,gBAAqB,GAC7B,SAAS,GAAG,IAAI,CAiClB"}
|