@modplayjs/playhead 0.0.0-stage → 0.3.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.
@@ -0,0 +1,3 @@
1
+ export { Playhead } from './playhead.js';
2
+ export type { PlaybackPosition, AudiblePosition, PlayheadOptions, } from './playhead.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ // SPDX-License-Identifier: BSD-3-Clause
2
+ // Copyright (c) 2026 Bitti09 — modplayjs contributors
3
+ // Project-original code.
4
+ // @modplayjs/playhead public API.
5
+ export { Playhead } from './playhead.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,sDAAsD;AACtD,yBAAyB;AACzB,kCAAkC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,94 @@
1
+ /** Row-resolution snapshot of the player at the moment of a pull. Matches
2
+ * the fields of `core.playState` so it can be passed straight through. */
3
+ export interface PlaybackPosition {
4
+ /** Order index (pattern position in the sequence). */
5
+ ord: number;
6
+ /** Row inside the current pattern. */
7
+ row: number;
8
+ /** Tick inside the row (0-based). */
9
+ frame: number;
10
+ /** Ticks per row (effect Fxx / Axx). */
11
+ speed: number;
12
+ /** Beats per minute (effect Fxx). */
13
+ bpm: number;
14
+ /** Elapsed playback time in ms as reported by the core. */
15
+ timeMs: number;
16
+ }
17
+ /** The position that is audible at the query time. */
18
+ export interface AudiblePosition extends PlaybackPosition {
19
+ /** Frames actually played out at the query time. */
20
+ consumedFrames: number;
21
+ /** How far into the row the playhead is, 0..1 (estimated). */
22
+ rowProgress: number;
23
+ /** How far render is ahead of the sink, in frames (ring + device). */
24
+ queuedFrames: number;
25
+ /** How old (ms) the newest mark was at the query time. */
26
+ markAgeMs: number;
27
+ }
28
+ export interface PlayheadOptions {
29
+ /** Output rate in Hz; must match `core.setSampleRate`. Default 44100. */
30
+ rate?: number;
31
+ /** History retained for backward lookups, ms. Default 5000. */
32
+ historyMs?: number;
33
+ /** Extra offset applied on top of the sink's accounting, in ms.
34
+ * Positive makes the view run LATER (use when the sink's counter is
35
+ * known to under-report the buffered audio). Default 0. */
36
+ latencyMs?: number;
37
+ /** Monotonic clock in ms. Defaults to performance.now(), else Date.now(). */
38
+ clock?: () => number;
39
+ }
40
+ export declare class Playhead {
41
+ private marks;
42
+ private rate;
43
+ private readonly historyMs;
44
+ private latencyMs;
45
+ private readonly clock;
46
+ private provider;
47
+ /** Frames per row is derived from the core's binding time model:
48
+ * ticksize = (int)(rate × 2.5 / bpm), row = ticksize × speed. */
49
+ constructor(opts?: PlayheadOptions);
50
+ /** Set the output rate (call after `core.setSampleRate`). Existing marks
51
+ * keep the row timing they were recorded with. */
52
+ setRate(hz: number): void;
53
+ /** Extra latency in ms on top of the sink's own accounting. */
54
+ setLatencyMs(ms: number): void;
55
+ getLatencyMs(): number;
56
+ /**
57
+ * Install the sink's played-frames counter. The callback runs on every
58
+ * `positionAt()`; it must be cheap (Atomic load / field read) and must
59
+ * return the count of frames that have LEFT the sink (not the count
60
+ * still buffered in it).
61
+ */
62
+ setConsumedProvider(fn: (() => number) | null): void;
63
+ /** Drop all history (new module, seek, transport restart). */
64
+ reset(): void;
65
+ /** Number of recorded marks (diagnostics). */
66
+ get markCount(): number;
67
+ /**
68
+ * Record the player state reached at the end of one pull.
69
+ * `frames` is the CUMULATIVE number of frames handed to the sink.
70
+ * Monotonic frames are the timeline; the wall clock is only used for
71
+ * history expiry and for the fallback mode.
72
+ */
73
+ mark(frames: number, pos: PlaybackPosition, atMs?: number): void;
74
+ /**
75
+ * The position audible now (or at `atMs` / at frame `atFrames` when
76
+ * given). Returns null before the first mark — callers fall back to
77
+ * `core.playState`, which is correct while nothing has been rendered.
78
+ *
79
+ * The lookup is O(log n) and allocates one small object; it is meant to
80
+ * be called from a 60 fps render loop.
81
+ */
82
+ positionAt(atMs?: number): AudiblePosition | null;
83
+ private consumedFrames;
84
+ /** frames per row from the core's binding time model. */
85
+ private rowFrames;
86
+ /** ticksize (samples per tick) — truncated, like libxmp's (int) cast. */
87
+ private tickFrames;
88
+ /** Output frame at which `pos.row` started. `pos.frame` is the index of
89
+ * the tick that just finished, so the row began `pos.frame + 1` ticks
90
+ * before the reported position. */
91
+ private rowStart;
92
+ private trim;
93
+ }
94
+ //# sourceMappingURL=playhead.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playhead.d.ts","sourceRoot":"","sources":["../src/playhead.ts"],"names":[],"mappings":"AAuBA;2EAC2E;AAC3E,MAAM,WAAW,gBAAgB;IAC/B,sDAAsD;IACtD,GAAG,EAAE,MAAM,CAAC;IACZ,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,sDAAsD;AACtD,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACvD,oDAAoD;IACpD,cAAc,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;gEAE4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAuBD,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAA+B;IAC/C;sEACkE;IAElE,YAAY,IAAI,GAAE,eAAoB,EAKrC;IAED;uDACmD;IACnD,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAExB;IAED,+DAA+D;IAC/D,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE7B;IAED,YAAY,IAAI,MAAM,CAErB;IAED;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,IAAI,GAAG,IAAI,CAEnD;IAED,8DAA8D;IAC9D,KAAK,IAAI,IAAI,CAEZ;IAED,8CAA8C;IAC9C,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED;;;;;OAKG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CA0B/D;IAED;;;;;;;OAOG;IACH,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAqEhD;IAED,OAAO,CAAC,cAAc;IAYtB,yDAAyD;IACzD,OAAO,CAAC,SAAS;IAMjB,yEAAyE;IACzE,OAAO,CAAC,UAAU;IAIlB;;wCAEoC;IACpC,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,IAAI;CAQb"}
@@ -0,0 +1,235 @@
1
+ // SPDX-License-Identifier: BSD-3-Clause
2
+ // Copyright (c) 2026 Bitti09 — modplayjs contributors
3
+ // Project-original code.
4
+ // @modplayjs/playhead — playback clock for UIs.
5
+ //
6
+ // A tracker player renders AHEAD of the speaker: out-webaudio keeps a ring
7
+ // at its high-water mark (HIGH_WATER_FRAMES = 0.68 s at 48 kHz) and a
8
+ // terminal sink adds its own buffer on top. Reading `core.playState` from a
9
+ // UI therefore shows the row the renderer is working on, not the row the
10
+ // listener hears — several rows early on a fast song.
11
+ //
12
+ // This module records (rendered-frames, position) marks while the output
13
+ // pulls `core.playBuffer`, then answers "which row was audible at time T"
14
+ // by looking up the frame the SINK has consumed. Nothing here knows about
15
+ // AudioWorklet, ALSA or a pipe: the output supplies the consumption counter
16
+ // (a provider), and the UI only asks for a position.
17
+ //
18
+ // Two modes:
19
+ // - provider mode: consumedFrames comes from the sink (exact; the ring
20
+ // depth, the device latency and pause/resume are all already in it).
21
+ // - fallback mode (no provider): consumed ≈ last mark − latency − elapsed,
22
+ // for sinks that cannot report depth (a plain pipe, a file writer).
23
+ const FALLBACK_CLOCK = () => {
24
+ // `performance` is not in the ES2022 lib (this package compiles without
25
+ // DOM/Node types); reach it through globalThis so a browser/Node runtime
26
+ // gets the monotonic clock and anything else falls back to wall time.
27
+ const perf = globalThis.performance;
28
+ if (perf !== undefined && typeof perf.now === 'function')
29
+ return perf.now();
30
+ return Date.now();
31
+ };
32
+ export class Playhead {
33
+ marks = [];
34
+ rate;
35
+ historyMs;
36
+ latencyMs;
37
+ clock;
38
+ provider = null;
39
+ /** Frames per row is derived from the core's binding time model:
40
+ * ticksize = (int)(rate × 2.5 / bpm), row = ticksize × speed. */
41
+ constructor(opts = {}) {
42
+ this.rate = opts.rate ?? 44100;
43
+ this.historyMs = opts.historyMs ?? 5000;
44
+ this.latencyMs = opts.latencyMs ?? 0;
45
+ this.clock = opts.clock ?? FALLBACK_CLOCK;
46
+ }
47
+ /** Set the output rate (call after `core.setSampleRate`). Existing marks
48
+ * keep the row timing they were recorded with. */
49
+ setRate(hz) {
50
+ if (hz > 0 && hz !== this.rate)
51
+ this.rate = hz;
52
+ }
53
+ /** Extra latency in ms on top of the sink's own accounting. */
54
+ setLatencyMs(ms) {
55
+ this.latencyMs = Number.isFinite(ms) ? ms : 0;
56
+ }
57
+ getLatencyMs() {
58
+ return this.latencyMs;
59
+ }
60
+ /**
61
+ * Install the sink's played-frames counter. The callback runs on every
62
+ * `positionAt()`; it must be cheap (Atomic load / field read) and must
63
+ * return the count of frames that have LEFT the sink (not the count
64
+ * still buffered in it).
65
+ */
66
+ setConsumedProvider(fn) {
67
+ this.provider = fn;
68
+ }
69
+ /** Drop all history (new module, seek, transport restart). */
70
+ reset() {
71
+ this.marks.length = 0;
72
+ }
73
+ /** Number of recorded marks (diagnostics). */
74
+ get markCount() {
75
+ return this.marks.length;
76
+ }
77
+ /**
78
+ * Record the player state reached at the end of one pull.
79
+ * `frames` is the CUMULATIVE number of frames handed to the sink.
80
+ * Monotonic frames are the timeline; the wall clock is only used for
81
+ * history expiry and for the fallback mode.
82
+ */
83
+ mark(frames, pos, atMs) {
84
+ const at = atMs ?? this.clock();
85
+ const last = this.marks[this.marks.length - 1];
86
+ if (last !== undefined) {
87
+ // A counter that went backwards means the transport restarted (a new
88
+ // start() resets the ring / the posted-frame counter). Start a new
89
+ // epoch instead of corrupting the lookup.
90
+ if (frames < last.frames)
91
+ this.marks.length = 0;
92
+ else if (frames === last.frames) {
93
+ // Same pull reported twice: keep the newer position.
94
+ last.pos = pos;
95
+ last.atMs = at;
96
+ last.rowFrames = this.rowFrames(pos);
97
+ last.rowStart = this.rowStart(frames, pos, last.rowFrames);
98
+ return;
99
+ }
100
+ }
101
+ const rowFrames = this.rowFrames(pos);
102
+ this.marks.push({
103
+ frames,
104
+ atMs: at,
105
+ pos,
106
+ rowFrames,
107
+ rowStart: this.rowStart(frames, pos, rowFrames),
108
+ });
109
+ this.trim(at);
110
+ }
111
+ /**
112
+ * The position audible now (or at `atMs` / at frame `atFrames` when
113
+ * given). Returns null before the first mark — callers fall back to
114
+ * `core.playState`, which is correct while nothing has been rendered.
115
+ *
116
+ * The lookup is O(log n) and allocates one small object; it is meant to
117
+ * be called from a 60 fps render loop.
118
+ */
119
+ positionAt(atMs) {
120
+ const marks = this.marks;
121
+ const len = marks.length;
122
+ if (len === 0)
123
+ return null;
124
+ const now = atMs ?? this.clock();
125
+ const last = marks[len - 1];
126
+ const first = marks[0];
127
+ let consumed = this.consumedFrames(now, last);
128
+ consumed -= Math.round((this.latencyMs * this.rate) / 1000);
129
+ let target = consumed;
130
+ // Clamp into the recorded window: before the first pull nothing is
131
+ // audible yet (the sink has not been fed), after the last pull the
132
+ // renderer has nothing newer to show.
133
+ if (target < first.frames)
134
+ target = first.frames;
135
+ if (target > last.frames)
136
+ target = last.frames;
137
+ // First mark whose cumulative frame count reaches the target: the
138
+ // target frame lies inside (marks[i-1].frames, marks[i].frames].
139
+ let lo = 0;
140
+ let hi = len - 1;
141
+ while (lo < hi) {
142
+ const mid = (lo + hi) >> 1;
143
+ if (marks[mid].frames < target)
144
+ lo = mid + 1;
145
+ else
146
+ hi = mid;
147
+ }
148
+ const m = marks[lo];
149
+ const prev = lo > 0 ? marks[lo - 1] : null;
150
+ // Resolve the row from where it STARTED, not from the pull boundary:
151
+ // a pull spans several ticks, so the last tick's row began mid-pull
152
+ // and the frames before that belonged to the previous row.
153
+ let row = m.pos.row;
154
+ let rowProgress = 1;
155
+ let timeMs = m.pos.timeMs - ((m.frames - target) / this.rate) * 1000;
156
+ if (m.rowFrames > 0) {
157
+ const intoRow = target - m.rowStart;
158
+ if (intoRow > 0) {
159
+ // Frames have been played past the row start, so this row sounds.
160
+ rowProgress = intoRow / m.rowFrames;
161
+ if (rowProgress > 1)
162
+ rowProgress = 1;
163
+ }
164
+ else {
165
+ // Target is at (or before) the row start: the newest played frame
166
+ // still belongs to the previous row. Step back whole rows at this
167
+ // tempo/speed; a tempo or speed change inside the pull makes the
168
+ // earlier rows a different length, so the previous mark bounds it.
169
+ const back = 1 + Math.floor(-intoRow / m.rowFrames);
170
+ row = m.pos.row - back;
171
+ rowProgress = (back * m.rowFrames + intoRow) / m.rowFrames;
172
+ }
173
+ if (prev !== null && row < prev.pos.row)
174
+ row = prev.pos.row;
175
+ if (row > m.pos.row)
176
+ row = m.pos.row;
177
+ if (row < 0)
178
+ row = 0; // crossed into the previous pattern: clamp
179
+ if (rowProgress < 0)
180
+ rowProgress = 0;
181
+ }
182
+ return {
183
+ ord: m.pos.ord,
184
+ row,
185
+ frame: m.pos.frame,
186
+ speed: m.pos.speed,
187
+ bpm: m.pos.bpm,
188
+ timeMs: timeMs > 0 ? timeMs : 0,
189
+ consumedFrames: consumed,
190
+ rowProgress,
191
+ queuedFrames: last.frames - consumed,
192
+ markAgeMs: now - m.atMs,
193
+ };
194
+ }
195
+ consumedFrames(now, last) {
196
+ if (this.provider !== null) {
197
+ const v = this.provider();
198
+ if (Number.isFinite(v))
199
+ return v;
200
+ }
201
+ // Fallback: assume the sink holds `latencyMs` of audio and drains at
202
+ // the output rate; clamp into the recorded window.
203
+ const elapsed = ((now - last.atMs) * this.rate) / 1000;
204
+ const v = last.frames - elapsed;
205
+ return v < 0 ? 0 : v;
206
+ }
207
+ /** frames per row from the core's binding time model. */
208
+ rowFrames(pos) {
209
+ if (!(pos.bpm > 0) || !(pos.speed > 0))
210
+ return 0;
211
+ const ticks = this.tickFrames(pos.bpm);
212
+ return ticks * pos.speed;
213
+ }
214
+ /** ticksize (samples per tick) — truncated, like libxmp's (int) cast. */
215
+ tickFrames(bpm) {
216
+ return Math.trunc((this.rate * 2.5) / bpm);
217
+ }
218
+ /** Output frame at which `pos.row` started. `pos.frame` is the index of
219
+ * the tick that just finished, so the row began `pos.frame + 1` ticks
220
+ * before the reported position. */
221
+ rowStart(frames, pos, rowFrames) {
222
+ if (rowFrames <= 0)
223
+ return frames;
224
+ return frames - (pos.frame + 1) * this.tickFrames(pos.bpm);
225
+ }
226
+ trim(now) {
227
+ const marks = this.marks;
228
+ // Keep a short tail even when the render loop stalls, and never drop
229
+ // below two marks (a lookup needs a bounding interval).
230
+ while (marks.length > 4 && now - marks[0].atMs > this.historyMs) {
231
+ marks.shift();
232
+ }
233
+ }
234
+ }
235
+ //# sourceMappingURL=playhead.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playhead.js","sourceRoot":"","sources":["../src/playhead.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,48 @@
1
1
  {
2
2
  "name": "@modplayjs/playhead",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.0",
4
+ "description": "Playback clock for modplayjs UIs: maps sink consumption time to the song row that is actually audible (pattern-view sync).",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc -p tsconfig.build.json",
19
+ "typecheck": "tsc -p tsconfig.json --noEmit"
20
+ },
21
+ "dependencies": {},
22
+ "keywords": [
23
+ "tracker",
24
+ "mod",
25
+ "libxmp",
26
+ "chiptune",
27
+ "playback-clock",
28
+ "latency",
29
+ "subtitles"
30
+ ],
31
+ "sideEffects": false,
32
+ "engines": {
33
+ "node": ">=18"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/modplayjs/modplayjs.git",
38
+ "directory": "packages/playhead"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/modplayjs/modplayjs/issues"
42
+ },
43
+ "homepage": "https://github.com/modplayjs/modplayjs#readme",
44
+ "license": "BSD-3-Clause",
45
+ "publishConfig": {
46
+ "registry": "https://npm.pkg.github.com"
47
+ }
48
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.