@sudobility/music_types 0.11.35 → 0.13.1

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 (51) hide show
  1. package/CLAUDE.md +20 -0
  2. package/dist/bytes/base64.d.ts +40 -0
  3. package/dist/bytes/base64.d.ts.map +1 -0
  4. package/dist/bytes/base64.js +101 -0
  5. package/dist/bytes/base64.js.map +1 -0
  6. package/dist/bytes/index.d.ts +2 -0
  7. package/dist/bytes/index.d.ts.map +1 -0
  8. package/dist/bytes/index.js +2 -0
  9. package/dist/bytes/index.js.map +1 -0
  10. package/dist/formats/index.d.ts +12 -0
  11. package/dist/formats/index.d.ts.map +1 -0
  12. package/dist/formats/index.js +12 -0
  13. package/dist/formats/index.js.map +1 -0
  14. package/dist/{platform → formats}/midi.d.ts +0 -4
  15. package/dist/formats/midi.d.ts.map +1 -0
  16. package/dist/{platform → formats}/midi.js.map +1 -1
  17. package/dist/{platform → formats}/mod.d.ts +0 -20
  18. package/dist/formats/mod.d.ts.map +1 -0
  19. package/dist/formats/mod.js.map +1 -0
  20. package/dist/index.d.ts +3 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +10 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/platform/index.d.ts +0 -2
  25. package/dist/platform/index.d.ts.map +1 -1
  26. package/dist/platform/index.js +0 -2
  27. package/dist/platform/index.js.map +1 -1
  28. package/dist/platform/midi-input.d.ts +4 -2
  29. package/dist/platform/midi-input.d.ts.map +1 -1
  30. package/dist/platform/midi-input.js +4 -2
  31. package/dist/platform/midi-input.js.map +1 -1
  32. package/dist/platform/playback.d.ts +9 -90
  33. package/dist/platform/playback.d.ts.map +1 -1
  34. package/dist/position/index.d.ts +3 -0
  35. package/dist/position/index.d.ts.map +1 -0
  36. package/dist/position/index.js +3 -0
  37. package/dist/position/index.js.map +1 -0
  38. package/dist/position/music-position.d.ts +63 -0
  39. package/dist/position/music-position.d.ts.map +1 -0
  40. package/dist/position/music-position.js +69 -0
  41. package/dist/position/music-position.js.map +1 -0
  42. package/dist/position/singleton.d.ts +34 -0
  43. package/dist/position/singleton.d.ts.map +1 -0
  44. package/dist/position/singleton.js +33 -0
  45. package/dist/position/singleton.js.map +1 -0
  46. package/package.json +1 -1
  47. package/dist/platform/midi.d.ts.map +0 -1
  48. package/dist/platform/mod.d.ts.map +0 -1
  49. package/dist/platform/mod.js.map +0 -1
  50. /package/dist/{platform → formats}/midi.js +0 -0
  51. /package/dist/{platform → formats}/mod.js +0 -0
package/CLAUDE.md CHANGED
@@ -39,6 +39,26 @@ Everything exports from a single sectioned `src/index.ts`:
39
39
 
40
40
  ## Gotchas
41
41
 
42
+ - **The playhead is the one stateful service in this package, and that is a
43
+ deliberate precedent.** `MusicPosition` and its singleton live in
44
+ `src/position/`. Everything else here is model and primitives; the playhead
45
+ earns the exception because it has exactly one writer
46
+ (`@sudobility/music_player`) and readers in every other package — the caret,
47
+ the note highlighting, the piano keyboard — so any other home creates a
48
+ dependency edge that exists only to reach it. It still obeys the four rules:
49
+ works on both sides, no dependency, no hooks, no async.
50
+ - **`base64.ts` is here because its two consumers ended up in different
51
+ packages.** `music_io`'s React Native file exporter encodes; `music_player`'s
52
+ sample-pack loader decodes. The module exists precisely so there is one
53
+ alphabet and one round-trip test rather than a copy each — splitting it would
54
+ break the property it was written to guarantee.
55
+ - **The playback *plan* is here; the engine *contract* is not.** `PlaybackPlan`
56
+ composes `PerformanceTimeline`, which `performanceTimeline()` in
57
+ `domain/score/` produces, so the whole plan cluster is anchored here — moving
58
+ it would make this package import from `music_player`. `PlaybackEngine`,
59
+ `PlaybackObserver` and `AuditionVoice` did move, because that package is the
60
+ only thing that implements or calls them.
61
+
42
62
  - No domain logic here: tick math, factories, commands, validation logic all live in `@sudobility/music_lib`. This package must never depend on music_lib (music_api depends on this package and must not pull in UI/audio code).
43
63
  - `noteEventSchema`/`restEventSchema` are `.strict()` on purpose (a stray `pitch` key must not pass as a rest); other schemas strip unknown keys for forward compatibility.
