@real-music-packages/web-core 0.11.0 → 0.12.0
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/README.md +25 -0
- package/dist/scene/headless.d.ts +10 -0
- package/dist/scene/headless.js +108 -0
- package/dist/scene/headless.js.map +1 -0
- package/dist/scene/index.d.ts +459 -10
- package/dist/scene/index.js +1446 -140
- package/dist/scene/index.js.map +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -36,6 +36,31 @@ peers; OSMD is only loaded via a dynamic import). Pure helpers (`parseMidi`,
|
|
|
36
36
|
- **`renderNotation(xml, opts?)`** — renders a MusicXML string via OSMD to a detached canvas; returns per-staff measure boxes (RSR geometry), per-measure column union boxes (RMT geometry), system rows, and content bounds. Parameterisable via `RenderNotationOpts` (paper, inkSumThreshold, hostWidth, bars).
|
|
37
37
|
- **`createPromoSampler(opts?)`** — creates a Tone.js Salamander sampler wired to a `MediaStreamDestination`. `keepAlive` option feeds a silent ConstantSource so the recorder never drops silent intro scenes (default false; RMT passes true).
|
|
38
38
|
|
|
39
|
+
### `./scene`
|
|
40
|
+
Render-components: the Score model (`scoreFromMusicXML`), the Layer contract +
|
|
41
|
+
SceneSpec runner, and the built-in layers (notation, scroll-cursor, keyboard,
|
|
42
|
+
falling-notes, promo cards, spectrum, branding, and the S5 extended catalog).
|
|
43
|
+
|
|
44
|
+
This barrel is **browser-safe** — it pulls in no Node-only dependencies, so
|
|
45
|
+
Vite/rolldown consumers need **no aliases**. `scoreFromMusicXML` runs unchanged in
|
|
46
|
+
the browser (native canvas handles OSMD's lyric layout).
|
|
47
|
+
|
|
48
|
+
### `./scene/headless` (Node-only)
|
|
49
|
+
`setupHeadlessDom()` — installs `jsdom` globals + a fake 2D canvas context so OSMD
|
|
50
|
+
can `load()` a score outside a browser (CI, batch, audio-only paths). Import it
|
|
51
|
+
**only in Node**, and call it once before `scoreFromMusicXML` (or pass
|
|
52
|
+
`opts.osmdFactory`):
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { setupHeadlessDom } from '@real-music-packages/web-core/scene/headless'; // Node only
|
|
56
|
+
import { scoreFromMusicXML } from '@real-music-packages/web-core/scene';
|
|
57
|
+
await setupHeadlessDom();
|
|
58
|
+
const score = scoreFromMusicXML(xml);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This subpath references `jsdom` and must never be imported from browser code. It
|
|
62
|
+
lives here (not in `./scene`) precisely so the `./scene` barrel stays bundler-safe.
|
|
63
|
+
|
|
39
64
|
## ⚠️ Octave-base gotcha (`scales.getMidiNote` / `getScaleDegree`)
|
|
40
65
|
These are ported verbatim from RealEarTrainer and use **RET's non-standard octave
|
|
41
66
|
base**: `getMidiNote(1, 'C', 4) === 48`, i.e. one octave below the General-MIDI
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install jsdom globals + a fake 2D canvas context so OSMD can `load()` a score
|
|
3
|
+
* headlessly. Idempotent. Call once before constructing an OSMD instance in Node.
|
|
4
|
+
*
|
|
5
|
+
* No-ops if `window`/`document` already exist (e.g. a real browser, or a vitest
|
|
6
|
+
* jsdom environment), so it is safe to call unconditionally.
|
|
7
|
+
*/
|
|
8
|
+
declare function setupHeadlessDom(): Promise<void>;
|
|
9
|
+
|
|
10
|
+
export { setupHeadlessDom };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// src/scene/headless.ts
|
|
2
|
+
var installed = false;
|
|
3
|
+
async function setupHeadlessDom() {
|
|
4
|
+
if (installed) return;
|
|
5
|
+
const g = globalThis;
|
|
6
|
+
if (typeof g.document !== "undefined" && typeof g.window !== "undefined") {
|
|
7
|
+
ensureFakeContext(g.window);
|
|
8
|
+
installed = true;
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const { JSDOM } = await import("jsdom");
|
|
12
|
+
const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>", {
|
|
13
|
+
pretendToBeVisual: true
|
|
14
|
+
});
|
|
15
|
+
const { window } = dom;
|
|
16
|
+
ensureFakeContext(window);
|
|
17
|
+
g.window = window;
|
|
18
|
+
g.document = window.document;
|
|
19
|
+
try {
|
|
20
|
+
g.navigator = window.navigator;
|
|
21
|
+
} catch {
|
|
22
|
+
}
|
|
23
|
+
g.HTMLElement = window.HTMLElement;
|
|
24
|
+
g.Node = window.Node;
|
|
25
|
+
g.DOMParser = window.DOMParser;
|
|
26
|
+
g.XMLSerializer = window.XMLSerializer;
|
|
27
|
+
g.requestAnimationFrame = (cb) => setTimeout(() => cb(Date.now()), 0);
|
|
28
|
+
g.cancelAnimationFrame = () => {
|
|
29
|
+
};
|
|
30
|
+
installed = true;
|
|
31
|
+
}
|
|
32
|
+
function ensureFakeContext(window) {
|
|
33
|
+
const proto = window.HTMLCanvasElement?.prototype;
|
|
34
|
+
if (!proto) return;
|
|
35
|
+
const fakeCtx = makeFakeContext();
|
|
36
|
+
proto.getContext = function() {
|
|
37
|
+
return fakeCtx;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function makeFakeContext() {
|
|
41
|
+
return {
|
|
42
|
+
font: "10px Arial",
|
|
43
|
+
fillStyle: "#000",
|
|
44
|
+
strokeStyle: "#000",
|
|
45
|
+
lineWidth: 1,
|
|
46
|
+
textAlign: "left",
|
|
47
|
+
textBaseline: "alphabetic",
|
|
48
|
+
globalAlpha: 1,
|
|
49
|
+
measureText: (s) => ({
|
|
50
|
+
width: (s ? s.length : 0) * 6,
|
|
51
|
+
actualBoundingBoxAscent: 8,
|
|
52
|
+
actualBoundingBoxDescent: 2
|
|
53
|
+
}),
|
|
54
|
+
save() {
|
|
55
|
+
},
|
|
56
|
+
restore() {
|
|
57
|
+
},
|
|
58
|
+
beginPath() {
|
|
59
|
+
},
|
|
60
|
+
closePath() {
|
|
61
|
+
},
|
|
62
|
+
moveTo() {
|
|
63
|
+
},
|
|
64
|
+
lineTo() {
|
|
65
|
+
},
|
|
66
|
+
bezierCurveTo() {
|
|
67
|
+
},
|
|
68
|
+
quadraticCurveTo() {
|
|
69
|
+
},
|
|
70
|
+
arc() {
|
|
71
|
+
},
|
|
72
|
+
rect() {
|
|
73
|
+
},
|
|
74
|
+
fill() {
|
|
75
|
+
},
|
|
76
|
+
stroke() {
|
|
77
|
+
},
|
|
78
|
+
fillRect() {
|
|
79
|
+
},
|
|
80
|
+
clearRect() {
|
|
81
|
+
},
|
|
82
|
+
fillText() {
|
|
83
|
+
},
|
|
84
|
+
strokeText() {
|
|
85
|
+
},
|
|
86
|
+
translate() {
|
|
87
|
+
},
|
|
88
|
+
rotate() {
|
|
89
|
+
},
|
|
90
|
+
scale() {
|
|
91
|
+
},
|
|
92
|
+
setTransform() {
|
|
93
|
+
},
|
|
94
|
+
transform() {
|
|
95
|
+
},
|
|
96
|
+
drawImage() {
|
|
97
|
+
},
|
|
98
|
+
clip() {
|
|
99
|
+
},
|
|
100
|
+
createLinearGradient: () => ({ addColorStop() {
|
|
101
|
+
} }),
|
|
102
|
+
getImageData: () => ({ data: new Uint8ClampedArray(4) })
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export {
|
|
106
|
+
setupHeadlessDom
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=headless.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/scene/headless.ts"],"sourcesContent":["// Headless DOM setup for running OSMD outside a browser (Node — CI, batch,\n// whozart's notation-less audio path).\n//\n// OSMD's load() runs graphical layout, which calls canvas 2D text metrics.\n// Bare jsdom returns null for getContext('2d'), so layout throws on scores\n// with lyrics. We install a minimal fake 2D context so layout completes; we\n// never read the pixels — only osmd.sheet (the source model) is consumed by\n// scoreFromMusicXML, so a no-op context is sufficient.\n//\n// The SAME extraction code (scoreFromMusicXML) runs unchanged in a real\n// browser: there the native 2D context handles lyric layout and this helper is\n// never called. This module is the only Node-specific seam.\n//\n// jsdom is a devDependency (used by tests + headless callers); it is imported\n// dynamically so bundling for the browser never pulls it in.\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nlet installed = false;\n\n/**\n * Install jsdom globals + a fake 2D canvas context so OSMD can `load()` a score\n * headlessly. Idempotent. Call once before constructing an OSMD instance in Node.\n *\n * No-ops if `window`/`document` already exist (e.g. a real browser, or a vitest\n * jsdom environment), so it is safe to call unconditionally.\n */\nexport async function setupHeadlessDom(): Promise<void> {\n if (installed) return;\n const g = globalThis as any;\n if (typeof g.document !== 'undefined' && typeof g.window !== 'undefined') {\n // A DOM is already present (browser or test env). Still ensure a usable 2D\n // context for OSMD's lyric layout if jsdom didn't provide one.\n ensureFakeContext(g.window);\n installed = true;\n return;\n }\n\n // Literal specifier so bundlers can tree-shake it out of browser builds.\n const { JSDOM } = await import('jsdom');\n const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {\n pretendToBeVisual: true,\n });\n const { window } = dom;\n\n ensureFakeContext(window);\n\n g.window = window;\n g.document = window.document;\n // Node 26: globalThis.navigator is read-only — tolerate the failure.\n try {\n g.navigator = window.navigator;\n } catch {\n /* read-only on some Node versions; OSMD doesn't require it */\n }\n g.HTMLElement = window.HTMLElement;\n g.Node = window.Node;\n g.DOMParser = window.DOMParser;\n g.XMLSerializer = window.XMLSerializer;\n g.requestAnimationFrame = (cb: (t: number) => void) => setTimeout(() => cb(Date.now()), 0);\n g.cancelAnimationFrame = () => {};\n\n installed = true;\n}\n\n/** Patch HTMLCanvasElement.getContext to return a no-op 2D context. */\nfunction ensureFakeContext(window: any): void {\n const proto = window.HTMLCanvasElement?.prototype;\n if (!proto) return;\n const fakeCtx = makeFakeContext();\n proto.getContext = function () {\n return fakeCtx;\n };\n}\n\n/** A minimal 2D context: text metrics return a rough width; everything else is a no-op. */\nfunction makeFakeContext(): any {\n return {\n font: '10px Arial',\n fillStyle: '#000',\n strokeStyle: '#000',\n lineWidth: 1,\n textAlign: 'left',\n textBaseline: 'alphabetic',\n globalAlpha: 1,\n measureText: (s: string) => ({\n width: (s ? s.length : 0) * 6,\n actualBoundingBoxAscent: 8,\n actualBoundingBoxDescent: 2,\n }),\n save() {},\n restore() {},\n beginPath() {},\n closePath() {},\n moveTo() {},\n lineTo() {},\n bezierCurveTo() {},\n quadraticCurveTo() {},\n arc() {},\n rect() {},\n fill() {},\n stroke() {},\n fillRect() {},\n clearRect() {},\n fillText() {},\n strokeText() {},\n translate() {},\n rotate() {},\n scale() {},\n setTransform() {},\n transform() {},\n drawImage() {},\n clip() {},\n createLinearGradient: () => ({ addColorStop() {} }),\n getImageData: () => ({ data: new Uint8ClampedArray(4) }),\n };\n}\n"],"mappings":";AAkBA,IAAI,YAAY;AAShB,eAAsB,mBAAkC;AACtD,MAAI,UAAW;AACf,QAAM,IAAI;AACV,MAAI,OAAO,EAAE,aAAa,eAAe,OAAO,EAAE,WAAW,aAAa;AAGxE,sBAAkB,EAAE,MAAM;AAC1B,gBAAY;AACZ;AAAA,EACF;AAGA,QAAM,EAAE,MAAM,IAAI,MAAM,OAAO,OAAO;AACtC,QAAM,MAAM,IAAI,MAAM,6CAA6C;AAAA,IACjE,mBAAmB;AAAA,EACrB,CAAC;AACD,QAAM,EAAE,OAAO,IAAI;AAEnB,oBAAkB,MAAM;AAExB,IAAE,SAAS;AACX,IAAE,WAAW,OAAO;AAEpB,MAAI;AACF,MAAE,YAAY,OAAO;AAAA,EACvB,QAAQ;AAAA,EAER;AACA,IAAE,cAAc,OAAO;AACvB,IAAE,OAAO,OAAO;AAChB,IAAE,YAAY,OAAO;AACrB,IAAE,gBAAgB,OAAO;AACzB,IAAE,wBAAwB,CAAC,OAA4B,WAAW,MAAM,GAAG,KAAK,IAAI,CAAC,GAAG,CAAC;AACzF,IAAE,uBAAuB,MAAM;AAAA,EAAC;AAEhC,cAAY;AACd;AAGA,SAAS,kBAAkB,QAAmB;AAC5C,QAAM,QAAQ,OAAO,mBAAmB;AACxC,MAAI,CAAC,MAAO;AACZ,QAAM,UAAU,gBAAgB;AAChC,QAAM,aAAa,WAAY;AAC7B,WAAO;AAAA,EACT;AACF;AAGA,SAAS,kBAAuB;AAC9B,SAAO;AAAA,IACL,MAAM;AAAA,IACN,WAAW;AAAA,IACX,aAAa;AAAA,IACb,WAAW;AAAA,IACX,WAAW;AAAA,IACX,cAAc;AAAA,IACd,aAAa;AAAA,IACb,aAAa,CAAC,OAAe;AAAA,MAC3B,QAAQ,IAAI,EAAE,SAAS,KAAK;AAAA,MAC5B,yBAAyB;AAAA,MACzB,0BAA0B;AAAA,IAC5B;AAAA,IACA,OAAO;AAAA,IAAC;AAAA,IACR,UAAU;AAAA,IAAC;AAAA,IACX,YAAY;AAAA,IAAC;AAAA,IACb,YAAY;AAAA,IAAC;AAAA,IACb,SAAS;AAAA,IAAC;AAAA,IACV,SAAS;AAAA,IAAC;AAAA,IACV,gBAAgB;AAAA,IAAC;AAAA,IACjB,mBAAmB;AAAA,IAAC;AAAA,IACpB,MAAM;AAAA,IAAC;AAAA,IACP,OAAO;AAAA,IAAC;AAAA,IACR,OAAO;AAAA,IAAC;AAAA,IACR,SAAS;AAAA,IAAC;AAAA,IACV,WAAW;AAAA,IAAC;AAAA,IACZ,YAAY;AAAA,IAAC;AAAA,IACb,WAAW;AAAA,IAAC;AAAA,IACZ,aAAa;AAAA,IAAC;AAAA,IACd,YAAY;AAAA,IAAC;AAAA,IACb,SAAS;AAAA,IAAC;AAAA,IACV,QAAQ;AAAA,IAAC;AAAA,IACT,eAAe;AAAA,IAAC;AAAA,IAChB,YAAY;AAAA,IAAC;AAAA,IACb,YAAY;AAAA,IAAC;AAAA,IACb,OAAO;AAAA,IAAC;AAAA,IACR,sBAAsB,OAAO,EAAE,eAAe;AAAA,IAAC,EAAE;AAAA,IACjD,cAAc,OAAO,EAAE,MAAM,IAAI,kBAAkB,CAAC,EAAE;AAAA,EACxD;AACF;","names":[]}
|
package/dist/scene/index.d.ts
CHANGED
|
@@ -69,15 +69,6 @@ interface ScoreFromMusicXMLOpts {
|
|
|
69
69
|
*/
|
|
70
70
|
declare function scoreFromMusicXML(xml: string, opts?: ScoreFromMusicXMLOpts): Promise<Score>;
|
|
71
71
|
|
|
72
|
-
/**
|
|
73
|
-
* Install jsdom globals + a fake 2D canvas context so OSMD can `load()` a score
|
|
74
|
-
* headlessly. Idempotent. Call once before constructing an OSMD instance in Node.
|
|
75
|
-
*
|
|
76
|
-
* No-ops if `window`/`document` already exist (e.g. a real browser, or a vitest
|
|
77
|
-
* jsdom environment), so it is safe to call unconditionally.
|
|
78
|
-
*/
|
|
79
|
-
declare function setupHeadlessDom(): Promise<void>;
|
|
80
|
-
|
|
81
72
|
/**
|
|
82
73
|
* Audio clock the runner exposes to layers. `nowMs` is the current playback
|
|
83
74
|
* position in milliseconds (audio-clock-driven, NOT wall-clock). During an
|
|
@@ -947,4 +938,462 @@ interface PromoCardsDemoOpts {
|
|
|
947
938
|
/** Build the demo SceneSpec wiring the S4 wrapper layers across a timeline. */
|
|
948
939
|
declare function promoCardsDemoSpec(opts?: PromoCardsDemoOpts): SceneSpec;
|
|
949
940
|
|
|
950
|
-
|
|
941
|
+
interface StaffKeyboardRayProps {
|
|
942
|
+
/** Layer key of the notation layer to link (forward-compat; v1 uses the single
|
|
943
|
+
* published engraving). Default "notation". */
|
|
944
|
+
notationKey?: string;
|
|
945
|
+
/** Layer key of the keyboard layer to link. Default "keyboard". */
|
|
946
|
+
keyboardKey?: string;
|
|
947
|
+
/** Ray reveal/grow + fade duration, ms. Default 180. */
|
|
948
|
+
fadeMs?: number;
|
|
949
|
+
/** Line width, world px. Default 4. */
|
|
950
|
+
width?: number;
|
|
951
|
+
/** Ray colour (default per-note hand colour via the theme accent). */
|
|
952
|
+
color?: string;
|
|
953
|
+
/** Number of segments to approximate the curve. Default 24. */
|
|
954
|
+
segments?: number;
|
|
955
|
+
}
|
|
956
|
+
declare const staffKeyboardRayFactory: LayerFactory<StaffKeyboardRayProps>;
|
|
957
|
+
|
|
958
|
+
interface RayEndpoints {
|
|
959
|
+
/** Staff-end point (screen px). */
|
|
960
|
+
staff: {
|
|
961
|
+
x: number;
|
|
962
|
+
y: number;
|
|
963
|
+
};
|
|
964
|
+
/** Keyboard-end point (screen px) = the top-centre of the key. */
|
|
965
|
+
keyboard: {
|
|
966
|
+
x: number;
|
|
967
|
+
y: number;
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* The staff-end anchor for an audio progress `t01` (0..1 over the clip), using the
|
|
972
|
+
* playhead sweep across the notation's mapped measure columns. Mirrors
|
|
973
|
+
* notationGeometry.playheadLine's x/y derivation so the ray starts exactly where
|
|
974
|
+
* the playhead is. Returns null when the layout has no measures/systems.
|
|
975
|
+
*/
|
|
976
|
+
declare function staffAnchor(layout: NotationLayout, t01: number): {
|
|
977
|
+
x: number;
|
|
978
|
+
y: number;
|
|
979
|
+
} | null;
|
|
980
|
+
/**
|
|
981
|
+
* Full ray endpoints for a note's pitch + an audio progress, given both layouts.
|
|
982
|
+
* The keyboard end is the top-centre of the note's key. Pure.
|
|
983
|
+
*/
|
|
984
|
+
declare function rayEndpoints(notation: NotationLayout, keyboard: KeyboardLayout, pitchMidi: number, t01: number): RayEndpoints | null;
|
|
985
|
+
/**
|
|
986
|
+
* A point along a quadratic Bézier from staff→keyboard, bulged toward the side, at
|
|
987
|
+
* parameter `u` 0..1. Used to draw a gently curved ray (a straight line reads as a
|
|
988
|
+
* cursor; a curve reads as a connector). Pure.
|
|
989
|
+
*/
|
|
990
|
+
declare function rayPointAt(ends: RayEndpoints, u: number, bulge?: number): {
|
|
991
|
+
x: number;
|
|
992
|
+
y: number;
|
|
993
|
+
};
|
|
994
|
+
|
|
995
|
+
interface CountingTrackProps {
|
|
996
|
+
/** Subdivisions per beat shown: 1 (just numbers), 2 ("&"), 4 ("e & a"). Default 1. */
|
|
997
|
+
subdiv?: 1 | 2 | 4;
|
|
998
|
+
/** Override time signature (else Score.timeSig, else 4/4). */
|
|
999
|
+
timeSig?: string;
|
|
1000
|
+
/** Override bpm (else Score.tempoMap, else 100). */
|
|
1001
|
+
bpm?: number;
|
|
1002
|
+
/** Track baseline (world y) the ball lands on. Default safeBox.bottom - 120. */
|
|
1003
|
+
trackY?: number;
|
|
1004
|
+
/** Bounce height in world px. Default 110. */
|
|
1005
|
+
bounce?: number;
|
|
1006
|
+
/** Syllable font size, world px. Default 34. */
|
|
1007
|
+
size?: number;
|
|
1008
|
+
/** Ball radius, world px. Default 22. */
|
|
1009
|
+
ballRadius?: number;
|
|
1010
|
+
/** Count-in offset, ms (first beat lands here). Default 0. */
|
|
1011
|
+
startMs?: number;
|
|
1012
|
+
}
|
|
1013
|
+
declare const countingTrackFactory: LayerFactory<CountingTrackProps>;
|
|
1014
|
+
|
|
1015
|
+
interface BeatGridOpts {
|
|
1016
|
+
/** Constant bpm of the QUARTER-note beat (tempoMap.segments[0].bpm). */
|
|
1017
|
+
bpm: number;
|
|
1018
|
+
/** Beats per bar (time-sig numerator). Default 4. */
|
|
1019
|
+
beatsPerBar?: number;
|
|
1020
|
+
/** Time-sig denominator (note value of one beat). Default 4. */
|
|
1021
|
+
beatUnit?: number;
|
|
1022
|
+
/** Subdivisions per beat for the syllable row: 1, 2 ("&"), or 4 ("e & a"). Default 1. */
|
|
1023
|
+
subdiv?: 1 | 2 | 4;
|
|
1024
|
+
/** Total length to cover, ms. */
|
|
1025
|
+
durationMs: number;
|
|
1026
|
+
/** Count-in offset: first beat lands at this ms (default 0). */
|
|
1027
|
+
startMs?: number;
|
|
1028
|
+
}
|
|
1029
|
+
interface BeatTick {
|
|
1030
|
+
/** Onset time, ms. */
|
|
1031
|
+
tMs: number;
|
|
1032
|
+
/** 0-based beat index across the whole clip. */
|
|
1033
|
+
index: number;
|
|
1034
|
+
/** 1-based beat number within the bar (the big counted number). */
|
|
1035
|
+
beatInBar: number;
|
|
1036
|
+
/** Subdivision index within the beat (0 = on the beat). */
|
|
1037
|
+
sub: number;
|
|
1038
|
+
/** The counting syllable ("1","e","&","a", "2", …). */
|
|
1039
|
+
syllable: string;
|
|
1040
|
+
/** True on the downbeat (beatInBar === 1, sub === 0). */
|
|
1041
|
+
downbeat: boolean;
|
|
1042
|
+
}
|
|
1043
|
+
/** ms between quarter beats; the beat-unit scales it (eighth beat = half a quarter). */
|
|
1044
|
+
declare function msPerBeat(bpm: number, beatUnit: number): number;
|
|
1045
|
+
/** Build the full beat+subdivision schedule across the clip. Pure. */
|
|
1046
|
+
declare function beatGrid(opts: BeatGridOpts): BeatTick[];
|
|
1047
|
+
/** The TempoMap's first-segment bpm (v1 single-tempo helper). */
|
|
1048
|
+
declare function bpmOf(tempoMap: TempoMap | undefined, fallback?: number): number;
|
|
1049
|
+
/**
|
|
1050
|
+
* Phase 0..1 within the current beat at time `tMs` (0 = on the beat). Pure.
|
|
1051
|
+
* Used by the bouncing ball's vertical position.
|
|
1052
|
+
*/
|
|
1053
|
+
declare function beatPhase(bpm: number, beatUnit: number, tMs: number, startMs?: number): number;
|
|
1054
|
+
/**
|
|
1055
|
+
* The bouncing ball's vertical offset 0..1 (0 = at the track line / floor, 1 =
|
|
1056
|
+
* top of the arc). A parabola that touches 0 exactly ON each beat and peaks at
|
|
1057
|
+
* mid-beat — so the ball "lands" on the count. Pure.
|
|
1058
|
+
*/
|
|
1059
|
+
declare function ballArc(phase01: number): number;
|
|
1060
|
+
/** Horizontal x (world) of the ball at time t, sweeping left→right across the
|
|
1061
|
+
* counting track in step with the beats. Pure. */
|
|
1062
|
+
declare function ballX(ticks: BeatTick[], tMs: number, xOf: (tick: BeatTick) => number): number;
|
|
1063
|
+
/** Parse a "n/d" time signature string into [numerator, denominator]. */
|
|
1064
|
+
declare function parseTimeSig(ts: string | undefined): [number, number];
|
|
1065
|
+
|
|
1066
|
+
type LabelMode = 'degree' | 'solfege';
|
|
1067
|
+
interface DegreeLabel {
|
|
1068
|
+
/** Diatonic degree 1..7 (the letter-distance from the tonic letter). */
|
|
1069
|
+
degree: number;
|
|
1070
|
+
/** Chromatic inflection in semitones vs the diatonic degree (−1 flat, +1 sharp, 0 natural). */
|
|
1071
|
+
alter: number;
|
|
1072
|
+
/** The display text for the chosen mode. */
|
|
1073
|
+
text: string;
|
|
1074
|
+
}
|
|
1075
|
+
/** Parse a key string ("Eb major" / "C# minor" / "F") into a tonic letter + mode. */
|
|
1076
|
+
declare function parseKey(key: string | undefined): {
|
|
1077
|
+
tonicLetter: string;
|
|
1078
|
+
tonicPc: number;
|
|
1079
|
+
minor: boolean;
|
|
1080
|
+
};
|
|
1081
|
+
/**
|
|
1082
|
+
* Compute the scale-degree label for a note, from its NOTATED spelling.
|
|
1083
|
+
*
|
|
1084
|
+
* `degree` = the letter distance (1..7) from the tonic letter to the note's
|
|
1085
|
+
* letter, which is the enharmonically-correct diatonic degree. `alter` = how the
|
|
1086
|
+
* note's actual pitch class differs from that degree's pitch in the key
|
|
1087
|
+
* (so D♯ vs D♭ in C give the same degree 2 but +1 / −1). For MINOR keys the
|
|
1088
|
+
* reference scale is natural minor (so the lowered 3/6/7 read as natural degrees,
|
|
1089
|
+
* matching how movable-do "la-based minor" is taught only in MAJOR; here we use
|
|
1090
|
+
* do-based, i.e. degrees relative to the major scale of the tonic — see note).
|
|
1091
|
+
*
|
|
1092
|
+
* Pure.
|
|
1093
|
+
*/
|
|
1094
|
+
declare function degreeLabel(step: string, alter: number, pitchMidi: number, key: string | undefined, mode: LabelMode): DegreeLabel;
|
|
1095
|
+
|
|
1096
|
+
interface DegreeLabelsProps {
|
|
1097
|
+
/** "degree" = scale-degree numbers (1..7, ♯/♭ chromatics); "solfege" =
|
|
1098
|
+
* movable-do syllables (do re mi …, di ra …). Default "degree". */
|
|
1099
|
+
mode?: LabelMode;
|
|
1100
|
+
/** Override the Score's key (e.g. for an excerpt in a different key). */
|
|
1101
|
+
key?: string;
|
|
1102
|
+
/** Label font size in world px. Default 30. */
|
|
1103
|
+
size?: number;
|
|
1104
|
+
/** Fade in/out of a label, ms. Default 120. */
|
|
1105
|
+
fadeMs?: number;
|
|
1106
|
+
/** Text colour. Default theme.ink on a translucent chip. */
|
|
1107
|
+
color?: string;
|
|
1108
|
+
}
|
|
1109
|
+
declare const degreeLabelsFactory: LayerFactory<DegreeLabelsProps>;
|
|
1110
|
+
|
|
1111
|
+
type HarmonicFunction = 'T' | 'S' | 'D' | 'other';
|
|
1112
|
+
interface ChordSpan {
|
|
1113
|
+
/** Span start, ms. */
|
|
1114
|
+
startMs: number;
|
|
1115
|
+
/** Span end, ms. */
|
|
1116
|
+
endMs: number;
|
|
1117
|
+
/** Tonal function. */
|
|
1118
|
+
fn: HarmonicFunction;
|
|
1119
|
+
/** Optional display label (roman numeral / chord symbol), drawn by the layer. */
|
|
1120
|
+
label?: string;
|
|
1121
|
+
}
|
|
1122
|
+
/** Default function colours (warm tonic / cool subdominant / tense dominant). */
|
|
1123
|
+
interface FunctionColors {
|
|
1124
|
+
T: string;
|
|
1125
|
+
S: string;
|
|
1126
|
+
D: string;
|
|
1127
|
+
other: string;
|
|
1128
|
+
}
|
|
1129
|
+
declare const DEFAULT_FUNCTION_COLORS: FunctionColors;
|
|
1130
|
+
/** The chord span active at time `tMs` (first whose [start,end) contains t), or null. */
|
|
1131
|
+
declare function activeChord(track: ChordSpan[], tMs: number): ChordSpan | null;
|
|
1132
|
+
/** Colour for a function. Pure. */
|
|
1133
|
+
declare function functionColor(fn: HarmonicFunction, colors: FunctionColors): string;
|
|
1134
|
+
/**
|
|
1135
|
+
* Validate a chord track (used by the layer's prop validator). Returns []
|
|
1136
|
+
* when valid, else human-readable errors. Exported so the gate message is precise.
|
|
1137
|
+
*/
|
|
1138
|
+
declare function validateChordTrack(track: unknown): string[];
|
|
1139
|
+
|
|
1140
|
+
type HarmonyMode = 'band' | 'keys' | 'wash';
|
|
1141
|
+
interface FunctionalHarmonyProps {
|
|
1142
|
+
/** REQUIRED: the timed chord spans (function + optional label). Host-supplied. */
|
|
1143
|
+
chordTrack: ChordSpan[];
|
|
1144
|
+
/** Which renderings to draw. Default ["band"]. */
|
|
1145
|
+
modes?: HarmonyMode[];
|
|
1146
|
+
/** Override the function→colour map. */
|
|
1147
|
+
colors?: FunctionColors;
|
|
1148
|
+
/** Band height (world px). Default 64. */
|
|
1149
|
+
bandHeight?: number;
|
|
1150
|
+
/** Band top (world y). Default safeBox.top. */
|
|
1151
|
+
bandTop?: number;
|
|
1152
|
+
/** Cross-fade between spans, ms. Default 200. */
|
|
1153
|
+
fadeMs?: number;
|
|
1154
|
+
/** Wash opacity at full intensity. Default 0.12. */
|
|
1155
|
+
washAlpha?: number;
|
|
1156
|
+
}
|
|
1157
|
+
declare const functionalHarmonyFactory: LayerFactory<FunctionalHarmonyProps>;
|
|
1158
|
+
|
|
1159
|
+
interface QuizOption {
|
|
1160
|
+
/** Option text (e.g. "Major 3rd", "Beethoven"). */
|
|
1161
|
+
text: string;
|
|
1162
|
+
}
|
|
1163
|
+
interface Quiz {
|
|
1164
|
+
/** The prompt ("Which interval?" / "Name this piece"). */
|
|
1165
|
+
question: string;
|
|
1166
|
+
/** 2–4 options. */
|
|
1167
|
+
options: QuizOption[];
|
|
1168
|
+
/** Index into options of the correct answer (0-based). */
|
|
1169
|
+
correctIndex: number;
|
|
1170
|
+
/** When the card starts asking (absolute ms). Default 0. */
|
|
1171
|
+
askMs?: number;
|
|
1172
|
+
/** When the correct option is revealed (absolute ms). REQUIRED. */
|
|
1173
|
+
revealMs: number;
|
|
1174
|
+
/** When the card finishes (absolute ms). REQUIRED. */
|
|
1175
|
+
endMs: number;
|
|
1176
|
+
/** Optional per-option poll percentages (0..100), shown as bars. Length must
|
|
1177
|
+
* match options. Host-supplied (e.g. real community votes); not fabricated. */
|
|
1178
|
+
poll?: number[];
|
|
1179
|
+
}
|
|
1180
|
+
type QuizPhase = 'before' | 'question' | 'reveal' | 'after';
|
|
1181
|
+
/** Resolve the card phase at time `tMs`. Pure. */
|
|
1182
|
+
declare function quizPhase(quiz: Quiz, tMs: number): QuizPhase;
|
|
1183
|
+
/**
|
|
1184
|
+
* Countdown fraction remaining 0..1 across [askMs, revealMs] (1 at ask, 0 at
|
|
1185
|
+
* reveal), clamped outside. Drives the countdown ring/bar. Pure.
|
|
1186
|
+
*/
|
|
1187
|
+
declare function countdownRemaining(quiz: Quiz, tMs: number): number;
|
|
1188
|
+
/**
|
|
1189
|
+
* Integer seconds remaining on the countdown (ceil), for the big number. Pure.
|
|
1190
|
+
*/
|
|
1191
|
+
declare function countdownSeconds(quiz: Quiz, tMs: number): number;
|
|
1192
|
+
/**
|
|
1193
|
+
* Reveal progress 0..1 across a short window after revealMs (for a pop/fade of
|
|
1194
|
+
* the correct option). `windowMs` default 350. Pure.
|
|
1195
|
+
*/
|
|
1196
|
+
declare function revealProgress(quiz: Quiz, tMs: number, windowMs?: number): number;
|
|
1197
|
+
/**
|
|
1198
|
+
* Validate a Quiz prop (used by the layer's validator + the pre-render gate).
|
|
1199
|
+
* Returns [] when valid, else human-readable errors. The card REQUIRES this prop;
|
|
1200
|
+
* it has no data otherwise.
|
|
1201
|
+
*/
|
|
1202
|
+
declare function validateQuiz(quiz: unknown): string[];
|
|
1203
|
+
|
|
1204
|
+
interface Section {
|
|
1205
|
+
/** Section start, ms. */
|
|
1206
|
+
startMs: number;
|
|
1207
|
+
/** Section end, ms. */
|
|
1208
|
+
endMs: number;
|
|
1209
|
+
/** Display label ("Verse", "A", "Exposition"). */
|
|
1210
|
+
label?: string;
|
|
1211
|
+
}
|
|
1212
|
+
/** Progress 0..1 along the clip at time t. Pure. */
|
|
1213
|
+
declare function progress01(durationMs: number, tMs: number): number;
|
|
1214
|
+
/** x of a time on a [left, left+width] bar across [0, durationMs]. Pure. */
|
|
1215
|
+
declare function timeToX(left: number, width: number, durationMs: number, tMs: number): number;
|
|
1216
|
+
/** The section containing time t (first whose [start,end) holds t), or null. Pure. */
|
|
1217
|
+
declare function activeSection(sections: Section[], tMs: number): Section | null;
|
|
1218
|
+
/**
|
|
1219
|
+
* Build uniform measure-span sections from a measure count + total duration (a
|
|
1220
|
+
* fallback when no explicit sections are given but a measure count is). Each
|
|
1221
|
+
* section is one measure, labelled by 1-based measure number. Pure.
|
|
1222
|
+
*/
|
|
1223
|
+
declare function measureSpans(measureCount: number, durationMs: number): Section[];
|
|
1224
|
+
/**
|
|
1225
|
+
* Validate a sections array (used by the layer validator). Returns [] when valid.
|
|
1226
|
+
*/
|
|
1227
|
+
declare function validateSections(sections: unknown): string[];
|
|
1228
|
+
|
|
1229
|
+
interface ExtendedDemoOpts {
|
|
1230
|
+
size?: [number, number];
|
|
1231
|
+
theme?: string;
|
|
1232
|
+
}
|
|
1233
|
+
/** Counting track + bouncing ball + scale-degree labels over a keyboard. */
|
|
1234
|
+
declare function countingDegreeDemoSpec(opts?: ExtendedDemoOpts & {
|
|
1235
|
+
subdiv?: 1 | 2 | 4;
|
|
1236
|
+
labelMode?: LabelMode;
|
|
1237
|
+
range?: '88' | 'auto';
|
|
1238
|
+
}): SceneSpec;
|
|
1239
|
+
/** Staff↔keyboard ray over a (pre-rendered) notation + keyboard. */
|
|
1240
|
+
declare function staffRayDemoSpec(rendered: RenderedNotation, opts?: ExtendedDemoOpts & {
|
|
1241
|
+
range?: '88' | 'auto';
|
|
1242
|
+
}): SceneSpec;
|
|
1243
|
+
/** Functional-harmony coloring (band + keys) over a keyboard, driven by a chord track. */
|
|
1244
|
+
declare function harmonyDemoSpec(chordTrack: ChordSpan[], opts?: ExtendedDemoOpts & {
|
|
1245
|
+
range?: '88' | 'auto';
|
|
1246
|
+
}): SceneSpec;
|
|
1247
|
+
/** MCQ poll card over a plain background (RET quiz / whozart name-that-piece). */
|
|
1248
|
+
declare function mcqDemoSpec(quiz: Quiz, opts?: ExtendedDemoOpts): SceneSpec;
|
|
1249
|
+
/** Circle-of-fifths widget (optionally animating a modulation). */
|
|
1250
|
+
declare function circleOfFifthsDemoSpec(opts?: ExtendedDemoOpts & {
|
|
1251
|
+
toKey?: string;
|
|
1252
|
+
fromMs?: number;
|
|
1253
|
+
toMs?: number;
|
|
1254
|
+
}): SceneSpec;
|
|
1255
|
+
/** Pitch-contour line over the melody (Score.notes) + a section minimap. */
|
|
1256
|
+
declare function contourMinimapDemoSpec(opts?: ExtendedDemoOpts & {
|
|
1257
|
+
sections?: Section[];
|
|
1258
|
+
measureCount?: number;
|
|
1259
|
+
}): SceneSpec;
|
|
1260
|
+
|
|
1261
|
+
interface McqCardProps {
|
|
1262
|
+
/** REQUIRED: the host-supplied quiz (question/options/correctIndex/timings). */
|
|
1263
|
+
quiz: Quiz;
|
|
1264
|
+
/** Accent for the correct reveal (default theme.accent). */
|
|
1265
|
+
correctColor?: string;
|
|
1266
|
+
/** Show the poll bars (only if quiz.poll is present). Default true. */
|
|
1267
|
+
showPoll?: boolean;
|
|
1268
|
+
}
|
|
1269
|
+
declare const mcqCardFactory: LayerFactory<McqCardProps>;
|
|
1270
|
+
|
|
1271
|
+
interface CircleOfFifthsProps {
|
|
1272
|
+
/** Override the highlighted key (else Score.key, else C). */
|
|
1273
|
+
key?: string;
|
|
1274
|
+
/** Animate the highlight to this key (modulation target). Host-supplied. */
|
|
1275
|
+
toKey?: string;
|
|
1276
|
+
/** Modulation start time (ms). Required if toKey is set. */
|
|
1277
|
+
fromMs?: number;
|
|
1278
|
+
/** Modulation end time (ms). Required if toKey is set. */
|
|
1279
|
+
toMs?: number;
|
|
1280
|
+
/** Widget centre [x,y] (world). Default centred in the safe box. */
|
|
1281
|
+
center?: [number, number];
|
|
1282
|
+
/** Outer radius (world px). Default min(safe.w, safe.h) * 0.32. */
|
|
1283
|
+
radius?: number;
|
|
1284
|
+
/** Show the relative-minor inner ring. Default true. */
|
|
1285
|
+
showMinor?: boolean;
|
|
1286
|
+
/** Highlight colour (default theme.accent). */
|
|
1287
|
+
color?: string;
|
|
1288
|
+
}
|
|
1289
|
+
declare const circleOfFifthsFactory: LayerFactory<CircleOfFifthsProps>;
|
|
1290
|
+
|
|
1291
|
+
/** Major-key labels around the circle, clockwise from the top. */
|
|
1292
|
+
declare const FIFTHS_MAJOR: string[];
|
|
1293
|
+
/** Relative-minor labels (inner ring), aligned to the same slots. */
|
|
1294
|
+
declare const FIFTHS_MINOR: string[];
|
|
1295
|
+
/** Pitch class at circle slot i (0..11), clockwise fifths from C. */
|
|
1296
|
+
declare function slotPc(i: number): number;
|
|
1297
|
+
/** The circle slot (0..11) for a tonic pitch class. Pure. */
|
|
1298
|
+
declare function pcToSlot(pc: number): number;
|
|
1299
|
+
/** The circle slot for a key string ("G major" / "Eb"). Pure. */
|
|
1300
|
+
declare function keySlot(key: string | undefined): number;
|
|
1301
|
+
/** Angle (radians, 0 = up/12-o'clock, clockwise +) of circle slot i. Pure. */
|
|
1302
|
+
declare function slotAngle(i: number): number;
|
|
1303
|
+
interface CirclePoint {
|
|
1304
|
+
x: number;
|
|
1305
|
+
y: number;
|
|
1306
|
+
}
|
|
1307
|
+
/** Cartesian point for a slot on a circle of radius r centred at (cx,cy).
|
|
1308
|
+
* 0 rad = straight up, increasing clockwise. Pure. */
|
|
1309
|
+
declare function slotPoint(cx: number, cy: number, r: number, i: number): CirclePoint;
|
|
1310
|
+
/**
|
|
1311
|
+
* Animated highlight slot as a fractional position from a source slot to a target
|
|
1312
|
+
* slot over progress `t01`, taking the SHORTEST way around the wheel. Returns a
|
|
1313
|
+
* fractional slot (e.g. 1.5 = between G and D). Pure.
|
|
1314
|
+
*/
|
|
1315
|
+
declare function animatedSlot(fromSlot: number, toSlot: number, t01: number): number;
|
|
1316
|
+
/** Cartesian point for a FRACTIONAL slot (e.g. mid-modulation) on the circle.
|
|
1317
|
+
* Pure. */
|
|
1318
|
+
declare function fracSlotPoint(cx: number, cy: number, r: number, frac: number): CirclePoint;
|
|
1319
|
+
|
|
1320
|
+
interface PitchContourProps {
|
|
1321
|
+
/** Plot band top (world y). Default safeBox.top + safeBox.h * 0.15. */
|
|
1322
|
+
top?: number;
|
|
1323
|
+
/** Plot band height (world px). Default safeBox.h * 0.35. */
|
|
1324
|
+
height?: number;
|
|
1325
|
+
/** Line width (world px). Default 5. */
|
|
1326
|
+
width?: number;
|
|
1327
|
+
/** Line colour (default theme.accent). */
|
|
1328
|
+
color?: string;
|
|
1329
|
+
/** Show the moving dot at the current pitch. Default true. */
|
|
1330
|
+
dot?: boolean;
|
|
1331
|
+
/** Dot radius (world px). Default 14. */
|
|
1332
|
+
dotRadius?: number;
|
|
1333
|
+
}
|
|
1334
|
+
declare const pitchContourFactory: LayerFactory<PitchContourProps>;
|
|
1335
|
+
|
|
1336
|
+
interface ContourPoint {
|
|
1337
|
+
/** Onset time, ms. */
|
|
1338
|
+
tMs: number;
|
|
1339
|
+
/** MIDI pitch of the melody note at that onset. */
|
|
1340
|
+
pitchMidi: number;
|
|
1341
|
+
}
|
|
1342
|
+
interface ContourPlot {
|
|
1343
|
+
left: number;
|
|
1344
|
+
right: number;
|
|
1345
|
+
top: number;
|
|
1346
|
+
bottom: number;
|
|
1347
|
+
minPitch: number;
|
|
1348
|
+
maxPitch: number;
|
|
1349
|
+
durationMs: number;
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Reduce notes to one melody point per distinct onset = the highest sounding
|
|
1353
|
+
* pitch at that onset. Returns points sorted by time. Pure.
|
|
1354
|
+
*/
|
|
1355
|
+
declare function contourPoints(notes: ScoreNote[]): ContourPoint[];
|
|
1356
|
+
/** Pitch range [min,max] of the points (with a 1-semitone pad, min span 2). Pure. */
|
|
1357
|
+
declare function pitchRange(points: ContourPoint[]): {
|
|
1358
|
+
min: number;
|
|
1359
|
+
max: number;
|
|
1360
|
+
};
|
|
1361
|
+
/** Project a (time,pitch) onto plot pixels. y is inverted (high pitch = up). Pure. */
|
|
1362
|
+
declare function projectPoint(plot: ContourPlot, tMs: number, pitchMidi: number): {
|
|
1363
|
+
x: number;
|
|
1364
|
+
y: number;
|
|
1365
|
+
};
|
|
1366
|
+
/** The full pixel polyline for the contour. Pure. */
|
|
1367
|
+
declare function contourPolyline(points: ContourPoint[], plot: ContourPlot): {
|
|
1368
|
+
x: number;
|
|
1369
|
+
y: number;
|
|
1370
|
+
}[];
|
|
1371
|
+
/**
|
|
1372
|
+
* The melody pitch sounding at time `tMs`: the pitch of the most recent onset
|
|
1373
|
+
* at-or-before t (step interpolation — a melody holds until the next note). Returns
|
|
1374
|
+
* null before the first onset. Pure.
|
|
1375
|
+
*/
|
|
1376
|
+
declare function pitchAt(points: ContourPoint[], tMs: number): number | null;
|
|
1377
|
+
/** The moving-dot position at time t (rides the contour at the current pitch). Pure. */
|
|
1378
|
+
declare function dotAt(points: ContourPoint[], plot: ContourPlot, tMs: number): {
|
|
1379
|
+
x: number;
|
|
1380
|
+
y: number;
|
|
1381
|
+
} | null;
|
|
1382
|
+
|
|
1383
|
+
interface SectionMinimapProps {
|
|
1384
|
+
/** Host-supplied section spans. Takes precedence over measureCount. */
|
|
1385
|
+
sections?: Section[];
|
|
1386
|
+
/** Fallback: split the clip into this many uniform measure bands. */
|
|
1387
|
+
measureCount?: number;
|
|
1388
|
+
/** Bar baseline (world y). Default safeBox.bottom - 40. */
|
|
1389
|
+
barY?: number;
|
|
1390
|
+
/** Bar thickness (world px). Default 10. */
|
|
1391
|
+
barHeight?: number;
|
|
1392
|
+
/** Show the active section's label above the bar. Default true. */
|
|
1393
|
+
showLabel?: boolean;
|
|
1394
|
+
/** Accent for the played portion + dot (default theme.accent). */
|
|
1395
|
+
color?: string;
|
|
1396
|
+
}
|
|
1397
|
+
declare const sectionMinimapFactory: LayerFactory<SectionMinimapProps>;
|
|
1398
|
+
|
|
1399
|
+
export { type Affine, type AudioClock, type AudioEvent, type BackgroundProps, type BeatGridOpts, type BeatTick, type BrandingProps, type BuildSceneOpts, type BuiltScene, type CameraState, type CaptionCue, type CaptionProps, type CaptionScript, type CaptionStyle, type ChordSpan, type CircleOfFifthsProps, type CirclePoint, type ClickTrackOpts, type ColorBy, type ContourPlot, type ContourPoint, type CountInOpts, type CountingTrackProps, type CtaProps, DEFAULT_FUNCTION_COLORS, type DegreeLabel, type DegreeLabelsProps, type DroneOpts, type DuckWindow, type Easing, type ExtendedDemoOpts, FIFTHS_MAJOR, FIFTHS_MINOR, FOLLOW_BARS, FOLLOW_PAD, type FallingKeyboardDemoOpts, type FallingNotesProps, type FunctionColors, type FunctionalHarmonyProps, type GateError, type GateInput, type HandColors, type HarmonicFunction, type HarmonyMode, type HighlightRegion, type HookProps, type KeyRect, type KeyboardLayout, type KeyboardLayoutOpts, type KeyboardProps, type LabelMode, type Layer, type LayerFactory, type McqCardProps, type NotationEngraving, type NotationLayout, type NotationLayoutOpts, type NotationProps, type NotationRect, type OutputProbe, PIANO_HIGH, PIANO_LOW, type PitchContourProps, type Placement, type PlayheadLine, type PortraitProps, type PromoCardsDemoOpts, type Quiz, type QuizOption, type QuizPhase, type RayEndpoints, type RecordSceneSpecOpts, type Rect, type RenderCtx, type ResolvedSegment, type RevealProps, type SafeGuidesProps, type SceneSpec, type Score, type ScoreFromMusicXMLOpts, type ScoreNote, type ScrollCursorProps, type Section, type SectionMinimapProps, type SpecLayer, type SpectrumInput, type SpectrumProps, type StaffKeyboardRayProps, type TempoMap, type TimeAnchor, type TimelineSegment, activeChord, activeCue, activeSection, animatedSlot, applySchedule, applyToContext, assertGate, ballArc, ballX, beatGrid, beatPhase, blackKeys, bpmOf, brandingFactory, buildScene, cameraForFollow, cameraTransform, circleOfFifthsDemoSpec, circleOfFifthsFactory, clamp, clickTrackSchedule, contourMinimapDemoSpec, contourPoints, contourPolyline, countInLeadSec, countInSchedule, countdownRemaining, countdownSeconds, countingDegreeDemoSpec, countingTrackFactory, cropAroundBox, ctaFactory, cubicEaseInOut, cueOpacity, degreeLabel, degreeLabelsFactory, dotAt, drawCaption, drawHighlight, droneSchedule, duckGainAt, easeIn, easeInOut, easeOut, fallingKeyboardDemoScore, fallingKeyboardDemoSpec, fallingNotesFactory, firstMeasureBox, followBoxAt, followSrcBox, followWindowStart, fracSlotPoint, frameRect, functionColor, functionalHarmonyFactory, getKeyboardLayout, getLayerFactory, getNotationEngraving, harmonyDemoSpec, highlightIntensity, hookFactory, identityCamera, inRange, invLerp, isBlackKey, kenBurns, keyCenterX, keyColumnWidth, keyRect, keySlot, keyboardFactory, keyboardLayout, lerp, lerpBox, lerpCamera, linear, mapBoxThroughLayout, mcqCardFactory, mcqDemoSpec, measureColumnsFromLayout, measureCount, measureSpanBox, measureSpans, timeToX as minimapTimeToX, msPerBeat, notationFactory, notationLayout, noteColor, noteSetXRange, parseKey, parseTimeSig, pcToSlot, pitchAt, pitchContourFactory, pitchRange, playheadLine, portraitFactory, progress01, projectPoint, promoCardsDemoSpec, quizPhase, rayEndpoints, rayPointAt, recordSceneSpec, registerLayer, registeredKeys, resolveAnchor, resolveKeyboardLayout, resolveTimeline, revealFactory, revealProgress, runGate, safeGuidesFactory, scoreFromMusicXML, scorePitchSpan, scrollCursorFactory, sectionMinimapFactory, setFollowLayoutProvider, setKeyboardLayout, setNotationEngraving, slotAngle, slotPc, slotPoint, spectrumFactory, staffAnchor, staffKeyboardRayFactory, staffRayDemoSpec, validateChordTrack, validateQuiz, validateSections, visualTimelineMs, whiteKeys, worldToViewport };
|