waa-play 0.1.1 → 0.2.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.
- package/README.md +40 -123
- package/dist/adapters.cjs +6 -6
- package/dist/adapters.d.cts +15 -3
- package/dist/adapters.d.ts +15 -3
- package/dist/adapters.js +1 -1
- package/dist/buffer.cjs +5 -5
- package/dist/buffer.d.cts +1 -1
- package/dist/buffer.d.ts +1 -1
- package/dist/buffer.js +1 -1
- package/dist/{chunk-T74FBKTY.js → chunk-2FGUFHZM.js} +2 -2
- package/dist/chunk-2FGUFHZM.js.map +1 -0
- package/dist/{chunk-CPAT75WD.cjs → chunk-3VTU5OX5.cjs} +2 -2
- package/dist/chunk-3VTU5OX5.cjs.map +1 -0
- package/dist/{chunk-2DL7CAEP.js → chunk-7JUVBZ6B.js} +2 -2
- package/dist/chunk-7JUVBZ6B.js.map +1 -0
- package/dist/{chunk-D5CD5KQZ.cjs → chunk-BRS7LZVH.cjs} +2 -2
- package/dist/chunk-BRS7LZVH.cjs.map +1 -0
- package/dist/{chunk-QWNV2BZ5.cjs → chunk-F6WXD3XW.cjs} +2 -2
- package/dist/chunk-F6WXD3XW.cjs.map +1 -0
- package/dist/{chunk-C2ASIYN5.js → chunk-FESPIMZM.js} +3 -7
- package/dist/chunk-FESPIMZM.js.map +1 -0
- package/dist/{chunk-GYH2JSCY.js → chunk-FY273Z3I.js} +2 -2
- package/dist/chunk-FY273Z3I.js.map +1 -0
- package/dist/{chunk-TULV7V5M.cjs → chunk-G37HMZEX.cjs} +1075 -982
- package/dist/chunk-G37HMZEX.cjs.map +1 -0
- package/dist/{chunk-V2QX5K42.js → chunk-GDBOHOGF.js} +1074 -982
- package/dist/chunk-GDBOHOGF.js.map +1 -0
- package/dist/{chunk-5J7S6QV3.cjs → chunk-HIF3UAF3.cjs} +2 -2
- package/dist/chunk-HIF3UAF3.cjs.map +1 -0
- package/dist/{chunk-CRODJ4KS.js → chunk-HTN52U23.js} +13 -6
- package/dist/chunk-HTN52U23.js.map +1 -0
- package/dist/{chunk-RWJ4EWJT.js → chunk-HYRDCTBO.js} +152 -116
- package/dist/chunk-HYRDCTBO.js.map +1 -0
- package/dist/chunk-JIHPQAEA.js +90 -0
- package/dist/chunk-JIHPQAEA.js.map +1 -0
- package/dist/chunk-KVKW7W66.cjs +148 -0
- package/dist/chunk-KVKW7W66.cjs.map +1 -0
- package/dist/{chunk-4LNVRSTM.cjs → chunk-OIY6I4TU.cjs} +3 -7
- package/dist/chunk-OIY6I4TU.cjs.map +1 -0
- package/dist/chunk-OZN5X4N6.cjs +96 -0
- package/dist/chunk-OZN5X4N6.cjs.map +1 -0
- package/dist/{chunk-CJJC6ASU.js → chunk-PL4J3NR7.js} +2 -2
- package/dist/chunk-PL4J3NR7.js.map +1 -0
- package/dist/chunk-QFJQU7TQ.js +146 -0
- package/dist/chunk-QFJQU7TQ.js.map +1 -0
- package/dist/{chunk-M5PDY5EZ.cjs → chunk-QGZGERGK.cjs} +2 -2
- package/dist/chunk-QGZGERGK.cjs.map +1 -0
- package/dist/{chunk-QFFQQMU4.cjs → chunk-VOSIA3GF.cjs} +13 -6
- package/dist/chunk-VOSIA3GF.cjs.map +1 -0
- package/dist/{chunk-PZE6HTZR.cjs → chunk-VY4UMZMJ.cjs} +154 -118
- package/dist/chunk-VY4UMZMJ.cjs.map +1 -0
- package/dist/{chunk-LETS7FKB.js → chunk-YFK7ETCF.js} +2 -2
- package/dist/chunk-YFK7ETCF.js.map +1 -0
- package/dist/context.d.cts +1 -1
- package/dist/context.d.ts +1 -1
- package/dist/emitter.cjs +2 -2
- package/dist/emitter.js +1 -1
- package/dist/engine-7DCOERRN.js +4 -0
- package/dist/{engine-M2U4LE3F.js.map → engine-7DCOERRN.js.map} +1 -1
- package/dist/engine-ALWPAIX6.cjs +17 -0
- package/dist/{engine-5JK2FCNL.cjs.map → engine-ALWPAIX6.cjs.map} +1 -1
- package/dist/fade.cjs +5 -5
- package/dist/fade.d.cts +1 -1
- package/dist/fade.d.ts +1 -1
- package/dist/fade.js +1 -1
- package/dist/index.cjs +47 -42
- package/dist/index.d.cts +7 -6
- package/dist/index.d.ts +7 -6
- package/dist/index.js +10 -9
- package/dist/nodes.cjs +11 -11
- package/dist/nodes.js +1 -1
- package/dist/play.cjs +3 -3
- package/dist/play.d.cts +1 -1
- package/dist/play.d.ts +1 -1
- package/dist/play.js +2 -2
- package/dist/player.cjs +22 -0
- package/dist/player.cjs.map +1 -0
- package/dist/player.d.cts +64 -0
- package/dist/player.d.ts +64 -0
- package/dist/player.js +13 -0
- package/dist/player.js.map +1 -0
- package/dist/scheduler.cjs +3 -3
- package/dist/scheduler.d.cts +1 -1
- package/dist/scheduler.d.ts +1 -1
- package/dist/scheduler.js +1 -1
- package/dist/stretcher.cjs +3 -3
- package/dist/stretcher.d.cts +5 -3
- package/dist/stretcher.d.ts +5 -3
- package/dist/stretcher.js +2 -2
- package/dist/synth.cjs +4 -4
- package/dist/synth.js +1 -1
- package/dist/{types-DUrbEbPl.d.cts → types-BYC6m7Q0.d.cts} +6 -6
- package/dist/{types-DUrbEbPl.d.ts → types-BYC6m7Q0.d.ts} +6 -6
- package/dist/waveform.cjs +4 -4
- package/dist/waveform.d.cts +1 -1
- package/dist/waveform.d.ts +1 -1
- package/dist/waveform.js +1 -1
- package/package.json +19 -7
- package/dist/chunk-2DL7CAEP.js.map +0 -1
- package/dist/chunk-4LNVRSTM.cjs.map +0 -1
- package/dist/chunk-5J7S6QV3.cjs.map +0 -1
- package/dist/chunk-AGP2IRC6.js +0 -63
- package/dist/chunk-AGP2IRC6.js.map +0 -1
- package/dist/chunk-C2ASIYN5.js.map +0 -1
- package/dist/chunk-CJJC6ASU.js.map +0 -1
- package/dist/chunk-CPAT75WD.cjs.map +0 -1
- package/dist/chunk-CRODJ4KS.js.map +0 -1
- package/dist/chunk-D5CD5KQZ.cjs.map +0 -1
- package/dist/chunk-GYH2JSCY.js.map +0 -1
- package/dist/chunk-HTGOHC73.cjs +0 -69
- package/dist/chunk-HTGOHC73.cjs.map +0 -1
- package/dist/chunk-LETS7FKB.js.map +0 -1
- package/dist/chunk-M5PDY5EZ.cjs.map +0 -1
- package/dist/chunk-PZE6HTZR.cjs.map +0 -1
- package/dist/chunk-QFFQQMU4.cjs.map +0 -1
- package/dist/chunk-QWNV2BZ5.cjs.map +0 -1
- package/dist/chunk-RWJ4EWJT.js.map +0 -1
- package/dist/chunk-T74FBKTY.js.map +0 -1
- package/dist/chunk-TULV7V5M.cjs.map +0 -1
- package/dist/chunk-V2QX5K42.js.map +0 -1
- package/dist/engine-5JK2FCNL.cjs +0 -13
- package/dist/engine-M2U4LE3F.js +0 -4
|
@@ -1,4 +1,58 @@
|
|
|
1
|
-
import { createEmitter } from './chunk-
|
|
1
|
+
import { createEmitter } from './chunk-FY273Z3I.js';
|
|
2
|
+
|
|
3
|
+
// src/playback-position.ts
|
|
4
|
+
function calcLoopPosition(elapsed, loopStart, loopEnd) {
|
|
5
|
+
const loopDur = loopEnd - loopStart;
|
|
6
|
+
if (loopDur <= 0) return null;
|
|
7
|
+
const offset = elapsed - loopStart;
|
|
8
|
+
return (offset % loopDur + loopDur) % loopDur + loopStart;
|
|
9
|
+
}
|
|
10
|
+
function calcPlaybackPosition(state, elapsed, duration, pausedAt, isLooping, loopStart, loopEnd) {
|
|
11
|
+
if (state === "paused") return pausedAt;
|
|
12
|
+
if (state === "stopped") return 0;
|
|
13
|
+
if (isLooping) {
|
|
14
|
+
const looped = calcLoopPosition(elapsed, loopStart ?? 0, loopEnd ?? duration);
|
|
15
|
+
if (looped !== null) return looped;
|
|
16
|
+
}
|
|
17
|
+
return Math.min(Math.max(elapsed, 0), duration);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/playback-state.ts
|
|
21
|
+
function createPlaybackStateManager(opts) {
|
|
22
|
+
let state = opts.initialState;
|
|
23
|
+
let timerId = null;
|
|
24
|
+
let disposed = false;
|
|
25
|
+
function getState() {
|
|
26
|
+
return state;
|
|
27
|
+
}
|
|
28
|
+
function setState(next) {
|
|
29
|
+
if (state === next) return false;
|
|
30
|
+
state = next;
|
|
31
|
+
opts.onStateChange(next);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
function startTimer() {
|
|
35
|
+
if (timerId !== null) return;
|
|
36
|
+
timerId = setInterval(() => {
|
|
37
|
+
if (state !== "playing" || disposed) return;
|
|
38
|
+
opts.onTimerTick();
|
|
39
|
+
}, opts.timerInterval);
|
|
40
|
+
}
|
|
41
|
+
function stopTimer() {
|
|
42
|
+
if (timerId !== null) {
|
|
43
|
+
clearInterval(timerId);
|
|
44
|
+
timerId = null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function isDisposed() {
|
|
48
|
+
return disposed;
|
|
49
|
+
}
|
|
50
|
+
function markDisposed() {
|
|
51
|
+
disposed = true;
|
|
52
|
+
stopTimer();
|
|
53
|
+
}
|
|
54
|
+
return { getState, setState, startTimer, stopTimer, isDisposed, markDisposed };
|
|
55
|
+
}
|
|
2
56
|
|
|
3
57
|
// src/play.ts
|
|
4
58
|
function play(ctx, buffer, options) {
|
|
@@ -18,14 +72,22 @@ function play(ctx, buffer, options) {
|
|
|
18
72
|
} = options ?? {};
|
|
19
73
|
const emitter = createEmitter();
|
|
20
74
|
const duration = buffer.duration;
|
|
21
|
-
let state = "stopped";
|
|
22
75
|
let sourceNode = null;
|
|
23
76
|
let startedAt = 0;
|
|
24
77
|
let pausedAt = initialOffset;
|
|
25
|
-
let currentRate = initialRate;
|
|
78
|
+
let currentRate = initialRate > 0 ? initialRate : 1;
|
|
26
79
|
let isLooping = loop;
|
|
27
|
-
|
|
28
|
-
|
|
80
|
+
const sm = createPlaybackStateManager({
|
|
81
|
+
initialState: "stopped",
|
|
82
|
+
onStateChange: (next) => emitter.emit("statechange", { state: next }),
|
|
83
|
+
onTimerTick: () => {
|
|
84
|
+
emitter.emit("timeupdate", {
|
|
85
|
+
position: getCurrentTime(),
|
|
86
|
+
duration
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
timerInterval: timeupdateInterval
|
|
90
|
+
});
|
|
29
91
|
function createSource() {
|
|
30
92
|
const src = ctx.createBufferSource();
|
|
31
93
|
src.buffer = buffer;
|
|
@@ -62,90 +124,63 @@ function play(ctx, buffer, options) {
|
|
|
62
124
|
}
|
|
63
125
|
}
|
|
64
126
|
function handleEnded() {
|
|
65
|
-
if (
|
|
127
|
+
if (sm.getState() !== "playing") return;
|
|
66
128
|
if (isLooping) {
|
|
67
129
|
emitter.emit("loop", void 0);
|
|
68
130
|
return;
|
|
69
131
|
}
|
|
70
|
-
setState("stopped");
|
|
132
|
+
sm.setState("stopped");
|
|
71
133
|
pausedAt = 0;
|
|
72
|
-
stopTimer();
|
|
134
|
+
sm.stopTimer();
|
|
73
135
|
emitter.emit("ended", void 0);
|
|
74
136
|
}
|
|
75
|
-
function setState(next) {
|
|
76
|
-
if (state === next) return;
|
|
77
|
-
state = next;
|
|
78
|
-
emitter.emit("statechange", { state: next });
|
|
79
|
-
}
|
|
80
|
-
function startTimer() {
|
|
81
|
-
if (timerId !== null) return;
|
|
82
|
-
timerId = setInterval(() => {
|
|
83
|
-
if (state !== "playing") return;
|
|
84
|
-
emitter.emit("timeupdate", {
|
|
85
|
-
position: getCurrentTime(),
|
|
86
|
-
duration
|
|
87
|
-
});
|
|
88
|
-
}, timeupdateInterval);
|
|
89
|
-
}
|
|
90
|
-
function stopTimer() {
|
|
91
|
-
if (timerId !== null) {
|
|
92
|
-
clearInterval(timerId);
|
|
93
|
-
timerId = null;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
137
|
function getCurrentTime() {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const loopDur = (loopEnd ?? duration) - (loopStart ?? 0);
|
|
101
|
-
return (elapsed - (loopStart ?? 0)) % loopDur + (loopStart ?? 0);
|
|
102
|
-
}
|
|
103
|
-
return Math.min(elapsed, duration);
|
|
104
|
-
}
|
|
105
|
-
if (state === "paused") return pausedAt;
|
|
106
|
-
return 0;
|
|
138
|
+
const state = sm.getState();
|
|
139
|
+
const elapsed = state === "playing" ? (ctx.currentTime - startedAt) * currentRate : 0;
|
|
140
|
+
return calcPlaybackPosition(state, elapsed, duration, pausedAt, isLooping, loopStart, loopEnd);
|
|
107
141
|
}
|
|
108
142
|
function pause() {
|
|
109
|
-
if (
|
|
143
|
+
if (sm.getState() !== "playing" || sm.isDisposed()) return;
|
|
110
144
|
pausedAt = getCurrentTime();
|
|
111
145
|
stopSource();
|
|
112
|
-
stopTimer();
|
|
113
|
-
setState("paused");
|
|
146
|
+
sm.stopTimer();
|
|
147
|
+
sm.setState("paused");
|
|
114
148
|
emitter.emit("pause", void 0);
|
|
115
149
|
}
|
|
116
150
|
function resume() {
|
|
117
|
-
if (
|
|
151
|
+
if (sm.getState() !== "paused" || sm.isDisposed()) return;
|
|
118
152
|
startSource(pausedAt);
|
|
119
|
-
setState("playing");
|
|
120
|
-
startTimer();
|
|
153
|
+
sm.setState("playing");
|
|
154
|
+
sm.startTimer();
|
|
121
155
|
emitter.emit("resume", void 0);
|
|
122
156
|
}
|
|
123
157
|
function togglePlayPause() {
|
|
124
|
-
if (
|
|
125
|
-
else if (
|
|
158
|
+
if (sm.getState() === "playing") pause();
|
|
159
|
+
else if (sm.getState() === "paused") resume();
|
|
126
160
|
}
|
|
127
161
|
function seek(position) {
|
|
128
|
-
if (
|
|
162
|
+
if (sm.isDisposed()) return;
|
|
129
163
|
const clamped = Math.max(0, Math.min(position, duration));
|
|
130
|
-
const wasPlaying =
|
|
164
|
+
const wasPlaying = sm.getState() === "playing";
|
|
131
165
|
stopSource();
|
|
132
|
-
stopTimer();
|
|
166
|
+
sm.stopTimer();
|
|
133
167
|
pausedAt = clamped;
|
|
134
168
|
if (wasPlaying) {
|
|
135
169
|
startSource(clamped);
|
|
136
|
-
startTimer();
|
|
170
|
+
sm.startTimer();
|
|
137
171
|
}
|
|
138
172
|
emitter.emit("seek", { position: clamped });
|
|
139
173
|
}
|
|
140
174
|
function stop() {
|
|
141
|
-
if (
|
|
175
|
+
if (sm.getState() === "stopped" || sm.isDisposed()) return;
|
|
142
176
|
stopSource();
|
|
143
|
-
stopTimer();
|
|
177
|
+
sm.stopTimer();
|
|
144
178
|
pausedAt = 0;
|
|
145
|
-
setState("stopped");
|
|
179
|
+
sm.setState("stopped");
|
|
146
180
|
emitter.emit("stop", void 0);
|
|
147
181
|
}
|
|
148
182
|
function setPlaybackRate(rate) {
|
|
183
|
+
if (rate <= 0) return;
|
|
149
184
|
const position = getCurrentTime();
|
|
150
185
|
currentRate = rate;
|
|
151
186
|
if (sourceNode) {
|
|
@@ -160,18 +195,17 @@ function play(ctx, buffer, options) {
|
|
|
160
195
|
}
|
|
161
196
|
}
|
|
162
197
|
function dispose() {
|
|
163
|
-
if (
|
|
164
|
-
|
|
198
|
+
if (sm.isDisposed()) return;
|
|
199
|
+
sm.markDisposed();
|
|
165
200
|
stopSource();
|
|
166
|
-
stopTimer();
|
|
167
201
|
emitter.clear();
|
|
168
202
|
}
|
|
169
203
|
startSource(initialOffset);
|
|
170
|
-
setState("playing");
|
|
171
|
-
startTimer();
|
|
204
|
+
sm.setState("playing");
|
|
205
|
+
sm.startTimer();
|
|
172
206
|
emitter.emit("play", void 0);
|
|
173
207
|
return {
|
|
174
|
-
getState: () =>
|
|
208
|
+
getState: () => sm.getState(),
|
|
175
209
|
getCurrentTime,
|
|
176
210
|
getDuration: () => duration,
|
|
177
211
|
getProgress: () => duration > 0 ? getCurrentTime() / duration : 0,
|
|
@@ -190,6 +224,7 @@ function play(ctx, buffer, options) {
|
|
|
190
224
|
function createStretchedPlayback(ctx, buffer, options) {
|
|
191
225
|
const {
|
|
192
226
|
offset: initialOffset = 0,
|
|
227
|
+
loop = false,
|
|
193
228
|
playbackRate: initialRate = 1,
|
|
194
229
|
through = [],
|
|
195
230
|
destination = ctx.destination,
|
|
@@ -197,74 +232,76 @@ function createStretchedPlayback(ctx, buffer, options) {
|
|
|
197
232
|
} = options;
|
|
198
233
|
const emitter = createEmitter();
|
|
199
234
|
const duration = buffer.duration;
|
|
200
|
-
let state = "playing";
|
|
201
235
|
let engineInstance = null;
|
|
202
|
-
let
|
|
203
|
-
let
|
|
204
|
-
let currentRate = initialRate;
|
|
236
|
+
let currentRate = initialRate > 0 ? initialRate : 1;
|
|
237
|
+
let isLooping = loop;
|
|
205
238
|
let pendingSeek = null;
|
|
239
|
+
const sm = createPlaybackStateManager({
|
|
240
|
+
initialState: "playing",
|
|
241
|
+
onStateChange: (next) => emitter.emit("statechange", { state: next }),
|
|
242
|
+
onTimerTick: () => {
|
|
243
|
+
emitter.emit("timeupdate", {
|
|
244
|
+
position: getCurrentTime(),
|
|
245
|
+
duration
|
|
246
|
+
});
|
|
247
|
+
},
|
|
248
|
+
timerInterval: timeupdateInterval
|
|
249
|
+
});
|
|
206
250
|
emitter.emit("statechange", { state: "playing" });
|
|
207
251
|
emitter.emit("play", void 0);
|
|
208
|
-
import('./engine-
|
|
209
|
-
if (
|
|
252
|
+
import('./engine-7DCOERRN.js').then(({ createStretcherEngine }) => {
|
|
253
|
+
if (sm.isDisposed()) return;
|
|
210
254
|
engineInstance = createStretcherEngine(ctx, buffer, {
|
|
211
255
|
tempo: currentRate,
|
|
212
256
|
offset: initialOffset,
|
|
257
|
+
loop: isLooping,
|
|
213
258
|
through,
|
|
214
259
|
destination,
|
|
215
260
|
timeupdateInterval
|
|
216
261
|
});
|
|
217
262
|
engineInstance.on("buffering", (data) => {
|
|
218
|
-
if (
|
|
263
|
+
if (sm.isDisposed()) return;
|
|
219
264
|
emitter.emit("buffering", data);
|
|
220
265
|
});
|
|
221
266
|
engineInstance.on("buffered", (data) => {
|
|
222
|
-
if (
|
|
267
|
+
if (sm.isDisposed()) return;
|
|
223
268
|
emitter.emit("buffered", data);
|
|
224
269
|
});
|
|
270
|
+
engineInstance.on("loop", () => {
|
|
271
|
+
if (sm.isDisposed()) return;
|
|
272
|
+
emitter.emit("loop", void 0);
|
|
273
|
+
});
|
|
225
274
|
engineInstance.on("ended", () => {
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
stopTimer();
|
|
229
|
-
|
|
275
|
+
if (sm.isDisposed()) return;
|
|
276
|
+
if (sm.getState() === "stopped") return;
|
|
277
|
+
sm.stopTimer();
|
|
278
|
+
sm.setState("stopped");
|
|
230
279
|
emitter.emit("ended", void 0);
|
|
231
280
|
});
|
|
232
281
|
engineInstance.on("error", (data) => {
|
|
233
|
-
if (
|
|
282
|
+
if (sm.isDisposed()) return;
|
|
234
283
|
if (data.fatal) {
|
|
235
|
-
|
|
236
|
-
emitter.emit("statechange", { state: "stopped" });
|
|
284
|
+
sm.setState("stopped");
|
|
237
285
|
emitter.emit("ended", void 0);
|
|
238
286
|
}
|
|
239
287
|
});
|
|
240
288
|
engineInstance.start();
|
|
241
|
-
startTimer();
|
|
289
|
+
sm.startTimer();
|
|
242
290
|
if (pendingSeek !== null) {
|
|
243
291
|
engineInstance.seek(pendingSeek);
|
|
244
292
|
pendingSeek = null;
|
|
245
293
|
}
|
|
246
|
-
if (
|
|
294
|
+
if (sm.getState() === "paused") {
|
|
247
295
|
engineInstance.pause();
|
|
248
|
-
} else if (
|
|
296
|
+
} else if (sm.getState() === "stopped") {
|
|
249
297
|
engineInstance.stop();
|
|
250
298
|
}
|
|
299
|
+
}).catch(() => {
|
|
300
|
+
if (sm.isDisposed()) return;
|
|
301
|
+
sm.stopTimer();
|
|
302
|
+
sm.setState("stopped");
|
|
303
|
+
emitter.emit("ended", void 0);
|
|
251
304
|
});
|
|
252
|
-
function startTimer() {
|
|
253
|
-
if (timerId !== null) return;
|
|
254
|
-
timerId = setInterval(() => {
|
|
255
|
-
if (state !== "playing" || disposed) return;
|
|
256
|
-
emitter.emit("timeupdate", {
|
|
257
|
-
position: getCurrentTime(),
|
|
258
|
-
duration
|
|
259
|
-
});
|
|
260
|
-
}, timeupdateInterval);
|
|
261
|
-
}
|
|
262
|
-
function stopTimer() {
|
|
263
|
-
if (timerId !== null) {
|
|
264
|
-
clearInterval(timerId);
|
|
265
|
-
timerId = null;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
305
|
function getCurrentTime() {
|
|
269
306
|
if (pendingSeek !== null) {
|
|
270
307
|
return pendingSeek;
|
|
@@ -275,27 +312,25 @@ function createStretchedPlayback(ctx, buffer, options) {
|
|
|
275
312
|
return initialOffset;
|
|
276
313
|
}
|
|
277
314
|
function pause() {
|
|
278
|
-
if (
|
|
279
|
-
state = "paused";
|
|
315
|
+
if (sm.getState() !== "playing" || sm.isDisposed()) return;
|
|
280
316
|
engineInstance?.pause();
|
|
281
|
-
stopTimer();
|
|
282
|
-
|
|
317
|
+
sm.stopTimer();
|
|
318
|
+
sm.setState("paused");
|
|
283
319
|
emitter.emit("pause", void 0);
|
|
284
320
|
}
|
|
285
321
|
function resume() {
|
|
286
|
-
if (
|
|
287
|
-
state = "playing";
|
|
322
|
+
if (sm.getState() !== "paused" || sm.isDisposed()) return;
|
|
288
323
|
engineInstance?.resume();
|
|
289
|
-
startTimer();
|
|
290
|
-
|
|
324
|
+
sm.startTimer();
|
|
325
|
+
sm.setState("playing");
|
|
291
326
|
emitter.emit("resume", void 0);
|
|
292
327
|
}
|
|
293
328
|
function togglePlayPause() {
|
|
294
|
-
if (
|
|
295
|
-
else if (
|
|
329
|
+
if (sm.getState() === "playing") pause();
|
|
330
|
+
else if (sm.getState() === "paused") resume();
|
|
296
331
|
}
|
|
297
332
|
function seek(position) {
|
|
298
|
-
if (
|
|
333
|
+
if (sm.isDisposed()) return;
|
|
299
334
|
const clamped = Math.max(0, Math.min(position, duration));
|
|
300
335
|
if (engineInstance) {
|
|
301
336
|
engineInstance.seek(clamped);
|
|
@@ -305,25 +340,26 @@ function createStretchedPlayback(ctx, buffer, options) {
|
|
|
305
340
|
emitter.emit("seek", { position: clamped });
|
|
306
341
|
}
|
|
307
342
|
function stop() {
|
|
308
|
-
if (
|
|
309
|
-
state = "stopped";
|
|
343
|
+
if (sm.getState() === "stopped" || sm.isDisposed()) return;
|
|
310
344
|
engineInstance?.stop();
|
|
311
|
-
stopTimer();
|
|
312
|
-
|
|
345
|
+
sm.stopTimer();
|
|
346
|
+
sm.setState("stopped");
|
|
313
347
|
emitter.emit("stop", void 0);
|
|
314
348
|
}
|
|
315
349
|
function setPlaybackRate(rate) {
|
|
350
|
+
if (rate <= 0) return;
|
|
316
351
|
currentRate = rate;
|
|
317
352
|
if (engineInstance) {
|
|
318
353
|
engineInstance.setTempo(rate);
|
|
319
354
|
}
|
|
320
355
|
}
|
|
321
|
-
function setLoop(
|
|
356
|
+
function setLoop(value) {
|
|
357
|
+
isLooping = value;
|
|
358
|
+
engineInstance?.setLoop(value);
|
|
322
359
|
}
|
|
323
360
|
function dispose() {
|
|
324
|
-
if (
|
|
325
|
-
|
|
326
|
-
stopTimer();
|
|
361
|
+
if (sm.isDisposed()) return;
|
|
362
|
+
sm.markDisposed();
|
|
327
363
|
engineInstance?.dispose();
|
|
328
364
|
emitter.clear();
|
|
329
365
|
}
|
|
@@ -332,7 +368,7 @@ function createStretchedPlayback(ctx, buffer, options) {
|
|
|
332
368
|
return engineInstance.getSnapshot();
|
|
333
369
|
}
|
|
334
370
|
const playback = {
|
|
335
|
-
getState: () =>
|
|
371
|
+
getState: () => sm.getState(),
|
|
336
372
|
getCurrentTime,
|
|
337
373
|
getDuration: () => duration,
|
|
338
374
|
getProgress: () => duration > 0 ? getCurrentTime() / duration : 0,
|
|
@@ -352,5 +388,5 @@ function createStretchedPlayback(ctx, buffer, options) {
|
|
|
352
388
|
}
|
|
353
389
|
|
|
354
390
|
export { play };
|
|
355
|
-
//# sourceMappingURL=chunk-
|
|
356
|
-
//# sourceMappingURL=chunk-
|
|
391
|
+
//# sourceMappingURL=chunk-HYRDCTBO.js.map
|
|
392
|
+
//# sourceMappingURL=chunk-HYRDCTBO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/playback-position.ts","../src/playback-state.ts","../src/play.ts"],"names":[],"mappings":"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","file":"chunk-HYRDCTBO.js","sourcesContent":["// ---------------------------------------------------------------------------\n// Pure functions for playback position calculation\n// ---------------------------------------------------------------------------\n\n/**\n * Calculate the current position within a loop region.\n *\n * Uses a true modulo (always non-negative) to avoid the JS `%` sign-preservation\n * issue where `elapsed < loopStart` would produce a value outside the loop region.\n *\n * Returns `null` when the loop region is invalid (loopDur <= 0) so the caller\n * can fall back to non-loop behaviour.\n */\nexport function calcLoopPosition(\n elapsed: number,\n loopStart: number,\n loopEnd: number,\n): number | null {\n const loopDur = loopEnd - loopStart;\n if (loopDur <= 0) return null;\n const offset = elapsed - loopStart;\n return (((offset % loopDur) + loopDur) % loopDur) + loopStart;\n}\n\n/**\n * Calculate the playback position for a given state.\n *\n * Pure function — no side effects, no dependency on AudioContext.\n */\nexport function calcPlaybackPosition(\n state: \"playing\" | \"paused\" | \"stopped\",\n elapsed: number,\n duration: number,\n pausedAt: number,\n isLooping: boolean,\n loopStart: number | undefined,\n loopEnd: number | undefined,\n): number {\n if (state === \"paused\") return pausedAt;\n if (state === \"stopped\") return 0;\n\n // state === \"playing\"\n if (isLooping) {\n const looped = calcLoopPosition(elapsed, loopStart ?? 0, loopEnd ?? duration);\n // Invalid loop region → fall back to non-loop clamping\n if (looped !== null) return looped;\n }\n return Math.min(Math.max(elapsed, 0), duration);\n}\n","// ---------------------------------------------------------------------------\n// Shared playback state management (used by both normal and stretched modes)\n// ---------------------------------------------------------------------------\n\nimport type { PlaybackState } from \"./types.js\";\n\nexport interface PlaybackStateManager {\n getState(): PlaybackState;\n setState(next: PlaybackState): boolean;\n startTimer(): void;\n stopTimer(): void;\n isDisposed(): boolean;\n markDisposed(): void;\n}\n\n/**\n * Create a reusable state manager that encapsulates the setState / startTimer /\n * stopTimer / dispose pattern shared between normal and stretched playback.\n */\nexport function createPlaybackStateManager(opts: {\n initialState: PlaybackState;\n onStateChange: (state: PlaybackState) => void;\n onTimerTick: () => void;\n timerInterval: number;\n}): PlaybackStateManager {\n let state: PlaybackState = opts.initialState;\n let timerId: ReturnType<typeof setInterval> | null = null;\n let disposed = false;\n\n function getState(): PlaybackState {\n return state;\n }\n\n /** Returns `true` if state actually changed. */\n function setState(next: PlaybackState): boolean {\n if (state === next) return false;\n state = next;\n opts.onStateChange(next);\n return true;\n }\n\n function startTimer(): void {\n if (timerId !== null) return;\n timerId = setInterval(() => {\n if (state !== \"playing\" || disposed) return;\n opts.onTimerTick();\n }, opts.timerInterval);\n }\n\n function stopTimer(): void {\n if (timerId !== null) {\n clearInterval(timerId);\n timerId = null;\n }\n }\n\n function isDisposed(): boolean {\n return disposed;\n }\n\n function markDisposed(): void {\n disposed = true;\n stopTimer();\n }\n\n return { getState, setState, startTimer, stopTimer, isDisposed, markDisposed };\n}\n","// ---------------------------------------------------------------------------\n// M3: Playback engine\n// ---------------------------------------------------------------------------\n\nimport { createEmitter } from \"./emitter.js\";\nimport { calcPlaybackPosition } from \"./playback-position.js\";\nimport { createPlaybackStateManager } from \"./playback-state.js\";\nimport type {\n Playback,\n PlaybackEventMap,\n PlayOptions,\n StretcherSnapshotExtension,\n} from \"./types.js\";\n\n/**\n * Play an `AudioBuffer` through an `AudioContext` and return a controllable\n * `Playback` handle.\n *\n * ```ts\n * const pb = play(ctx, buffer, { loop: true });\n * pb.on(\"timeupdate\", ({ position }) => console.log(position));\n * pb.pause();\n * pb.resume();\n * pb.seek(10);\n * pb.stop();\n * pb.dispose();\n * ```\n */\nexport function play(ctx: AudioContext, buffer: AudioBuffer, options?: PlayOptions): Playback {\n const { preservePitch = true } = options ?? {};\n\n // ----- Pitch-preserving mode (WSOLA-based time-stretch) -----\n if (preservePitch) {\n return createStretchedPlayback(ctx, buffer, options ?? {});\n }\n\n const {\n offset: initialOffset = 0,\n loop = false,\n loopStart,\n loopEnd,\n playbackRate: initialRate = 1,\n through = [],\n destination = ctx.destination,\n timeupdateInterval = 50,\n } = options ?? {};\n\n const emitter = createEmitter<PlaybackEventMap>();\n const duration = buffer.duration;\n\n // ----- mutable internal state -----\n let sourceNode: AudioBufferSourceNode | null = null;\n let startedAt = 0; // ctx.currentTime when playback last started/resumed\n let pausedAt = initialOffset; // position in the buffer (seconds)\n let currentRate = initialRate > 0 ? initialRate : 1;\n let isLooping = loop;\n\n const sm = createPlaybackStateManager({\n initialState: \"stopped\",\n onStateChange: (next) => emitter.emit(\"statechange\", { state: next }),\n onTimerTick: () => {\n emitter.emit(\"timeupdate\", {\n position: getCurrentTime(),\n duration,\n });\n },\n timerInterval: timeupdateInterval,\n });\n\n // ----- helpers -----\n\n function createSource(): AudioBufferSourceNode {\n const src = ctx.createBufferSource();\n src.buffer = buffer;\n src.playbackRate.value = currentRate;\n src.loop = isLooping;\n if (loopStart !== undefined) src.loopStart = loopStart;\n if (loopEnd !== undefined) src.loopEnd = loopEnd;\n\n // Connect through the node chain (or directly to destination).\n if (through.length > 0) {\n src.connect(through[0]!);\n for (let i = 0; i < through.length - 1; i++) {\n through[i]!.connect(through[i + 1]!);\n }\n through[through.length - 1]!.connect(destination);\n } else {\n src.connect(destination);\n }\n\n src.onended = handleEnded;\n return src;\n }\n\n function startSource(positionInBuffer: number) {\n sourceNode = createSource();\n sourceNode.start(0, positionInBuffer);\n startedAt = ctx.currentTime - positionInBuffer / currentRate;\n }\n\n function stopSource() {\n if (sourceNode) {\n sourceNode.onended = null;\n try {\n sourceNode.stop();\n } catch {\n // Already stopped — safe to ignore.\n }\n sourceNode.disconnect();\n sourceNode = null;\n }\n }\n\n function handleEnded() {\n // If we manually stopped / paused, the handler was already removed.\n if (sm.getState() !== \"playing\") return;\n if (isLooping) {\n emitter.emit(\"loop\", undefined as never);\n return;\n }\n sm.setState(\"stopped\");\n pausedAt = 0;\n sm.stopTimer();\n emitter.emit(\"ended\", undefined as never);\n }\n\n // ----- public API -----\n\n function getCurrentTime(): number {\n const state = sm.getState();\n const elapsed = state === \"playing\" ? (ctx.currentTime - startedAt) * currentRate : 0;\n return calcPlaybackPosition(state, elapsed, duration, pausedAt, isLooping, loopStart, loopEnd);\n }\n\n function pause() {\n if (sm.getState() !== \"playing\" || sm.isDisposed()) return;\n pausedAt = getCurrentTime();\n stopSource();\n sm.stopTimer();\n sm.setState(\"paused\");\n emitter.emit(\"pause\", undefined as never);\n }\n\n function resume() {\n if (sm.getState() !== \"paused\" || sm.isDisposed()) return;\n startSource(pausedAt);\n sm.setState(\"playing\");\n sm.startTimer();\n emitter.emit(\"resume\", undefined as never);\n }\n\n function togglePlayPause() {\n if (sm.getState() === \"playing\") pause();\n else if (sm.getState() === \"paused\") resume();\n }\n\n function seek(position: number) {\n if (sm.isDisposed()) return;\n const clamped = Math.max(0, Math.min(position, duration));\n const wasPlaying = sm.getState() === \"playing\";\n\n stopSource();\n sm.stopTimer();\n\n pausedAt = clamped;\n\n if (wasPlaying) {\n startSource(clamped);\n sm.startTimer();\n }\n\n emitter.emit(\"seek\", { position: clamped });\n }\n\n function stop() {\n if (sm.getState() === \"stopped\" || sm.isDisposed()) return;\n stopSource();\n sm.stopTimer();\n pausedAt = 0;\n sm.setState(\"stopped\");\n emitter.emit(\"stop\", undefined as never);\n }\n\n function setPlaybackRate(rate: number) {\n if (rate <= 0) return;\n const position = getCurrentTime();\n currentRate = rate;\n if (sourceNode) {\n sourceNode.playbackRate.value = rate;\n startedAt = ctx.currentTime - position / rate;\n }\n }\n\n function setLoop(value: boolean) {\n isLooping = value;\n if (sourceNode) {\n sourceNode.loop = value;\n }\n }\n\n function dispose() {\n if (sm.isDisposed()) return;\n sm.markDisposed();\n stopSource();\n emitter.clear();\n }\n\n // ----- kick off initial playback -----\n\n startSource(initialOffset);\n sm.setState(\"playing\");\n sm.startTimer();\n emitter.emit(\"play\", undefined as never);\n\n return {\n getState: () => sm.getState(),\n getCurrentTime,\n getDuration: () => duration,\n getProgress: () => (duration > 0 ? getCurrentTime() / duration : 0),\n pause,\n resume,\n togglePlayPause,\n seek,\n stop,\n setPlaybackRate,\n setLoop,\n on: emitter.on.bind(emitter) as Playback[\"on\"],\n off: emitter.off.bind(emitter) as Playback[\"off\"],\n dispose,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Stretched playback (preservePitch: true)\n// ---------------------------------------------------------------------------\n\nfunction createStretchedPlayback(\n ctx: AudioContext,\n buffer: AudioBuffer,\n options: PlayOptions,\n): Playback {\n const {\n offset: initialOffset = 0,\n loop = false,\n playbackRate: initialRate = 1,\n through = [],\n destination = ctx.destination,\n timeupdateInterval = 50,\n } = options;\n\n const emitter = createEmitter<PlaybackEventMap>();\n const duration = buffer.duration;\n\n let engineInstance: import(\"./stretcher/types.js\").StretcherEngine | null = null;\n let currentRate = initialRate > 0 ? initialRate : 1;\n let isLooping = loop;\n let pendingSeek: number | null = null;\n\n const sm = createPlaybackStateManager({\n initialState: \"playing\",\n onStateChange: (next) => emitter.emit(\"statechange\", { state: next }),\n onTimerTick: () => {\n emitter.emit(\"timeupdate\", {\n position: getCurrentTime(),\n duration,\n });\n },\n timerInterval: timeupdateInterval,\n });\n\n // Emit initial play event\n emitter.emit(\"statechange\", { state: \"playing\" });\n emitter.emit(\"play\", undefined as never);\n\n // Fire-and-forget dynamic import of the stretcher engine\n import(\"./stretcher/engine.js\")\n .then(({ createStretcherEngine }) => {\n if (sm.isDisposed()) return;\n\n engineInstance = createStretcherEngine(ctx, buffer, {\n tempo: currentRate,\n offset: initialOffset,\n loop: isLooping,\n through,\n destination,\n timeupdateInterval,\n });\n\n // Wire stretcher events to playback events\n engineInstance.on(\"buffering\", (data) => {\n if (sm.isDisposed()) return;\n emitter.emit(\"buffering\", data);\n });\n\n engineInstance.on(\"buffered\", (data) => {\n if (sm.isDisposed()) return;\n emitter.emit(\"buffered\", data);\n });\n\n engineInstance.on(\"loop\", () => {\n if (sm.isDisposed()) return;\n emitter.emit(\"loop\", undefined as never);\n });\n\n engineInstance.on(\"ended\", () => {\n if (sm.isDisposed()) return;\n if (sm.getState() === \"stopped\") return;\n sm.stopTimer();\n sm.setState(\"stopped\");\n emitter.emit(\"ended\", undefined as never);\n });\n\n engineInstance.on(\"error\", (data) => {\n if (sm.isDisposed()) return;\n if (data.fatal) {\n sm.setState(\"stopped\");\n emitter.emit(\"ended\", undefined as never);\n }\n });\n\n // Start engine and timeupdate timer\n engineInstance.start();\n sm.startTimer();\n\n // Apply pending seek if any\n if (pendingSeek !== null) {\n engineInstance.seek(pendingSeek);\n pendingSeek = null;\n }\n\n // If we were paused before the engine loaded, pause it\n if (sm.getState() === \"paused\") {\n engineInstance.pause();\n } else if (sm.getState() === \"stopped\") {\n engineInstance.stop();\n }\n })\n .catch(() => {\n if (sm.isDisposed()) return;\n sm.stopTimer();\n sm.setState(\"stopped\");\n emitter.emit(\"ended\", undefined as never);\n });\n\n function getCurrentTime(): number {\n if (pendingSeek !== null) {\n return pendingSeek;\n }\n if (engineInstance) {\n return engineInstance.getCurrentPosition();\n }\n return initialOffset;\n }\n\n function pause() {\n if (sm.getState() !== \"playing\" || sm.isDisposed()) return;\n engineInstance?.pause();\n sm.stopTimer();\n sm.setState(\"paused\");\n emitter.emit(\"pause\", undefined as never);\n }\n\n function resume() {\n if (sm.getState() !== \"paused\" || sm.isDisposed()) return;\n engineInstance?.resume();\n sm.startTimer();\n sm.setState(\"playing\");\n emitter.emit(\"resume\", undefined as never);\n }\n\n function togglePlayPause() {\n if (sm.getState() === \"playing\") pause();\n else if (sm.getState() === \"paused\") resume();\n }\n\n function seek(position: number) {\n if (sm.isDisposed()) return;\n const clamped = Math.max(0, Math.min(position, duration));\n if (engineInstance) {\n engineInstance.seek(clamped);\n } else {\n pendingSeek = clamped;\n }\n emitter.emit(\"seek\", { position: clamped });\n }\n\n function stop() {\n if (sm.getState() === \"stopped\" || sm.isDisposed()) return;\n engineInstance?.stop();\n sm.stopTimer();\n sm.setState(\"stopped\");\n emitter.emit(\"stop\", undefined as never);\n }\n\n function setPlaybackRate(rate: number) {\n if (rate <= 0) return;\n currentRate = rate;\n if (engineInstance) {\n engineInstance.setTempo(rate);\n }\n }\n\n function setLoop(value: boolean) {\n isLooping = value;\n engineInstance?.setLoop(value);\n }\n\n function dispose() {\n if (sm.isDisposed()) return;\n sm.markDisposed();\n engineInstance?.dispose();\n emitter.clear();\n }\n\n function _getStretcherSnapshot(): StretcherSnapshotExtension | null {\n if (!engineInstance) return null;\n return engineInstance.getSnapshot();\n }\n\n const playback: Playback & {\n _getStretcherSnapshot: typeof _getStretcherSnapshot;\n } = {\n getState: () => sm.getState(),\n getCurrentTime,\n getDuration: () => duration,\n getProgress: () => (duration > 0 ? getCurrentTime() / duration : 0),\n pause,\n resume,\n togglePlayPause,\n seek,\n stop,\n setPlaybackRate,\n setLoop,\n on: emitter.on.bind(emitter) as Playback[\"on\"],\n off: emitter.off.bind(emitter) as Playback[\"off\"],\n dispose,\n _getStretcherSnapshot,\n };\n\n return playback;\n}\n"]}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// src/adapters.ts
|
|
2
|
+
var snapshotCache = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
var subscriberCount = /* @__PURE__ */ new WeakMap();
|
|
4
|
+
function computeSnapshot(playback) {
|
|
5
|
+
const state = playback.getState();
|
|
6
|
+
const position = playback.getCurrentTime();
|
|
7
|
+
const duration = playback.getDuration();
|
|
8
|
+
const progress = playback.getProgress();
|
|
9
|
+
const getter = playback._getStretcherSnapshot;
|
|
10
|
+
let stretcher;
|
|
11
|
+
if (typeof getter === "function") {
|
|
12
|
+
stretcher = getter();
|
|
13
|
+
}
|
|
14
|
+
const snap = { state, position, duration, progress };
|
|
15
|
+
if (stretcher) {
|
|
16
|
+
snap.stretcher = stretcher;
|
|
17
|
+
}
|
|
18
|
+
return snap;
|
|
19
|
+
}
|
|
20
|
+
function getSnapshot(playback) {
|
|
21
|
+
const cached = snapshotCache.get(playback);
|
|
22
|
+
if (cached) return cached;
|
|
23
|
+
const snap = computeSnapshot(playback);
|
|
24
|
+
snapshotCache.set(playback, snap);
|
|
25
|
+
return snap;
|
|
26
|
+
}
|
|
27
|
+
function subscribeSnapshot(playback, callback) {
|
|
28
|
+
const count = (subscriberCount.get(playback) ?? 0) + 1;
|
|
29
|
+
subscriberCount.set(playback, count);
|
|
30
|
+
snapshotCache.set(playback, computeSnapshot(playback));
|
|
31
|
+
const notify = () => {
|
|
32
|
+
snapshotCache.set(playback, computeSnapshot(playback));
|
|
33
|
+
callback();
|
|
34
|
+
};
|
|
35
|
+
const unsubs = [];
|
|
36
|
+
unsubs.push(playback.on("statechange", notify));
|
|
37
|
+
unsubs.push(playback.on("timeupdate", notify));
|
|
38
|
+
unsubs.push(playback.on("seek", notify));
|
|
39
|
+
unsubs.push(playback.on("ended", notify));
|
|
40
|
+
return () => {
|
|
41
|
+
for (const unsub of unsubs) unsub();
|
|
42
|
+
const c = (subscriberCount.get(playback) ?? 1) - 1;
|
|
43
|
+
if (c <= 0) {
|
|
44
|
+
subscriberCount.delete(playback);
|
|
45
|
+
} else {
|
|
46
|
+
subscriberCount.set(playback, c);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function onFrame(playback, callback) {
|
|
51
|
+
let rafId = null;
|
|
52
|
+
function tick() {
|
|
53
|
+
const snap = computeSnapshot(playback);
|
|
54
|
+
snapshotCache.set(playback, snap);
|
|
55
|
+
callback(snap);
|
|
56
|
+
rafId = requestAnimationFrame(tick);
|
|
57
|
+
}
|
|
58
|
+
rafId = requestAnimationFrame(tick);
|
|
59
|
+
return () => {
|
|
60
|
+
if (rafId !== null) {
|
|
61
|
+
cancelAnimationFrame(rafId);
|
|
62
|
+
rafId = null;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function whenEnded(playback) {
|
|
67
|
+
return new Promise((resolve) => {
|
|
68
|
+
const unsub = playback.on("ended", () => {
|
|
69
|
+
unsub();
|
|
70
|
+
resolve();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function whenPosition(playback, position) {
|
|
75
|
+
if (playback.getCurrentTime() >= position) {
|
|
76
|
+
return Promise.resolve();
|
|
77
|
+
}
|
|
78
|
+
return new Promise((resolve) => {
|
|
79
|
+
const unsub = playback.on("timeupdate", ({ position: current }) => {
|
|
80
|
+
if (current >= position) {
|
|
81
|
+
unsub();
|
|
82
|
+
resolve();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { getSnapshot, onFrame, subscribeSnapshot, whenEnded, whenPosition };
|
|
89
|
+
//# sourceMappingURL=chunk-JIHPQAEA.js.map
|
|
90
|
+
//# sourceMappingURL=chunk-JIHPQAEA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters.ts"],"names":[],"mappings":"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","file":"chunk-JIHPQAEA.js","sourcesContent":["// ---------------------------------------------------------------------------\n// M10: Framework adapters\n// ---------------------------------------------------------------------------\n\nimport type { Playback, PlaybackSnapshot } from \"./types.js\";\n\nconst snapshotCache = new WeakMap<Playback, PlaybackSnapshot>();\nconst subscriberCount = new WeakMap<Playback, number>();\n\nfunction computeSnapshot(playback: Playback): PlaybackSnapshot {\n const state = playback.getState();\n const position = playback.getCurrentTime();\n const duration = playback.getDuration();\n const progress = playback.getProgress();\n\n const getter = (playback as unknown as Record<string, unknown>)._getStretcherSnapshot;\n let stretcher: PlaybackSnapshot[\"stretcher\"];\n if (typeof getter === \"function\") {\n stretcher = (getter as () => PlaybackSnapshot[\"stretcher\"])();\n }\n\n const snap: PlaybackSnapshot = { state, position, duration, progress };\n if (stretcher) {\n snap.stretcher = stretcher;\n }\n return snap;\n}\n\n/**\n * Get an immutable snapshot of the current playback state.\n * Designed for use with React's `useSyncExternalStore` or similar patterns.\n *\n * Always returns a referentially stable (cached) object. The cache is updated\n * by `subscribeSnapshot` (on playback events) and `onFrame` (every animation\n * frame), so `getSnapshot` itself never computes a fresh snapshot — it only\n * reads or initialises the cache. This guarantees the reference-equality\n * contract required by `useSyncExternalStore`.\n */\nexport function getSnapshot(playback: Playback): PlaybackSnapshot {\n const cached = snapshotCache.get(playback);\n if (cached) return cached;\n\n const snap = computeSnapshot(playback);\n snapshotCache.set(playback, snap);\n return snap;\n}\n\n/**\n * Subscribe to playback state changes, calling `callback` with a fresh\n * snapshot whenever the state updates.\n *\n * Returns an unsubscribe function. Works as the `subscribe` parameter for\n * React's `useSyncExternalStore`.\n *\n * ```ts\n * // React example:\n * import { useCallback } from \"react\";\n * const subscribe = useCallback(\n * (cb: () => void) => subscribeSnapshot(playback, cb),\n * [playback],\n * );\n * const snap = useCallback(\n * () => getSnapshot(playback),\n * [playback],\n * );\n * const snapshot = useSyncExternalStore(subscribe, snap, snap);\n * ```\n */\nexport function subscribeSnapshot(playback: Playback, callback: () => void): () => void {\n const count = (subscriberCount.get(playback) ?? 0) + 1;\n subscriberCount.set(playback, count);\n\n // Eagerly compute the initial snapshot so getSnapshot() has a\n // stable reference before the first event fires.\n snapshotCache.set(playback, computeSnapshot(playback));\n\n const notify = () => {\n // Pre-compute and cache the snapshot BEFORE notifying the\n // subscriber. This ensures getSnapshot() returns the same\n // reference during React's render and post-commit check.\n snapshotCache.set(playback, computeSnapshot(playback));\n callback();\n };\n\n const unsubs: Array<() => void> = [];\n unsubs.push(playback.on(\"statechange\", notify));\n unsubs.push(playback.on(\"timeupdate\", notify));\n unsubs.push(playback.on(\"seek\", notify));\n unsubs.push(playback.on(\"ended\", notify));\n\n return () => {\n for (const unsub of unsubs) unsub();\n const c = (subscriberCount.get(playback) ?? 1) - 1;\n if (c <= 0) {\n subscriberCount.delete(playback);\n } else {\n subscriberCount.set(playback, c);\n }\n };\n}\n\n/**\n * Call `callback` on every animation frame with the current playback snapshot.\n * Useful for smooth UI animations (waveform cursors, progress bars, etc.).\n *\n * Returns a `stop` function that cancels the loop.\n */\nexport function onFrame(\n playback: Playback,\n callback: (snapshot: PlaybackSnapshot) => void,\n): () => void {\n let rafId: number | null = null;\n\n function tick() {\n const snap = computeSnapshot(playback);\n snapshotCache.set(playback, snap);\n callback(snap);\n rafId = requestAnimationFrame(tick);\n }\n\n rafId = requestAnimationFrame(tick);\n\n return () => {\n if (rafId !== null) {\n cancelAnimationFrame(rafId);\n rafId = null;\n }\n };\n}\n\n/**\n * Return a `Promise` that resolves when the playback reaches the `\"stopped\"`\n * state via the `ended` event (natural end, not manual stop).\n */\nexport function whenEnded(playback: Playback): Promise<void> {\n return new Promise<void>((resolve) => {\n const unsub = playback.on(\"ended\", () => {\n unsub();\n resolve();\n });\n });\n}\n\n/**\n * Return a `Promise` that resolves when the playback position reaches or\n * exceeds `position` seconds.\n */\nexport function whenPosition(playback: Playback, position: number): Promise<void> {\n if (playback.getCurrentTime() >= position) {\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n const unsub = playback.on(\"timeupdate\", ({ position: current }) => {\n if (current >= position) {\n unsub();\n resolve();\n }\n });\n });\n}\n"]}
|