44
64
  - Ticks are integers at 480 PPQ by convention; `startTick` is absolute.
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Base64, both directions.
3
+ *
4
+ * Here rather than in a platform package because its two consumers now live in
5
+ * different ones — `music_io`'s React Native file exporter encodes, and
6
+ * `music_player`'s sample-pack loader decodes — and this module exists
7
+ * precisely so there is one alphabet and one round-trip test rather than a
8
+ * copy each. Pure string and byte work: no dependency, no async, nothing
9
+ * platform-bound, so it sits inside this package's four rules.
10
+ *
11
+ * Written out rather than reached for because Hermes guarantees neither `btoa`
12
+ * nor Node's `Buffer`, and the two places that need it need opposite
13
+ * directions: `file/file.rn.ts` encodes bytes to hand `react-native-fs` a
14
+ * base64 string, and `playback/pack-library.ts` decodes the base64 mp3 data
15
+ * URIs inside a FluidR3 sample pack.
16
+ *
17
+ * One module rather than a copy each, so the alphabet is defined once and the
18
+ * pair can be round-tripped against each other — which is a stronger check than
19
+ * either direction gets alone, since a matching error in both would still
20
+ * survive testing them separately.
21
+ *
22
+ * **Both are hot enough to be worth the shape they are in.** A sample pack is
23
+ * ~2.7MB of base64 decoded on the way into playback, per instrument, and an
24
+ * audio export is tens of megabytes encoded on the way out — on a phone, with
25
+ * no JIT. The obvious implementations of each were measured at 77ms and 580ms
26
+ * for those sizes on a laptop; these are 9ms and 303ms.
27
+ */
28
+ /** Bytes to base64, padded. */
29
+ export declare function bytesToBase64(bytes: Uint8Array): string;
30
+ /**
31
+ * Base64 to bytes. Anything outside the alphabet — padding, newlines a data URI
32
+ * may carry — is skipped rather than rejected.
33
+ *
34
+ * Skipped *inline* rather than stripped first: the obvious
35
+ * `replace(/[^A-Za-z0-9+/]/g, '')` copies the whole multi-megabyte string
36
+ * before any decoding starts, which on a sample pack costs as much as the
37
+ * decode itself.
38
+ */
39
+ export declare function base64ToBytes(base64: string): ArrayBuffer;
40
+ //# sourceMappingURL=base64.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../../src/bytes/base64.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAyBH,+BAA+B;AAC/B,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAmBvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAoBzD"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Base64, both directions.
3
+ *
4
+ * Here rather than in a platform package because its two consumers now live in
5
+ * different ones — `music_io`'s React Native file exporter encodes, and
6
+ * `music_player`'s sample-pack loader decodes — and this module exists
7
+ * precisely so there is one alphabet and one round-trip test rather than a
8
+ * copy each. Pure string and byte work: no dependency, no async, nothing
9
+ * platform-bound, so it sits inside this package's four rules.
10
+ *
11
+ * Written out rather than reached for because Hermes guarantees neither `btoa`
12
+ * nor Node's `Buffer`, and the two places that need it need opposite
13
+ * directions: `file/file.rn.ts` encodes bytes to hand `react-native-fs` a
14
+ * base64 string, and `playback/pack-library.ts` decodes the base64 mp3 data
15
+ * URIs inside a FluidR3 sample pack.
16
+ *
17
+ * One module rather than a copy each, so the alphabet is defined once and the
18
+ * pair can be round-tripped against each other — which is a stronger check than
19
+ * either direction gets alone, since a matching error in both would still
20
+ * survive testing them separately.
21
+ *
22
+ * **Both are hot enough to be worth the shape they are in.** A sample pack is
23
+ * ~2.7MB of base64 decoded on the way into playback, per instrument, and an
24
+ * audio export is tens of megabytes encoded on the way out — on a phone, with
25
+ * no JIT. The obvious implementations of each were measured at 77ms and 580ms
26
+ * for those sizes on a laptop; these are 9ms and 303ms.
27
+ */
28
+ const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
29
+ /**
30
+ * Character code to sextet, -1 for anything else.
31
+ *
32
+ * A table rather than `ALPHABET.indexOf(char)`, which is a linear search over
33
+ * 64 characters *per character decoded* — 8.5x the total cost on a pack-sized
34
+ * input, and worse under an interpreter than under V8.
35
+ */
36
+ const SEXTET = new Int8Array(256).fill(-1);
37
+ for (let i = 0; i < ALPHABET.length; i += 1)
38
+ SEXTET[ALPHABET.charCodeAt(i)] = i;
39
+ /**
40
+ * How much string to accumulate before pushing it into the chunk list.
41
+ *
42
+ * Growing one string four characters at a time asks the engine to keep
43
+ * re-materialising it; joining a list of moderate pieces is roughly twice as
44
+ * fast on an export-sized input. Large enough that the list stays short, small
45
+ * enough that no single piece is itself expensive to grow.
46
+ */
47
+ const ENCODE_CHUNK_CHARS = 8192;
48
+ /** Bytes to base64, padded. */
49
+ export function bytesToBase64(bytes) {
50
+ const chunks = [];
51
+ let piece = '';
52
+ for (let i = 0; i < bytes.length; i += 3) {
53
+ const a = bytes[i];
54
+ const b = bytes[i + 1];
55
+ const c = bytes[i + 2];
56
+ piece +=
57
+ ALPHABET[a >> 2] +
58
+ ALPHABET[((a & 0x03) << 4) | ((b ?? 0) >> 4)] +
59
+ (b === undefined ? '=' : ALPHABET[((b & 0x0f) << 2) | ((c ?? 0) >> 6)]) +
60
+ (c === undefined ? '=' : ALPHABET[c & 0x3f]);
61
+ if (piece.length >= ENCODE_CHUNK_CHARS) {
62
+ chunks.push(piece);
63
+ piece = '';
64
+ }
65
+ }
66
+ if (piece)
67
+ chunks.push(piece);
68
+ return chunks.join('');
69
+ }
70
+ /**
71
+ * Base64 to bytes. Anything outside the alphabet — padding, newlines a data URI
72
+ * may carry — is skipped rather than rejected.
73
+ *
74
+ * Skipped *inline* rather than stripped first: the obvious
75
+ * `replace(/[^A-Za-z0-9+/]/g, '')` copies the whole multi-megabyte string
76
+ * before any decoding starts, which on a sample pack costs as much as the
77
+ * decode itself.
78
+ */
79
+ export function base64ToBytes(base64) {
80
+ // An upper bound — exact when there is no padding or whitespace, over by at
81
+ // most a couple of bytes when there is. Trimmed at the end rather than
82
+ // counted in a first pass, which would be another walk over megabytes.
83
+ const out = new Uint8Array(((base64.length * 3) >> 2) + 3);
84
+ let outIndex = 0;
85
+ let accumulator = 0;
86
+ let bits = 0;
87
+ for (let i = 0; i < base64.length; i += 1) {
88
+ const sextet = SEXTET[base64.charCodeAt(i) & 0xff];
89
+ if (sextet < 0)
90
+ continue;
91
+ accumulator = (accumulator << 6) | sextet;
92
+ bits += 6;
93
+ if (bits >= 8) {
94
+ bits -= 8;
95
+ out[outIndex] = (accumulator >> bits) & 0xff;
96
+ outIndex += 1;
97
+ }
98
+ }
99
+ return out.buffer.slice(0, outIndex);
100
+ }
101
+ //# sourceMappingURL=base64.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64.js","sourceRoot":"","sources":["../../src/bytes/base64.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from "./base64.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bytes/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from "./base64.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bytes/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Score file-format models: the neutral shapes a MIDI file and a tracker
3
+ * module decode into.
4
+ *
5
+ * Not under `platform/`, deliberately. These carry notes rather than samples,
6
+ * so nothing about them is platform-bound — the codecs that produce them live
7
+ * in `@sudobility/music_codecs` and run identically on web, React Native and
8
+ * the server.
9
+ */
10
+ export * from "./midi.js";
11
+ export * from "./mod.js";
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/formats/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Score file-format models: the neutral shapes a MIDI file and a tracker
3
+ * module decode into.
4
+ *
5
+ * Not under `platform/`, deliberately. These carry notes rather than samples,
6
+ * so nothing about them is platform-bound — the codecs that produce them live
7
+ * in `@sudobility/music_codecs` and run identically on web, React Native and
8
+ * the server.
9
+ */
10
+ export * from "./midi.js";
11
+ export * from "./mod.js";
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/formats/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
@@ -52,8 +52,4 @@ export type MidiFile = {
52
52
  /** Longest track duration in seconds. */
53
53
  duration: number;
54
54
  };
55
- export interface MidiCodec {
56
- decode(data: ArrayBuffer): MidiFile;
57
- encode(file: MidiFile): Uint8Array;
58
- }
59
55
  //# sourceMappingURL=midi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"midi.d.ts","sourceRoot":"","sources":["../../src/formats/midi.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AACF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AACF,MAAM,MAAM,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAC5D,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;IACpD,aAAa,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,cAAc,EAAE,sBAAsB,EAAE,CAAC;KAC1C,CAAC;IACF,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"midi.js","sourceRoot":"","sources":["../../src/platform/midi.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"midi.js","sourceRoot":"","sources":["../../src/formats/midi.ts"],"names":[],"mappings":""}
@@ -54,24 +54,4 @@ export type TrackerModule = {
54
54
  /** `patterns[p][row][channel]`. Row count varies per pattern — MOD is always 64, IT allows 200. */
55
55
  patterns: TrackerCell[][][];
56
56
  };
57
- /**
58
- * Reading a tracker module.
59
- *
60
- * A capability for surface consistency — the app reaches every file format
61
- * through `getAppServices().io` — even though nothing about parsing is
62
- * platform-bound. All three implementations delegate to one shared module.
63
- */
64
- export interface TrackerCodec {
65
- /** Sniffs the format from the bytes and decodes it. Throws on anything that is not one, rather than returning a garbage module that looks imported. */
66
- decode(bytes: ArrayBuffer): TrackerModule;
67
- /**
68
- * Writes a module out, in the format `module.format` names.
69
- *
70
- * No sample data is written: a module is a notes format, so the slots are
71
- * named and empty and the file is silent until somebody fills them. Throws
72
- * for a format export does not write (`dsm`, `mptm`) rather than guessing a
73
- * near neighbour.
74
- */
75
- encode(module: TrackerModule): ArrayBuffer;
76
- }
77
57
  //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/formats/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,MAAM,CAAC;AAEzE,2GAA2G;AAC3G,MAAM,MAAM,iBAAiB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;IAC5B,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,2FAA2F;IAC3F,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,mGAAmG;IACnG,QAAQ,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;CAC7B,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.js","sourceRoot":"","sources":["../../src/formats/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
package/dist/index.d.ts CHANGED
@@ -28,6 +28,9 @@ export * from "./model/generation.js";
28
28
  export * from "./model/api.js";
29
29
  export * from "./model/position.js";
30
30
  export * from "./model/selection-source.js";
31
+ export * from "./position/index.js";
32
+ export * from "./bytes/index.js";
33
+ export * from "./formats/index.js";
31
34
  export * from "./platform/index.js";
32
35
  export * from "./domain/pitch/pitch.js";
33
36
  export * from "./domain/pitch/transpose.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AASH,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAK5C,cAAc,qBAAqB,CAAC;AAMpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AAMjD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,oCAAoC,CAAC;AACnD,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,wCAAwC,CAAC;AACvD,cAAc,sCAAsC,CAAC;AACrD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,kCAAkC,CAAC;AACjD,cAAc,sCAAsC,CAAC;AACrD,cAAc,0CAA0C,CAAC;AACzD,cAAc,uCAAuC,CAAC;AACtD,cAAc,iCAAiC,CAAC;AAChD,cAAc,uCAAuC,CAAC;AAItD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,wCAAwC,CAAC;AACvD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,0CAA0C,CAAC;AACzD,cAAc,mCAAmC,CAAC;AAClD,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,wCAAwC,CAAC;AACvD,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AASH,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAQ5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AAEjC,cAAc,oBAAoB,CAAC;AAKnC,cAAc,qBAAqB,CAAC;AAMpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AAMjD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,oCAAoC,CAAC;AACnD,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,wCAAwC,CAAC;AACvD,cAAc,sCAAsC,CAAC;AACrD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,kCAAkC,CAAC;AACjD,cAAc,sCAAsC,CAAC;AACrD,cAAc,0CAA0C,CAAC;AACzD,cAAc,uCAAuC,CAAC;AACtD,cAAc,iCAAiC,CAAC;AAChD,cAAc,uCAAuC,CAAC;AAItD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,wCAAwC,CAAC;AACvD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,0CAA0C,CAAC;AACzD,cAAc,mCAAmC,CAAC;AAClD,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,wCAAwC,CAAC;AACvD,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -36,7 +36,16 @@ export * from "./model/api.js";
36
36
  export * from "./model/position.js";
37
37
  export * from "./model/selection-source.js";
38
38
  // ---------------------------------------------------------------------------
39
- // 10. Platform interfaces (implementations live in @sudobility/music_io)
39
+ // 10. Score file-format models (codecs live in @sudobility/music_codecs)
40
+ // ---------------------------------------------------------------------------
41
+ // ---------------------------------------------------------------------------
42
+ // Playhead — the one stateful service here; see position/music-position.ts
43
+ // ---------------------------------------------------------------------------
44
+ export * from "./position/index.js";
45
+ export * from "./bytes/index.js";
46
+ export * from "./formats/index.js";
47
+ // ---------------------------------------------------------------------------
48
+ // 11. Platform interfaces (implementations live in @sudobility/music_io)
40
49
  // ---------------------------------------------------------------------------
41
50
  export * from "./platform/index.js";
42
51
  // ---------------------------------------------------------------------------
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,8EAA8E;AAC9E,+CAA+C;AAC/C,EAAE;AACF,8EAA8E;AAC9E,sEAAsE;AACtE,iEAAiE;AACjE,8EAA8E;AAC9E,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAE5C,8EAA8E;AAC9E,yEAAyE;AACzE,8EAA8E;AAC9E,cAAc,qBAAqB,CAAC;AAEpC,8EAA8E;AAC9E,8DAA8D;AAC9D,8EAA8E;AAE9E,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AAEjD,8EAA8E;AAC9E,iFAAiF;AACjF,8EAA8E;AAE9E,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,oCAAoC,CAAC;AACnD,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,wCAAwC,CAAC;AACvD,cAAc,sCAAsC,CAAC;AACrD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,kCAAkC,CAAC;AACjD,cAAc,sCAAsC,CAAC;AACrD,cAAc,0CAA0C,CAAC;AACzD,cAAc,uCAAuC,CAAC;AACtD,cAAc,iCAAiC,CAAC;AAChD,cAAc,uCAAuC,CAAC;AAEtD,yEAAyE;AACzE,yCAAyC;AACzC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,wCAAwC,CAAC;AACvD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,0CAA0C,CAAC;AACzD,cAAc,mCAAmC,CAAC;AAClD,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,wCAAwC,CAAC;AACvD,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -7,7 +7,5 @@ export * from "./audio.js";
7
7
  export * from "./playback.js";
8
8
  export * from "./xml.js";
9
9
  export * from "./file.js";
10
- export * from "./midi.js";
11
10
  export * from "./midi-input.js";
12
- export * from "./mod.js";
13
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/platform/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B;;;;GAIG;AACH,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/platform/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B;;;;GAIG;AACH,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC"}
@@ -7,7 +7,5 @@ export * from "./audio.js";
7
7
  export * from "./playback.js";
8
8
  export * from "./xml.js";
9
9
  export * from "./file.js";
10
- export * from "./midi.js";
11
10
  export * from "./midi-input.js";
12
- export * from "./mod.js";
13
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/platform/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B;;;;GAIG;AACH,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/platform/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B;;;;GAIG;AACH,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC"}
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * Playing notes in from a MIDI keyboard.
3
3
  *
4
- * Separate from `MidiCodec`, which reads and writes `.mid` files: this is a
5
- * live input device, and the two have nothing in common but the word MIDI.
4
+ * Separate from the `.mid` file codec in `@sudobility/music_codecs`: reading a
5
+ * file is byte arithmetic that runs anywhere, while this is a live input
6
+ * device that only a platform can offer. The two have nothing in common but
7
+ * the word MIDI, which is why only this one is a capability.
6
8
  *
7
9
  * A capability rather than a direct Web MIDI call, for the same reason
8
10
  * playback is one: the browser has `navigator.requestMIDIAccess`, React Native
@@ -1 +1 @@
1
- {"version":3,"file":"midi-input.d.ts","sourceRoot":"","sources":["../../src/platform/midi-input.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,+BAA+B;AAC/B,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;AAE/D,MAAM,MAAM,SAAS,GAAG;IACtB,yDAAyD;IACzD,WAAW,IAAI,OAAO,CAAC;IAEvB;;;;;;OAMG;IACH,WAAW,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAE1C;;;;;;OAMG;IACH,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,IAAI,CAAC;CACrE,CAAC"}
1
+ {"version":3,"file":"midi-input.d.ts","sourceRoot":"","sources":["../../src/platform/midi-input.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,+BAA+B;AAC/B,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;AAE/D,MAAM,MAAM,SAAS,GAAG;IACtB,yDAAyD;IACzD,WAAW,IAAI,OAAO,CAAC;IAEvB;;;;;;OAMG;IACH,WAAW,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAE1C;;;;;;OAMG;IACH,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,IAAI,CAAC;CACrE,CAAC"}
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * Playing notes in from a MIDI keyboard.
3
3
  *
4
- * Separate from `MidiCodec`, which reads and writes `.mid` files: this is a
5
- * live input device, and the two have nothing in common but the word MIDI.
4
+ * Separate from the `.mid` file codec in `@sudobility/music_codecs`: reading a
5
+ * file is byte arithmetic that runs anywhere, while this is a live input
6
+ * device that only a platform can offer. The two have nothing in common but
7
+ * the word MIDI, which is why only this one is a capability.
6
8
  *
7
9
  * A capability rather than a direct Web MIDI call, for the same reason
8
10
  * playback is one: the browser has `navigator.requestMIDIAccess`, React Native
@@ -1 +1 @@
1
- {"version":3,"file":"midi-input.js","sourceRoot":"","sources":["../../src/platform/midi-input.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
1
+ {"version":3,"file":"midi-input.js","sourceRoot":"","sources":["../../src/platform/midi-input.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
@@ -7,7 +7,6 @@
7
7
  * things that import an audio library; everything that depends on playback
8
8
  * depends on this file instead, so the engine stays swappable and mockable.
9
9
  */
10
- import type { ScoreRange } from "../index.js";
11
10
  import type { RenderTrack } from "./audio.js";
12
11
  export type TransportPlaybackState = "stopped" | "playing" | "paused";
13
12
  /**
@@ -24,29 +23,6 @@ export type SoundingNote = {
24
23
  /** Its sounding MIDI pitch, so a keyboard can light a key without resolving the event. */
25
24
  midi: number;
26
25
  };
27
- export type PlaybackObserver = {
28
- onPositionTick(tick: number): void;
29
- /**
30
- * The notes currently sounding, whenever that set changes.
31
- *
32
- * Resolved rather than bare ids. The scheduler already knows each note's
33
- * track and pitch — it scheduled them — and emitting only the id forced every
34
- * consumer to search the whole score to get it back: `playingPitchesForTrack`
35
- * was an O(score) scan per sounding note, twenty times a second.
36
- */
37
- onActiveNotes(notes: SoundingNote[]): void;
38
- onStateChange(state: TransportPlaybackState): void;
39
- /**
40
- * How far along the engine is in becoming able to make a sound.
41
- *
42
- * Optional because not every engine has anything to load. The soundfont
43
- * engine does: tens of megabytes to fetch and several seconds for the synth
44
- * to digest, all on the first press of Play. Without this the transport
45
- * simply looks broken for that whole time — which is exactly how it read
46
- * before there was anywhere to report it.
47
- */
48
- onLoadStateChange?(state: PlaybackLoadState): void;
49
- };
50
26
  /**
51
27
  * The engine's readiness to play, for a progress indicator.
52
28
  *
@@ -77,20 +53,6 @@ export type PlaybackTrack = RenderTrack & {
77
53
  muted: boolean;
78
54
  solo: boolean;
79
55
  };
80
- /**
81
- * A voice to audition, resolved from the GM tables **by the caller**.
82
- *
83
- * The engine cannot resolve it: a percussion voice's program addresses a drum
84
- * kit, and only the GM tables know which kit an arbitrary address falls in.
85
- * Resolving here is what lets the platform layer keep no catalogue of its own.
86
- */
87
- export type AuditionVoice = {
88
- /** The kit's program on a percussion voice, the instrument's own otherwise. */
89
- program: number;
90
- /** The GM catalogue name for `program`. */
91
- name: string;
92
- isPercussion: boolean;
93
- };
94
56
  /** One playback-ready note, in score ticks, carrying the ids playback reports back. */
95
57
  export type PlaybackNote = {
96
58
  tick: number;
@@ -135,6 +97,15 @@ export type PerformanceTimeline = {
135
97
  segments: readonly TimelineSegment[];
136
98
  durationTicks: number;
137
99
  };
100
+ /**
101
+ * The engine contract — `PlaybackEngine`, `PlaybackObserver` and
102
+ * `AuditionVoice` — lives in `@sudobility/music_player`, which is the only
103
+ * thing that implements it.
104
+ *
105
+ * The *plan* stays here, and must: `PlaybackPlan` composes `PerformanceTimeline`,
106
+ * which `performanceTimeline()` in `domain/score/` produces. Moving it would
107
+ * make this package import from music_player.
108
+ */
138
109
  export type PlaybackPlan = {
139
110
  tracks: readonly PlaybackTrack[];
140
111
  notes: readonly PlaybackNote[];
@@ -156,56 +127,4 @@ export type PlaybackLoadState = {
156
127
  status: "failed";
157
128
  message: string;
158
129
  };
159
- export interface PlaybackEngine {
160
- initialize(): Promise<void>;
161
- /** Adopts a plan. The engine is handed music, never a score. */
162
- load(plan: PlaybackPlan): Promise<void>;
163
- play(fromTick?: number): Promise<void>;
164
- pause(): void;
165
- stop(): void;
166
- seek(tick: number): void;
167
- setTempoMultiplier(multiplier: number): void;
168
- setLoop(range: ScoreRange | null): void;
169
- setTrackMute(trackId: string, muted: boolean): void;
170
- setTrackSolo(trackId: string, solo: boolean): void;
171
- /**
172
- * Re-reads every track's volume, pan, mute and solo and pushes them,
173
- * touching nothing that is scheduled.
174
- *
175
- * The mixing counterpart to `load`. Mute and solo had setters; volume and pan
176
- * did not, and a track's volume was only ever read at load time — so once a
177
- * mix change stopped reloading the score (the playback edit lock, see
178
- * `music_lib`'s `score-slice`), moving a fader during playback moved the
179
- * fader and not the sound.
180
- *
181
- * Takes only the tracks, so changing a gain does not rebuild every note —
182
- * which is the whole reason this is separate from `load`. Idempotent, and
183
- * takes them all rather than one property: the caller says "the mix changed,
184
- * here it is" and does not work out which property it was.
185
- */
186
- applyMix(tracks: readonly PlaybackTrack[]): void;
187
- /** Toggles the metronome click. */
188
- setMetronome(enabled: boolean): void;
189
- /** Sets overall output level, 0-1 linear gain. */
190
- setMasterVolume(volume: number): void;
191
- /**
192
- * Sounds `midi` immediately on `program`'s voice and holds it, independent of
193
- * the transport — for auditioning a key while editing.
194
- *
195
- * `voice.isPercussion` mirrors what playback does with a percussion-clef
196
- * track: without it, tapping a note on a drum track auditioned a pitched
197
- * instrument while the same note played back as a drum.
198
- *
199
- * Separate from `play` because the two answer different questions: `play`
200
- * renders the written score along a timeline, this makes a sound *now*, for
201
- * as long as the caller holds it, whether or not a score is even loaded.
202
- * Implementations must not disturb transport state.
203
- */
204
- noteOn(midi: number, voice: AuditionVoice): void;
205
- /** Releases a pitch started by `noteOn`. Silent no-op if it is not sounding. */
206
- noteOff(midi: number): void;
207
- /** Registers (or, with `null`, clears) the single observer receiving position/active-note/state updates. */
208
- setObserver(observer: PlaybackObserver | null): void;
209
- dispose(): void;
210
- }
211
130
  //# sourceMappingURL=playback.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"playback.d.ts","sourceRoot":"","sources":["../../src/platform/playback.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"playback.d.ts","sourceRoot":"","sources":["../../src/platform/playback.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEtE;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,0FAA0F;IAC1F,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAGF;;;;;;GAMG;AACH;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACrC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CACzC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG;IACxC,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAGF,uFAAuF;AACvF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,iEAAiE;AACjE,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAE/D;;;;;;;GAOG;AACH,yEAAyE;AACzE,MAAM,MAAM,eAAe,GAAG;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IACrC,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC;IACjC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IAC/B,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAClC,KAAK,EAAE,eAAe,CAAC;IACvB,mEAAmE;IACnE,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GACzB;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAClB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC9C;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GACnB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./music-position.js";
2
+ export * from "./singleton.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/position/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./music-position.js";
2
+ export * from "./singleton.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/position/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The one place that knows where playback is.
3
+ *
4
+ * **The first stateful singleton service in music_types, and deliberately so.**
5
+ * This package is otherwise model and primitives. The playhead earns the
6
+ * exception because it has exactly one writer — `@sudobility/music_player` —
7
+ * and readers in every other package: the caret, the note highlighting and the
8
+ * piano keyboard. Any other home creates a dependency edge that exists only to
9
+ * reach it. It still obeys the four rules this package keeps: it works on both
10
+ * the frontend and the backend, adds no dependency, contains no hooks and
11
+ * contains no async code.
12
+ *
13
+ * Implements `IMusicPosition` — see that interface for why this exists at all.
14
+ * The short version: the caret, the note highlighting and the piano keyboard
15
+ * used to derive the playhead three different ways and drifted apart under
16
+ * load. Now they read one number.
17
+ *
18
+ * **The smoothing lives here, not in the caret.** That is the load-bearing
19
+ * decision. The engine reports position about thirty times a second, and those
20
+ * reports arrive in clumps because they come off the audio scheduler's
21
+ * lookahead loop rather than a wall clock — driving anything straight off them
22
+ * leaves it stalled on most frames and jumping when it does move. So the
23
+ * playhead is dead-reckoned forward from the last report, and because that
24
+ * happens *inside the single source of truth*, every consumer that asks gets
25
+ * the same smoothed answer at the same instant. When the caret did its own
26
+ * dead-reckoning, it was smoothing a number nobody else was smoothing.
27
+ *
28
+ * The anchor is the engine's own clock reading, not `performance.now()` at the
29
+ * moment the report was handled. Anchoring on receipt folds event-loop latency
30
+ * into the playhead: under load the anchor is stamped late and everything
31
+ * derived from it lags the audio, which is exactly the drift this replaces.
32
+ */
33
+ import type { IMusicPositionSource, UnsubscribePosition } from '../model/position.js';
34
+ export declare class MusicPosition implements IMusicPositionSource {
35
+ private readonly listeners;
36
+ /** The last authoritative report, and the clock reading it belongs to. */
37
+ private anchorTick;
38
+ private anchorSeconds;
39
+ private playing;
40
+ private ticksPerSecond;
41
+ get isPlaying(): boolean;
42
+ /**
43
+ * The playhead now.
44
+ *
45
+ * While stopped this is exactly the last reported tick — a paused caret must
46
+ * not creep. While playing it is the last report projected forward by the
47
+ * time since it was taken, which is what makes motion even however unevenly
48
+ * the reports land.
49
+ */
50
+ get tick(): number;
51
+ report(tick: number, atSeconds?: number): void;
52
+ /**
53
+ * Update the tick rate without disturbing the anchor.
54
+ *
55
+ * Deliberately not folded into `report`: re-anchoring is `report`'s job and
56
+ * a rate change must not do it, or a tempo change mid-bar would restart the
57
+ * projection from wherever the last report left off.
58
+ */
59
+ setRate(ticksPerSecond: number): void;
60
+ setPlaying(playing: boolean, ticksPerSecond?: number): void;
61
+ subscribe(listener: (tick: number) => void): UnsubscribePosition;
62
+ }
63
+ //# sourceMappingURL=music-position.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"music-position.d.ts","sourceRoot":"","sources":["../../src/position/music-position.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,sBAAsB,CAAC;AAS9B,qBAAa,aAAc,YAAW,oBAAoB;IACxD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqC;IAE/D,0EAA0E;IAC1E,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,aAAa,CAAgB;IAErC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAK;IAE3B,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;;;;;;OAOG;IACH,IAAI,IAAI,IAAI,MAAM,CASjB;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAM9C;;;;;;OAMG;IACH,OAAO,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAIrC,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI;IAY3D,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,mBAAmB;CAIjE"}
@@ -0,0 +1,69 @@
1
+ /** Wall-clock seconds, monotonic where the platform offers it. */
2
+ function nowSeconds() {
3
+ return typeof performance !== 'undefined'
4
+ ? performance.now() / 1000
5
+ : Date.now() / 1000;
6
+ }
7
+ export class MusicPosition {
8
+ constructor() {
9
+ this.listeners = new Set();
10
+ /** The last authoritative report, and the clock reading it belongs to. */
11
+ this.anchorTick = 0;
12
+ this.anchorSeconds = nowSeconds();
13
+ this.playing = false;
14
+ this.ticksPerSecond = 0;
15
+ }
16
+ get isPlaying() {
17
+ return this.playing;
18
+ }
19
+ /**
20
+ * The playhead now.
21
+ *
22
+ * While stopped this is exactly the last reported tick — a paused caret must
23
+ * not creep. While playing it is the last report projected forward by the
24
+ * time since it was taken, which is what makes motion even however unevenly
25
+ * the reports land.
26
+ */
27
+ get tick() {
28
+ if (!this.playing || this.ticksPerSecond <= 0)
29
+ return this.anchorTick;
30
+ const elapsed = nowSeconds() - this.anchorSeconds;
31
+ // Never backwards: a report that arrives late would otherwise pull the
32
+ // playhead back a few ticks, which reads as a stutter.
33
+ return Math.max(this.anchorTick, this.anchorTick + elapsed * this.ticksPerSecond);
34
+ }
35
+ report(tick, atSeconds) {
36
+ this.anchorTick = tick;
37
+ this.anchorSeconds = atSeconds ?? nowSeconds();
38
+ for (const listener of this.listeners)
39
+ listener(tick);
40
+ }
41
+ /**
42
+ * Update the tick rate without disturbing the anchor.
43
+ *
44
+ * Deliberately not folded into `report`: re-anchoring is `report`'s job and
45
+ * a rate change must not do it, or a tempo change mid-bar would restart the
46
+ * projection from wherever the last report left off.
47
+ */
48
+ setRate(ticksPerSecond) {
49
+ this.ticksPerSecond = Math.max(0, ticksPerSecond);
50
+ }
51
+ setPlaying(playing, ticksPerSecond) {
52
+ // Re-anchor on the transition, or the first `tick` read after resuming
53
+ // would project forward from however long the transport sat paused.
54
+ if (playing !== this.playing) {
55
+ this.anchorTick = this.tick;
56
+ this.anchorSeconds = nowSeconds();
57
+ }
58
+ this.playing = playing;
59
+ if (ticksPerSecond !== undefined)
60
+ this.ticksPerSecond = ticksPerSecond;
61
+ for (const listener of this.listeners)
62
+ listener(this.anchorTick);
63
+ }
64
+ subscribe(listener) {
65
+ this.listeners.add(listener);
66
+ return () => this.listeners.delete(listener);
67
+ }
68
+ }
69
+ //# sourceMappingURL=music-position.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"music-position.js","sourceRoot":"","sources":["../../src/position/music-position.ts"],"names":[],"mappings":"AAqCA,kEAAkE;AAClE,SAAS,UAAU;IACjB,OAAO,OAAO,WAAW,KAAK,WAAW;QACvC,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,IAAI;QAC1B,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AACxB,CAAC;AAED,MAAM,OAAO,aAAa;IAA1B;QACmB,cAAS,GAAG,IAAI,GAAG,EAA0B,CAAC;QAE/D,0EAA0E;QAClE,eAAU,GAAG,CAAC,CAAC;QACf,kBAAa,GAAG,UAAU,EAAE,CAAC;QAE7B,YAAO,GAAG,KAAK,CAAC;QAChB,mBAAc,GAAG,CAAC,CAAC;IA0D7B,CAAC;IAxDC,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;;;;;;OAOG;IACH,IAAI,IAAI;QACN,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,cAAc,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC,UAAU,CAAC;QACtE,MAAM,OAAO,GAAG,UAAU,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC;QAClD,uEAAuE;QACvE,uDAAuD;QACvD,OAAO,IAAI,CAAC,GAAG,CACb,IAAI,CAAC,UAAU,EACf,IAAI,CAAC,UAAU,GAAG,OAAO,GAAG,IAAI,CAAC,cAAc,CAChD,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,SAAkB;QACrC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,SAAS,IAAI,UAAU,EAAE,CAAC;QAC/C,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS;YAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IACxD,CAAC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,cAAsB;QAC5B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;IACpD,CAAC;IAED,UAAU,CAAC,OAAgB,EAAE,cAAuB;QAClD,uEAAuE;QACvE,oEAAoE;QACpE,IAAI,OAAO,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;YAC5B,IAAI,CAAC,aAAa,GAAG,UAAU,EAAE,CAAC;QACpC,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,cAAc,KAAK,SAAS;YAAE,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACvE,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS;YAAE,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACnE,CAAC;IAED,SAAS,CAAC,QAAgC;QACxC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/C,CAAC;CACF"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The music-position singleton.
3
+ *
4
+ * Same shape as the rest of the family's services (see `@sudobility/di`'s
5
+ * storage and network singletons): initialise once at start-up, read it
6
+ * everywhere, reset it in tests.
7
+ *
8
+ * A singleton because the playhead *is* global to a running transport — there
9
+ * is one piece of music playing, and the whole point of this type is that
10
+ * everything following it reads the same number. Two instances would
11
+ * reintroduce exactly the disagreement it exists to prevent.
12
+ */
13
+ import type { IMusicPosition, IMusicPositionSource } from '../model/position.js';
14
+ /**
15
+ * Creates the singleton if it does not exist.
16
+ *
17
+ * Idempotent, so a second call from a re-mounting composition root does not
18
+ * silently swap the playhead out from under everything subscribed to it.
19
+ */
20
+ export declare function initializeMusicPosition(override?: IMusicPositionSource): IMusicPositionSource;
21
+ /**
22
+ * The writable playhead, for whatever is driving the transport.
23
+ *
24
+ * Auto-initialises rather than throwing: unlike a storage service there is no
25
+ * configuration to get wrong, and a playhead that refuses to exist until
26
+ * somebody remembers a start-up call is a worse failure than one that starts
27
+ * at zero.
28
+ */
29
+ export declare function getMusicPositionSource(): IMusicPositionSource;
30
+ /** The read-only playhead, for anything that follows the music. */
31
+ export declare function getMusicPosition(): IMusicPosition;
32
+ /** Drops the singleton. Tests only — a suite must not inherit the last one's playhead. */
33
+ export declare function resetMusicPosition(): void;
34
+ //# sourceMappingURL=singleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singleton.d.ts","sourceRoot":"","sources":["../../src/position/singleton.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EACV,cAAc,EACd,oBAAoB,EACrB,MAAM,sBAAsB,CAAC;AAK9B;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,oBAAoB,CAGtB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,IAAI,oBAAoB,CAE7D;AAED,mEAAmE;AACnE,wBAAgB,gBAAgB,IAAI,cAAc,CAEjD;AAED,0FAA0F;AAC1F,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
@@ -0,0 +1,33 @@
1
+ import { MusicPosition } from './music-position.js';
2
+ let instance = null;
3
+ /**
4
+ * Creates the singleton if it does not exist.
5
+ *
6
+ * Idempotent, so a second call from a re-mounting composition root does not
7
+ * silently swap the playhead out from under everything subscribed to it.
8
+ */
9
+ export function initializeMusicPosition(override) {
10
+ if (!instance)
11
+ instance = override ?? new MusicPosition();
12
+ return instance;
13
+ }
14
+ /**
15
+ * The writable playhead, for whatever is driving the transport.
16
+ *
17
+ * Auto-initialises rather than throwing: unlike a storage service there is no
18
+ * configuration to get wrong, and a playhead that refuses to exist until
19
+ * somebody remembers a start-up call is a worse failure than one that starts
20
+ * at zero.
21
+ */
22
+ export function getMusicPositionSource() {
23
+ return initializeMusicPosition();
24
+ }
25
+ /** The read-only playhead, for anything that follows the music. */
26
+ export function getMusicPosition() {
27
+ return initializeMusicPosition();
28
+ }
29
+ /** Drops the singleton. Tests only — a suite must not inherit the last one's playhead. */
30
+ export function resetMusicPosition() {
31
+ instance = null;
32
+ }
33
+ //# sourceMappingURL=singleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singleton.js","sourceRoot":"","sources":["../../src/position/singleton.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,IAAI,QAAQ,GAAgC,IAAI,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,QAA+B;IAE/B,IAAI,CAAC,QAAQ;QAAE,QAAQ,GAAG,QAAQ,IAAI,IAAI,aAAa,EAAE,CAAC;IAC1D,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,uBAAuB,EAAE,CAAC;AACnC,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,gBAAgB;IAC9B,OAAO,uBAAuB,EAAE,CAAC;AACnC,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,kBAAkB;IAChC,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sudobility/music_types",
3
- "version": "0.11.35",
3
+ "version": "0.13.1",
4
4
  "description": "TypeScript types and Zod schemas for the ScoreSmith music API - scores, generation contracts, project payloads",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1 +0,0 @@
1
- {"version":3,"file":"midi.d.ts","sourceRoot":"","sources":["../../src/platform/midi.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AACF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AACF,MAAM,MAAM,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAC5D,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;IACpD,aAAa,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,cAAc,EAAE,sBAAsB,EAAE,CAAC;KAC1C,CAAC;IACF,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ,CAAC;IACpC,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;CACpC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/platform/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,MAAM,CAAC;AAEzE,2GAA2G;AAC3G,MAAM,MAAM,iBAAiB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;IAC5B,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,2FAA2F;IAC3F,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,mGAAmG;IACnG,QAAQ,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;CAC7B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,uJAAuJ;IACvJ,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,aAAa,CAAC;IAC1C;;;;;;;OAOG;IACH,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC;CAC5C"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mod.js","sourceRoot":"","sources":["../../src/platform/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
File without changes
File without changes