waa-play 0.1.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.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -0
  3. package/dist/adapters.cjs +28 -0
  4. package/dist/adapters.cjs.map +1 -0
  5. package/dist/adapters.d.cts +42 -0
  6. package/dist/adapters.d.ts +42 -0
  7. package/dist/adapters.js +3 -0
  8. package/dist/adapters.js.map +1 -0
  9. package/dist/buffer.cjs +24 -0
  10. package/dist/buffer.cjs.map +1 -0
  11. package/dist/buffer.d.cts +34 -0
  12. package/dist/buffer.d.ts +34 -0
  13. package/dist/buffer.js +3 -0
  14. package/dist/buffer.js.map +1 -0
  15. package/dist/chunk-2DL7CAEP.js +69 -0
  16. package/dist/chunk-2DL7CAEP.js.map +1 -0
  17. package/dist/chunk-37CPPRLV.js +24 -0
  18. package/dist/chunk-37CPPRLV.js.map +1 -0
  19. package/dist/chunk-4LNVRSTM.cjs +72 -0
  20. package/dist/chunk-4LNVRSTM.cjs.map +1 -0
  21. package/dist/chunk-5J7S6QV3.cjs +44 -0
  22. package/dist/chunk-5J7S6QV3.cjs.map +1 -0
  23. package/dist/chunk-6UTN73HG.cjs +29 -0
  24. package/dist/chunk-6UTN73HG.cjs.map +1 -0
  25. package/dist/chunk-AGP2IRC6.js +63 -0
  26. package/dist/chunk-AGP2IRC6.js.map +1 -0
  27. package/dist/chunk-C2ASIYN5.js +67 -0
  28. package/dist/chunk-C2ASIYN5.js.map +1 -0
  29. package/dist/chunk-CJJC6ASU.js +73 -0
  30. package/dist/chunk-CJJC6ASU.js.map +1 -0
  31. package/dist/chunk-CPAT75WD.cjs +60 -0
  32. package/dist/chunk-CPAT75WD.cjs.map +1 -0
  33. package/dist/chunk-CRODJ4KS.js +71 -0
  34. package/dist/chunk-CRODJ4KS.js.map +1 -0
  35. package/dist/chunk-D5CD5KQZ.cjs +72 -0
  36. package/dist/chunk-D5CD5KQZ.cjs.map +1 -0
  37. package/dist/chunk-GYH2JSCY.js +42 -0
  38. package/dist/chunk-GYH2JSCY.js.map +1 -0
  39. package/dist/chunk-HTGOHC73.cjs +69 -0
  40. package/dist/chunk-HTGOHC73.cjs.map +1 -0
  41. package/dist/chunk-LETS7FKB.js +33 -0
  42. package/dist/chunk-LETS7FKB.js.map +1 -0
  43. package/dist/chunk-M5PDY5EZ.cjs +84 -0
  44. package/dist/chunk-M5PDY5EZ.cjs.map +1 -0
  45. package/dist/chunk-PZE6HTZR.cjs +358 -0
  46. package/dist/chunk-PZE6HTZR.cjs.map +1 -0
  47. package/dist/chunk-QFFQQMU4.cjs +75 -0
  48. package/dist/chunk-QFFQQMU4.cjs.map +1 -0
  49. package/dist/chunk-QWNV2BZ5.cjs +37 -0
  50. package/dist/chunk-QWNV2BZ5.cjs.map +1 -0
  51. package/dist/chunk-RWJ4EWJT.js +356 -0
  52. package/dist/chunk-RWJ4EWJT.js.map +1 -0
  53. package/dist/chunk-T74FBKTY.js +55 -0
  54. package/dist/chunk-T74FBKTY.js.map +1 -0
  55. package/dist/chunk-TULV7V5M.cjs +1710 -0
  56. package/dist/chunk-TULV7V5M.cjs.map +1 -0
  57. package/dist/chunk-V2QX5K42.js +1708 -0
  58. package/dist/chunk-V2QX5K42.js.map +1 -0
  59. package/dist/context.cjs +24 -0
  60. package/dist/context.cjs.map +1 -0
  61. package/dist/context.d.cts +27 -0
  62. package/dist/context.d.ts +27 -0
  63. package/dist/context.js +3 -0
  64. package/dist/context.js.map +1 -0
  65. package/dist/emitter.cjs +12 -0
  66. package/dist/emitter.cjs.map +1 -0
  67. package/dist/emitter.d.cts +24 -0
  68. package/dist/emitter.d.ts +24 -0
  69. package/dist/emitter.js +3 -0
  70. package/dist/emitter.js.map +1 -0
  71. package/dist/engine-5JK2FCNL.cjs +13 -0
  72. package/dist/engine-5JK2FCNL.cjs.map +1 -0
  73. package/dist/engine-M2U4LE3F.js +4 -0
  74. package/dist/engine-M2U4LE3F.js.map +1 -0
  75. package/dist/fade.cjs +24 -0
  76. package/dist/fade.cjs.map +1 -0
  77. package/dist/fade.d.cts +21 -0
  78. package/dist/fade.d.ts +21 -0
  79. package/dist/fade.js +3 -0
  80. package/dist/fade.js.map +1 -0
  81. package/dist/index.cjs +165 -0
  82. package/dist/index.cjs.map +1 -0
  83. package/dist/index.d.cts +11 -0
  84. package/dist/index.d.ts +11 -0
  85. package/dist/index.js +12 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/nodes.cjs +48 -0
  88. package/dist/nodes.cjs.map +1 -0
  89. package/dist/nodes.d.cts +61 -0
  90. package/dist/nodes.d.ts +61 -0
  91. package/dist/nodes.js +3 -0
  92. package/dist/nodes.js.map +1 -0
  93. package/dist/play.cjs +13 -0
  94. package/dist/play.cjs.map +1 -0
  95. package/dist/play.d.cts +19 -0
  96. package/dist/play.d.ts +19 -0
  97. package/dist/play.js +4 -0
  98. package/dist/play.js.map +1 -0
  99. package/dist/scheduler.cjs +16 -0
  100. package/dist/scheduler.cjs.map +1 -0
  101. package/dist/scheduler.d.cts +39 -0
  102. package/dist/scheduler.d.ts +39 -0
  103. package/dist/scheduler.js +3 -0
  104. package/dist/scheduler.js.map +1 -0
  105. package/dist/stretcher.cjs +13 -0
  106. package/dist/stretcher.cjs.map +1 -0
  107. package/dist/stretcher.d.cts +171 -0
  108. package/dist/stretcher.d.ts +171 -0
  109. package/dist/stretcher.js +4 -0
  110. package/dist/stretcher.js.map +1 -0
  111. package/dist/synth.cjs +20 -0
  112. package/dist/synth.cjs.map +1 -0
  113. package/dist/synth.d.cts +15 -0
  114. package/dist/synth.d.ts +15 -0
  115. package/dist/synth.js +3 -0
  116. package/dist/synth.js.map +1 -0
  117. package/dist/types-DUrbEbPl.d.cts +177 -0
  118. package/dist/types-DUrbEbPl.d.ts +177 -0
  119. package/dist/waveform.cjs +20 -0
  120. package/dist/waveform.cjs.map +1 -0
  121. package/dist/waveform.d.cts +22 -0
  122. package/dist/waveform.d.ts +22 -0
  123. package/dist/waveform.js +3 -0
  124. package/dist/waveform.js.map +1 -0
  125. package/package.json +123 -0
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+
3
+ // src/adapters.ts
4
+ function getSnapshot(playback) {
5
+ const base = {
6
+ state: playback.getState(),
7
+ position: playback.getCurrentTime(),
8
+ duration: playback.getDuration(),
9
+ progress: playback.getProgress()
10
+ };
11
+ const getter = playback["_getStretcherSnapshot"];
12
+ if (typeof getter === "function") {
13
+ const stretcher = getter();
14
+ if (stretcher) {
15
+ base.stretcher = stretcher;
16
+ }
17
+ }
18
+ return base;
19
+ }
20
+ function subscribeSnapshot(playback, callback) {
21
+ const unsubs = [];
22
+ unsubs.push(playback.on("statechange", callback));
23
+ unsubs.push(playback.on("timeupdate", callback));
24
+ unsubs.push(playback.on("seek", callback));
25
+ unsubs.push(playback.on("ended", callback));
26
+ return () => {
27
+ for (const unsub of unsubs) unsub();
28
+ };
29
+ }
30
+ function onFrame(playback, callback) {
31
+ let rafId = null;
32
+ function tick() {
33
+ callback(getSnapshot(playback));
34
+ rafId = requestAnimationFrame(tick);
35
+ }
36
+ rafId = requestAnimationFrame(tick);
37
+ return () => {
38
+ if (rafId !== null) {
39
+ cancelAnimationFrame(rafId);
40
+ rafId = null;
41
+ }
42
+ };
43
+ }
44
+ function whenEnded(playback) {
45
+ return new Promise((resolve) => {
46
+ const unsub = playback.on("ended", () => {
47
+ unsub();
48
+ resolve();
49
+ });
50
+ });
51
+ }
52
+ function whenPosition(playback, position) {
53
+ return new Promise((resolve) => {
54
+ const unsub = playback.on("timeupdate", ({ position: current }) => {
55
+ if (current >= position) {
56
+ unsub();
57
+ resolve();
58
+ }
59
+ });
60
+ });
61
+ }
62
+
63
+ exports.getSnapshot = getSnapshot;
64
+ exports.onFrame = onFrame;
65
+ exports.subscribeSnapshot = subscribeSnapshot;
66
+ exports.whenEnded = whenEnded;
67
+ exports.whenPosition = whenPosition;
68
+ //# sourceMappingURL=chunk-HTGOHC73.cjs.map
69
+ //# sourceMappingURL=chunk-HTGOHC73.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters.ts"],"names":[],"mappings":";;;AAUO,SAAS,YAAY,QAAA,EAAsC;AAChE,EAAA,MAAM,IAAA,GAAyB;AAAA,IAC7B,KAAA,EAAO,SAAS,QAAA,EAAS;AAAA,IACzB,QAAA,EAAU,SAAS,cAAA,EAAe;AAAA,IAClC,QAAA,EAAU,SAAS,WAAA,EAAY;AAAA,IAC/B,QAAA,EAAU,SAAS,WAAA;AAAY,GACjC;AAGA,EAAA,MAAM,MAAA,GAAU,SAAgD,uBAAuB,CAAA;AACvF,EAAA,IAAI,OAAO,WAAW,UAAA,EAAY;AAChC,IAAA,MAAM,YAAa,MAAA,EAA+C;AAClE,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AAAA,IACnB;AAAA,EACF;AAEA,EAAA,OAAO,IAAA;AACT;AAiBO,SAAS,iBAAA,CACd,UACA,QAAA,EACY;AACZ,EAAA,MAAM,SAA4B,EAAC;AAEnC,EAAA,MAAA,CAAO,IAAA,CAAK,QAAA,CAAS,EAAA,CAAG,aAAA,EAAe,QAAQ,CAAC,CAAA;AAChD,EAAA,MAAA,CAAO,IAAA,CAAK,QAAA,CAAS,EAAA,CAAG,YAAA,EAAc,QAAQ,CAAC,CAAA;AAC/C,EAAA,MAAA,CAAO,IAAA,CAAK,QAAA,CAAS,EAAA,CAAG,MAAA,EAAQ,QAAQ,CAAC,CAAA;AACzC,EAAA,MAAA,CAAO,IAAA,CAAK,QAAA,CAAS,EAAA,CAAG,OAAA,EAAS,QAAQ,CAAC,CAAA;AAE1C,EAAA,OAAO,MAAM;AACX,IAAA,KAAA,MAAW,KAAA,IAAS,QAAQ,KAAA,EAAM;AAAA,EACpC,CAAA;AACF;AAQO,SAAS,OAAA,CACd,UACA,QAAA,EACY;AACZ,EAAA,IAAI,KAAA,GAAuB,IAAA;AAE3B,EAAA,SAAS,IAAA,GAAO;AACd,IAAA,QAAA,CAAS,WAAA,CAAY,QAAQ,CAAC,CAAA;AAC9B,IAAA,KAAA,GAAQ,sBAAsB,IAAI,CAAA;AAAA,EACpC;AAEA,EAAA,KAAA,GAAQ,sBAAsB,IAAI,CAAA;AAElC,EAAA,OAAO,MAAM;AACX,IAAA,IAAI,UAAU,IAAA,EAAM;AAClB,MAAA,oBAAA,CAAqB,KAAK,CAAA;AAC1B,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AAAA,EACF,CAAA;AACF;AAMO,SAAS,UAAU,QAAA,EAAmC;AAC3D,EAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY;AACpC,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,EAAA,CAAG,OAAA,EAAS,MAAM;AACvC,MAAA,KAAA,EAAM;AACN,MAAA,OAAA,EAAQ;AAAA,IACV,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AACH;AAMO,SAAS,YAAA,CACd,UACA,QAAA,EACe;AACf,EAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY;AACpC,IAAA,MAAM,KAAA,GAAQ,SAAS,EAAA,CAAG,YAAA,EAAc,CAAC,EAAE,QAAA,EAAU,SAAQ,KAAM;AACjE,MAAA,IAAI,WAAW,QAAA,EAAU;AACvB,QAAA,KAAA,EAAM;AACN,QAAA,OAAA,EAAQ;AAAA,MACV;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AACH","file":"chunk-HTGOHC73.cjs","sourcesContent":["// ---------------------------------------------------------------------------\n// M10: Framework adapters\n// ---------------------------------------------------------------------------\n\nimport type { Playback, PlaybackSnapshot } from \"./types.js\";\n\n/**\n * Get an immutable snapshot of the current playback state.\n * Designed for use with React's `useSyncExternalStore` or similar patterns.\n */\nexport function getSnapshot(playback: Playback): PlaybackSnapshot {\n const base: PlaybackSnapshot = {\n state: playback.getState(),\n position: playback.getCurrentTime(),\n duration: playback.getDuration(),\n progress: playback.getProgress(),\n };\n\n // Include stretcher snapshot if available (no static import required)\n const getter = (playback as unknown as Record<string, unknown>)[\"_getStretcherSnapshot\"];\n if (typeof getter === \"function\") {\n const stretcher = (getter as () => PlaybackSnapshot[\"stretcher\"])();\n if (stretcher) {\n base.stretcher = stretcher;\n }\n }\n\n return base;\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 * const snap = useSyncExternalStore(\n * (cb) => subscribeSnapshot(playback, cb),\n * () => getSnapshot(playback),\n * );\n * ```\n */\nexport function subscribeSnapshot(\n playback: Playback,\n callback: () => void,\n): () => void {\n const unsubs: Array<() => void> = [];\n\n unsubs.push(playback.on(\"statechange\", callback));\n unsubs.push(playback.on(\"timeupdate\", callback));\n unsubs.push(playback.on(\"seek\", callback));\n unsubs.push(playback.on(\"ended\", callback));\n\n return () => {\n for (const unsub of unsubs) unsub();\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 callback(getSnapshot(playback));\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(\n playback: Playback,\n position: number,\n): Promise<void> {\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"]}
@@ -0,0 +1,33 @@
1
+ // src/synth.ts
2
+ function createSineBuffer(ctx, frequency, duration) {
3
+ const length = Math.ceil(ctx.sampleRate * duration);
4
+ const buffer = ctx.createBuffer(1, length, ctx.sampleRate);
5
+ const data = buffer.getChannelData(0);
6
+ for (let i = 0; i < length; i++) {
7
+ data[i] = Math.sin(2 * Math.PI * frequency * i / ctx.sampleRate);
8
+ }
9
+ return buffer;
10
+ }
11
+ function createNoiseBuffer(ctx, duration) {
12
+ const length = Math.ceil(ctx.sampleRate * duration);
13
+ const buffer = ctx.createBuffer(1, length, ctx.sampleRate);
14
+ const data = buffer.getChannelData(0);
15
+ for (let i = 0; i < length; i++) {
16
+ data[i] = Math.random() * 2 - 1;
17
+ }
18
+ return buffer;
19
+ }
20
+ function createClickBuffer(ctx, frequency, duration) {
21
+ const length = Math.ceil(ctx.sampleRate * duration);
22
+ const buffer = ctx.createBuffer(1, length, ctx.sampleRate);
23
+ const data = buffer.getChannelData(0);
24
+ for (let i = 0; i < length; i++) {
25
+ const envelope = Math.exp(-5 * i / length);
26
+ data[i] = envelope * Math.sin(2 * Math.PI * frequency * i / ctx.sampleRate);
27
+ }
28
+ return buffer;
29
+ }
30
+
31
+ export { createClickBuffer, createNoiseBuffer, createSineBuffer };
32
+ //# sourceMappingURL=chunk-LETS7FKB.js.map
33
+ //# sourceMappingURL=chunk-LETS7FKB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/synth.ts"],"names":[],"mappings":";AAQO,SAAS,gBAAA,CACd,GAAA,EACA,SAAA,EACA,QAAA,EACa;AACb,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,aAAa,QAAQ,CAAA;AAClD,EAAA,MAAM,SAAS,GAAA,CAAI,YAAA,CAAa,CAAA,EAAG,MAAA,EAAQ,IAAI,UAAU,CAAA;AACzD,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,cAAA,CAAe,CAAC,CAAA;AAEpC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,MAAA,EAAQ,CAAA,EAAA,EAAK;AAC/B,IAAA,IAAA,CAAK,CAAC,CAAA,GAAI,IAAA,CAAK,GAAA,CAAK,CAAA,GAAI,KAAK,EAAA,GAAK,SAAA,GAAY,CAAA,GAAK,GAAA,CAAI,UAAU,CAAA;AAAA,EACnE;AAEA,EAAA,OAAO,MAAA;AACT;AAKO,SAAS,iBAAA,CACd,KACA,QAAA,EACa;AACb,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,aAAa,QAAQ,CAAA;AAClD,EAAA,MAAM,SAAS,GAAA,CAAI,YAAA,CAAa,CAAA,EAAG,MAAA,EAAQ,IAAI,UAAU,CAAA;AACzD,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,cAAA,CAAe,CAAC,CAAA;AAEpC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,MAAA,EAAQ,CAAA,EAAA,EAAK;AAC/B,IAAA,IAAA,CAAK,CAAC,CAAA,GAAI,IAAA,CAAK,MAAA,KAAW,CAAA,GAAI,CAAA;AAAA,EAChC;AAEA,EAAA,OAAO,MAAA;AACT;AAKO,SAAS,iBAAA,CACd,GAAA,EACA,SAAA,EACA,QAAA,EACa;AACb,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,aAAa,QAAQ,CAAA;AAClD,EAAA,MAAM,SAAS,GAAA,CAAI,YAAA,CAAa,CAAA,EAAG,MAAA,EAAQ,IAAI,UAAU,CAAA;AACzD,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,cAAA,CAAe,CAAC,CAAA;AAEpC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,MAAA,EAAQ,CAAA,EAAA,EAAK;AAE/B,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAK,EAAA,GAAK,IAAK,MAAM,CAAA;AAC3C,IAAA,IAAA,CAAK,CAAC,CAAA,GACJ,QAAA,GAAW,IAAA,CAAK,GAAA,CAAK,CAAA,GAAI,IAAA,CAAK,EAAA,GAAK,SAAA,GAAY,CAAA,GAAK,GAAA,CAAI,UAAU,CAAA;AAAA,EACtE;AAEA,EAAA,OAAO,MAAA;AACT","file":"chunk-LETS7FKB.js","sourcesContent":["// ---------------------------------------------------------------------------\n// M9: Buffer synthesis\n// ---------------------------------------------------------------------------\n\n/**\n * Create an `AudioBuffer` containing a sine wave.\n * Useful for test tones and debugging.\n */\nexport function createSineBuffer(\n ctx: AudioContext,\n frequency: number,\n duration: number,\n): AudioBuffer {\n const length = Math.ceil(ctx.sampleRate * duration);\n const buffer = ctx.createBuffer(1, length, ctx.sampleRate);\n const data = buffer.getChannelData(0);\n\n for (let i = 0; i < length; i++) {\n data[i] = Math.sin((2 * Math.PI * frequency * i) / ctx.sampleRate);\n }\n\n return buffer;\n}\n\n/**\n * Create an `AudioBuffer` containing white noise.\n */\nexport function createNoiseBuffer(\n ctx: AudioContext,\n duration: number,\n): AudioBuffer {\n const length = Math.ceil(ctx.sampleRate * duration);\n const buffer = ctx.createBuffer(1, length, ctx.sampleRate);\n const data = buffer.getChannelData(0);\n\n for (let i = 0; i < length; i++) {\n data[i] = Math.random() * 2 - 1;\n }\n\n return buffer;\n}\n\n/**\n * Create an `AudioBuffer` containing a short click/impulse.\n */\nexport function createClickBuffer(\n ctx: AudioContext,\n frequency: number,\n duration: number,\n): AudioBuffer {\n const length = Math.ceil(ctx.sampleRate * duration);\n const buffer = ctx.createBuffer(1, length, ctx.sampleRate);\n const data = buffer.getChannelData(0);\n\n for (let i = 0; i < length; i++) {\n // Exponential decay envelope.\n const envelope = Math.exp((-5 * i) / length);\n data[i] =\n envelope * Math.sin((2 * Math.PI * frequency * i) / ctx.sampleRate);\n }\n\n return buffer;\n}\n"]}
@@ -0,0 +1,84 @@
1
+ 'use strict';
2
+
3
+ // src/nodes.ts
4
+ function createGain(ctx, initialValue) {
5
+ const gain = ctx.createGain();
6
+ if (initialValue !== void 0) {
7
+ gain.gain.value = initialValue;
8
+ }
9
+ return gain;
10
+ }
11
+ function rampGain(gain, target, duration) {
12
+ const now = gain.context.currentTime;
13
+ gain.gain.cancelScheduledValues(now);
14
+ gain.gain.setValueAtTime(gain.gain.value, now);
15
+ gain.gain.linearRampToValueAtTime(target, now + duration);
16
+ }
17
+ function createAnalyser(ctx, options) {
18
+ const analyser = ctx.createAnalyser();
19
+ if (options?.fftSize !== void 0) analyser.fftSize = options.fftSize;
20
+ if (options?.smoothingTimeConstant !== void 0) {
21
+ analyser.smoothingTimeConstant = options.smoothingTimeConstant;
22
+ }
23
+ return analyser;
24
+ }
25
+ function getFrequencyData(analyser) {
26
+ const data = new Float32Array(analyser.frequencyBinCount);
27
+ analyser.getFloatFrequencyData(data);
28
+ return data;
29
+ }
30
+ function getFrequencyDataByte(analyser) {
31
+ const data = new Uint8Array(analyser.frequencyBinCount);
32
+ analyser.getByteFrequencyData(data);
33
+ return data;
34
+ }
35
+ function createFilter(ctx, options) {
36
+ const filter = ctx.createBiquadFilter();
37
+ if (options?.type !== void 0) filter.type = options.type;
38
+ if (options?.frequency !== void 0) filter.frequency.value = options.frequency;
39
+ if (options?.Q !== void 0) filter.Q.value = options.Q;
40
+ if (options?.gain !== void 0) filter.gain.value = options.gain;
41
+ return filter;
42
+ }
43
+ function createPanner(ctx, pan) {
44
+ const panner = ctx.createStereoPanner();
45
+ if (pan !== void 0) {
46
+ panner.pan.value = pan;
47
+ }
48
+ return panner;
49
+ }
50
+ function createCompressor(ctx, options) {
51
+ const comp = ctx.createDynamicsCompressor();
52
+ if (options?.threshold !== void 0) comp.threshold.value = options.threshold;
53
+ if (options?.knee !== void 0) comp.knee.value = options.knee;
54
+ if (options?.ratio !== void 0) comp.ratio.value = options.ratio;
55
+ if (options?.attack !== void 0) comp.attack.value = options.attack;
56
+ if (options?.release !== void 0) comp.release.value = options.release;
57
+ return comp;
58
+ }
59
+ function chain(...nodes) {
60
+ for (let i = 0; i < nodes.length - 1; i++) {
61
+ nodes[i].connect(nodes[i + 1]);
62
+ }
63
+ }
64
+ function disconnectChain(...nodes) {
65
+ for (const node of nodes) {
66
+ try {
67
+ node.disconnect();
68
+ } catch {
69
+ }
70
+ }
71
+ }
72
+
73
+ exports.chain = chain;
74
+ exports.createAnalyser = createAnalyser;
75
+ exports.createCompressor = createCompressor;
76
+ exports.createFilter = createFilter;
77
+ exports.createGain = createGain;
78
+ exports.createPanner = createPanner;
79
+ exports.disconnectChain = disconnectChain;
80
+ exports.getFrequencyData = getFrequencyData;
81
+ exports.getFrequencyDataByte = getFrequencyDataByte;
82
+ exports.rampGain = rampGain;
83
+ //# sourceMappingURL=chunk-M5PDY5EZ.cjs.map
84
+ //# sourceMappingURL=chunk-M5PDY5EZ.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/nodes.ts"],"names":[],"mappings":"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","file":"chunk-M5PDY5EZ.cjs","sourcesContent":["// ---------------------------------------------------------------------------\n// M5: Audio node factory & graph utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Create a `GainNode` with an optional initial value.\n */\nexport function createGain(\n ctx: AudioContext,\n initialValue?: number,\n): GainNode {\n const gain = ctx.createGain();\n if (initialValue !== undefined) {\n gain.gain.value = initialValue;\n }\n return gain;\n}\n\n/**\n * Smoothly ramp a `GainNode` to a target value over `duration` seconds using\n * `linearRampToValueAtTime`. This avoids audible clicks when changing volume.\n */\nexport function rampGain(\n gain: GainNode,\n target: number,\n duration: number,\n): void {\n const now = gain.context.currentTime;\n gain.gain.cancelScheduledValues(now);\n gain.gain.setValueAtTime(gain.gain.value, now);\n gain.gain.linearRampToValueAtTime(target, now + duration);\n}\n\n/**\n * Create an `AnalyserNode`.\n */\nexport function createAnalyser(\n ctx: AudioContext,\n options?: { fftSize?: number; smoothingTimeConstant?: number },\n): AnalyserNode {\n const analyser = ctx.createAnalyser();\n if (options?.fftSize !== undefined) analyser.fftSize = options.fftSize;\n if (options?.smoothingTimeConstant !== undefined) {\n analyser.smoothingTimeConstant = options.smoothingTimeConstant;\n }\n return analyser;\n}\n\n/**\n * Get the current frequency data from an `AnalyserNode` as `Float32Array`.\n */\nexport function getFrequencyData(analyser: AnalyserNode): Float32Array {\n const data = new Float32Array(analyser.frequencyBinCount);\n analyser.getFloatFrequencyData(data);\n return data;\n}\n\n/**\n * Get the current frequency data from an `AnalyserNode` as `Uint8Array`.\n */\nexport function getFrequencyDataByte(analyser: AnalyserNode): Uint8Array {\n const data = new Uint8Array(analyser.frequencyBinCount);\n analyser.getByteFrequencyData(data);\n return data;\n}\n\n/**\n * Create a `BiquadFilterNode`.\n */\nexport function createFilter(\n ctx: AudioContext,\n options?: {\n type?: BiquadFilterType;\n frequency?: number;\n Q?: number;\n gain?: number;\n },\n): BiquadFilterNode {\n const filter = ctx.createBiquadFilter();\n if (options?.type !== undefined) filter.type = options.type;\n if (options?.frequency !== undefined) filter.frequency.value = options.frequency;\n if (options?.Q !== undefined) filter.Q.value = options.Q;\n if (options?.gain !== undefined) filter.gain.value = options.gain;\n return filter;\n}\n\n/**\n * Create a `StereoPannerNode`.\n */\nexport function createPanner(\n ctx: AudioContext,\n pan?: number,\n): StereoPannerNode {\n const panner = ctx.createStereoPanner();\n if (pan !== undefined) {\n panner.pan.value = pan;\n }\n return panner;\n}\n\n/**\n * Create a `DynamicsCompressorNode`.\n */\nexport function createCompressor(\n ctx: AudioContext,\n options?: {\n threshold?: number;\n knee?: number;\n ratio?: number;\n attack?: number;\n release?: number;\n },\n): DynamicsCompressorNode {\n const comp = ctx.createDynamicsCompressor();\n if (options?.threshold !== undefined) comp.threshold.value = options.threshold;\n if (options?.knee !== undefined) comp.knee.value = options.knee;\n if (options?.ratio !== undefined) comp.ratio.value = options.ratio;\n if (options?.attack !== undefined) comp.attack.value = options.attack;\n if (options?.release !== undefined) comp.release.value = options.release;\n return comp;\n}\n\n/**\n * Connect a series of `AudioNode`s in order (serial chain).\n *\n * ```ts\n * chain(source, gain, analyser, ctx.destination);\n * ```\n */\nexport function chain(...nodes: AudioNode[]): void {\n for (let i = 0; i < nodes.length - 1; i++) {\n nodes[i]!.connect(nodes[i + 1]!);\n }\n}\n\n/**\n * Disconnect a series of `AudioNode`s that were previously chained.\n */\nexport function disconnectChain(...nodes: AudioNode[]): void {\n for (const node of nodes) {\n try {\n node.disconnect();\n } catch {\n // Already disconnected — safe to ignore.\n }\n }\n}\n"]}
@@ -0,0 +1,358 @@
1
+ 'use strict';
2
+
3
+ var chunk5J7S6QV3_cjs = require('./chunk-5J7S6QV3.cjs');
4
+
5
+ // src/play.ts
6
+ function play(ctx, buffer, options) {
7
+ const { preservePitch = true } = options ?? {};
8
+ if (preservePitch) {
9
+ return createStretchedPlayback(ctx, buffer, options ?? {});
10
+ }
11
+ const {
12
+ offset: initialOffset = 0,
13
+ loop = false,
14
+ loopStart,
15
+ loopEnd,
16
+ playbackRate: initialRate = 1,
17
+ through = [],
18
+ destination = ctx.destination,
19
+ timeupdateInterval = 50
20
+ } = options ?? {};
21
+ const emitter = chunk5J7S6QV3_cjs.createEmitter();
22
+ const duration = buffer.duration;
23
+ let state = "stopped";
24
+ let sourceNode = null;
25
+ let startedAt = 0;
26
+ let pausedAt = initialOffset;
27
+ let currentRate = initialRate;
28
+ let isLooping = loop;
29
+ let timerId = null;
30
+ let disposed = false;
31
+ function createSource() {
32
+ const src = ctx.createBufferSource();
33
+ src.buffer = buffer;
34
+ src.playbackRate.value = currentRate;
35
+ src.loop = isLooping;
36
+ if (loopStart !== void 0) src.loopStart = loopStart;
37
+ if (loopEnd !== void 0) src.loopEnd = loopEnd;
38
+ if (through.length > 0) {
39
+ src.connect(through[0]);
40
+ for (let i = 0; i < through.length - 1; i++) {
41
+ through[i].connect(through[i + 1]);
42
+ }
43
+ through[through.length - 1].connect(destination);
44
+ } else {
45
+ src.connect(destination);
46
+ }
47
+ src.onended = handleEnded;
48
+ return src;
49
+ }
50
+ function startSource(positionInBuffer) {
51
+ sourceNode = createSource();
52
+ sourceNode.start(0, positionInBuffer);
53
+ startedAt = ctx.currentTime - positionInBuffer / currentRate;
54
+ }
55
+ function stopSource() {
56
+ if (sourceNode) {
57
+ sourceNode.onended = null;
58
+ try {
59
+ sourceNode.stop();
60
+ } catch {
61
+ }
62
+ sourceNode.disconnect();
63
+ sourceNode = null;
64
+ }
65
+ }
66
+ function handleEnded() {
67
+ if (state !== "playing") return;
68
+ if (isLooping) {
69
+ emitter.emit("loop", void 0);
70
+ return;
71
+ }
72
+ setState("stopped");
73
+ pausedAt = 0;
74
+ stopTimer();
75
+ emitter.emit("ended", void 0);
76
+ }
77
+ function setState(next) {
78
+ if (state === next) return;
79
+ state = next;
80
+ emitter.emit("statechange", { state: next });
81
+ }
82
+ function startTimer() {
83
+ if (timerId !== null) return;
84
+ timerId = setInterval(() => {
85
+ if (state !== "playing") return;
86
+ emitter.emit("timeupdate", {
87
+ position: getCurrentTime(),
88
+ duration
89
+ });
90
+ }, timeupdateInterval);
91
+ }
92
+ function stopTimer() {
93
+ if (timerId !== null) {
94
+ clearInterval(timerId);
95
+ timerId = null;
96
+ }
97
+ }
98
+ function getCurrentTime() {
99
+ if (state === "playing") {
100
+ const elapsed = (ctx.currentTime - startedAt) * currentRate;
101
+ if (isLooping) {
102
+ const loopDur = (loopEnd ?? duration) - (loopStart ?? 0);
103
+ return (elapsed - (loopStart ?? 0)) % loopDur + (loopStart ?? 0);
104
+ }
105
+ return Math.min(elapsed, duration);
106
+ }
107
+ if (state === "paused") return pausedAt;
108
+ return 0;
109
+ }
110
+ function pause() {
111
+ if (state !== "playing" || disposed) return;
112
+ pausedAt = getCurrentTime();
113
+ stopSource();
114
+ stopTimer();
115
+ setState("paused");
116
+ emitter.emit("pause", void 0);
117
+ }
118
+ function resume() {
119
+ if (state !== "paused" || disposed) return;
120
+ startSource(pausedAt);
121
+ setState("playing");
122
+ startTimer();
123
+ emitter.emit("resume", void 0);
124
+ }
125
+ function togglePlayPause() {
126
+ if (state === "playing") pause();
127
+ else if (state === "paused") resume();
128
+ }
129
+ function seek(position) {
130
+ if (disposed) return;
131
+ const clamped = Math.max(0, Math.min(position, duration));
132
+ const wasPlaying = state === "playing";
133
+ stopSource();
134
+ stopTimer();
135
+ pausedAt = clamped;
136
+ if (wasPlaying) {
137
+ startSource(clamped);
138
+ startTimer();
139
+ }
140
+ emitter.emit("seek", { position: clamped });
141
+ }
142
+ function stop() {
143
+ if (state === "stopped" || disposed) return;
144
+ stopSource();
145
+ stopTimer();
146
+ pausedAt = 0;
147
+ setState("stopped");
148
+ emitter.emit("stop", void 0);
149
+ }
150
+ function setPlaybackRate(rate) {
151
+ const position = getCurrentTime();
152
+ currentRate = rate;
153
+ if (sourceNode) {
154
+ sourceNode.playbackRate.value = rate;
155
+ startedAt = ctx.currentTime - position / rate;
156
+ }
157
+ }
158
+ function setLoop(value) {
159
+ isLooping = value;
160
+ if (sourceNode) {
161
+ sourceNode.loop = value;
162
+ }
163
+ }
164
+ function dispose() {
165
+ if (disposed) return;
166
+ disposed = true;
167
+ stopSource();
168
+ stopTimer();
169
+ emitter.clear();
170
+ }
171
+ startSource(initialOffset);
172
+ setState("playing");
173
+ startTimer();
174
+ emitter.emit("play", void 0);
175
+ return {
176
+ getState: () => state,
177
+ getCurrentTime,
178
+ getDuration: () => duration,
179
+ getProgress: () => duration > 0 ? getCurrentTime() / duration : 0,
180
+ pause,
181
+ resume,
182
+ togglePlayPause,
183
+ seek,
184
+ stop,
185
+ setPlaybackRate,
186
+ setLoop,
187
+ on: emitter.on.bind(emitter),
188
+ off: emitter.off.bind(emitter),
189
+ dispose
190
+ };
191
+ }
192
+ function createStretchedPlayback(ctx, buffer, options) {
193
+ const {
194
+ offset: initialOffset = 0,
195
+ playbackRate: initialRate = 1,
196
+ through = [],
197
+ destination = ctx.destination,
198
+ timeupdateInterval = 50
199
+ } = options;
200
+ const emitter = chunk5J7S6QV3_cjs.createEmitter();
201
+ const duration = buffer.duration;
202
+ let state = "playing";
203
+ let engineInstance = null;
204
+ let timerId = null;
205
+ let disposed = false;
206
+ let currentRate = initialRate;
207
+ let pendingSeek = null;
208
+ emitter.emit("statechange", { state: "playing" });
209
+ emitter.emit("play", void 0);
210
+ import('./engine-5JK2FCNL.cjs').then(({ createStretcherEngine }) => {
211
+ if (disposed) return;
212
+ engineInstance = createStretcherEngine(ctx, buffer, {
213
+ tempo: currentRate,
214
+ offset: initialOffset,
215
+ through,
216
+ destination,
217
+ timeupdateInterval
218
+ });
219
+ engineInstance.on("buffering", (data) => {
220
+ if (disposed) return;
221
+ emitter.emit("buffering", data);
222
+ });
223
+ engineInstance.on("buffered", (data) => {
224
+ if (disposed) return;
225
+ emitter.emit("buffered", data);
226
+ });
227
+ engineInstance.on("ended", () => {
228
+ if (disposed) return;
229
+ state = "stopped";
230
+ stopTimer();
231
+ emitter.emit("statechange", { state: "stopped" });
232
+ emitter.emit("ended", void 0);
233
+ });
234
+ engineInstance.on("error", (data) => {
235
+ if (disposed) return;
236
+ if (data.fatal) {
237
+ state = "stopped";
238
+ emitter.emit("statechange", { state: "stopped" });
239
+ emitter.emit("ended", void 0);
240
+ }
241
+ });
242
+ engineInstance.start();
243
+ startTimer();
244
+ if (pendingSeek !== null) {
245
+ engineInstance.seek(pendingSeek);
246
+ pendingSeek = null;
247
+ }
248
+ if (state === "paused") {
249
+ engineInstance.pause();
250
+ } else if (state === "stopped") {
251
+ engineInstance.stop();
252
+ }
253
+ });
254
+ function startTimer() {
255
+ if (timerId !== null) return;
256
+ timerId = setInterval(() => {
257
+ if (state !== "playing" || disposed) return;
258
+ emitter.emit("timeupdate", {
259
+ position: getCurrentTime(),
260
+ duration
261
+ });
262
+ }, timeupdateInterval);
263
+ }
264
+ function stopTimer() {
265
+ if (timerId !== null) {
266
+ clearInterval(timerId);
267
+ timerId = null;
268
+ }
269
+ }
270
+ function getCurrentTime() {
271
+ if (pendingSeek !== null) {
272
+ return pendingSeek;
273
+ }
274
+ if (engineInstance) {
275
+ return engineInstance.getCurrentPosition();
276
+ }
277
+ return initialOffset;
278
+ }
279
+ function pause() {
280
+ if (state !== "playing" || disposed) return;
281
+ state = "paused";
282
+ engineInstance?.pause();
283
+ stopTimer();
284
+ emitter.emit("statechange", { state: "paused" });
285
+ emitter.emit("pause", void 0);
286
+ }
287
+ function resume() {
288
+ if (state !== "paused" || disposed) return;
289
+ state = "playing";
290
+ engineInstance?.resume();
291
+ startTimer();
292
+ emitter.emit("statechange", { state: "playing" });
293
+ emitter.emit("resume", void 0);
294
+ }
295
+ function togglePlayPause() {
296
+ if (state === "playing") pause();
297
+ else if (state === "paused") resume();
298
+ }
299
+ function seek(position) {
300
+ if (disposed) return;
301
+ const clamped = Math.max(0, Math.min(position, duration));
302
+ if (engineInstance) {
303
+ engineInstance.seek(clamped);
304
+ } else {
305
+ pendingSeek = clamped;
306
+ }
307
+ emitter.emit("seek", { position: clamped });
308
+ }
309
+ function stop() {
310
+ if (state === "stopped" || disposed) return;
311
+ state = "stopped";
312
+ engineInstance?.stop();
313
+ stopTimer();
314
+ emitter.emit("statechange", { state: "stopped" });
315
+ emitter.emit("stop", void 0);
316
+ }
317
+ function setPlaybackRate(rate) {
318
+ currentRate = rate;
319
+ if (engineInstance) {
320
+ engineInstance.setTempo(rate);
321
+ }
322
+ }
323
+ function setLoop(_value) {
324
+ }
325
+ function dispose() {
326
+ if (disposed) return;
327
+ disposed = true;
328
+ stopTimer();
329
+ engineInstance?.dispose();
330
+ emitter.clear();
331
+ }
332
+ function _getStretcherSnapshot() {
333
+ if (!engineInstance) return null;
334
+ return engineInstance.getSnapshot();
335
+ }
336
+ const playback = {
337
+ getState: () => state,
338
+ getCurrentTime,
339
+ getDuration: () => duration,
340
+ getProgress: () => duration > 0 ? getCurrentTime() / duration : 0,
341
+ pause,
342
+ resume,
343
+ togglePlayPause,
344
+ seek,
345
+ stop,
346
+ setPlaybackRate,
347
+ setLoop,
348
+ on: emitter.on.bind(emitter),
349
+ off: emitter.off.bind(emitter),
350
+ dispose,
351
+ _getStretcherSnapshot
352
+ };
353
+ return playback;
354
+ }
355
+
356
+ exports.play = play;
357
+ //# sourceMappingURL=chunk-PZE6HTZR.cjs.map
358
+ //# sourceMappingURL=chunk-PZE6HTZR.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/play.ts"],"names":["createEmitter"],"mappings":"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","file":"chunk-PZE6HTZR.cjs","sourcesContent":["// ---------------------------------------------------------------------------\n// M3: Playback engine\n// ---------------------------------------------------------------------------\n\nimport { createEmitter } from \"./emitter.js\";\nimport type {\n Playback,\n PlaybackEventMap,\n PlaybackState,\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(\n ctx: AudioContext,\n buffer: AudioBuffer,\n options?: PlayOptions,\n): 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 state: PlaybackState = \"stopped\";\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;\n let isLooping = loop;\n let timerId: ReturnType<typeof setInterval> | null = null;\n let disposed = false;\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 (state !== \"playing\") return;\n if (isLooping) {\n emitter.emit(\"loop\", undefined as never);\n return;\n }\n setState(\"stopped\");\n pausedAt = 0;\n stopTimer();\n emitter.emit(\"ended\", undefined as never);\n }\n\n function setState(next: PlaybackState) {\n if (state === next) return;\n state = next;\n emitter.emit(\"statechange\", { state: next });\n }\n\n function startTimer() {\n if (timerId !== null) return;\n timerId = setInterval(() => {\n if (state !== \"playing\") return;\n emitter.emit(\"timeupdate\", {\n position: getCurrentTime(),\n duration,\n });\n }, timeupdateInterval);\n }\n\n function stopTimer() {\n if (timerId !== null) {\n clearInterval(timerId);\n timerId = null;\n }\n }\n\n // ----- public API -----\n\n function getCurrentTime(): number {\n if (state === \"playing\") {\n const elapsed = (ctx.currentTime - startedAt) * currentRate;\n if (isLooping) {\n const loopDur =\n (loopEnd ?? duration) - (loopStart ?? 0);\n return ((elapsed - (loopStart ?? 0)) % loopDur) + (loopStart ?? 0);\n }\n return Math.min(elapsed, duration);\n }\n if (state === \"paused\") return pausedAt;\n return 0;\n }\n\n function pause() {\n if (state !== \"playing\" || disposed) return;\n pausedAt = getCurrentTime();\n stopSource();\n stopTimer();\n setState(\"paused\");\n emitter.emit(\"pause\", undefined as never);\n }\n\n function resume() {\n if (state !== \"paused\" || disposed) return;\n startSource(pausedAt);\n setState(\"playing\");\n startTimer();\n emitter.emit(\"resume\", undefined as never);\n }\n\n function togglePlayPause() {\n if (state === \"playing\") pause();\n else if (state === \"paused\") resume();\n }\n\n function seek(position: number) {\n if (disposed) return;\n const clamped = Math.max(0, Math.min(position, duration));\n const wasPlaying = state === \"playing\";\n\n stopSource();\n stopTimer();\n\n pausedAt = clamped;\n\n if (wasPlaying) {\n startSource(clamped);\n startTimer();\n }\n\n emitter.emit(\"seek\", { position: clamped });\n }\n\n function stop() {\n if (state === \"stopped\" || disposed) return;\n stopSource();\n stopTimer();\n pausedAt = 0;\n setState(\"stopped\");\n emitter.emit(\"stop\", undefined as never);\n }\n\n function setPlaybackRate(rate: number) {\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 (disposed) return;\n disposed = true;\n stopSource();\n stopTimer();\n emitter.clear();\n }\n\n // ----- kick off initial playback -----\n\n startSource(initialOffset);\n setState(\"playing\");\n startTimer();\n emitter.emit(\"play\", undefined as never);\n\n return {\n getState: () => state,\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 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 state: PlaybackState = \"playing\";\n let engineInstance: import(\"./stretcher/types.js\").StretcherEngine | null =\n null;\n let timerId: ReturnType<typeof setInterval> | null = null;\n let disposed = false;\n let currentRate = initialRate;\n let pendingSeek: number | null = null;\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\").then(({ createStretcherEngine }) => {\n if (disposed) return;\n\n engineInstance = createStretcherEngine(ctx, buffer, {\n tempo: currentRate,\n offset: initialOffset,\n through,\n destination,\n timeupdateInterval,\n });\n\n // Wire stretcher events to playback events\n engineInstance.on(\"buffering\", (data) => {\n if (disposed) return;\n emitter.emit(\"buffering\", data);\n });\n\n engineInstance.on(\"buffered\", (data) => {\n if (disposed) return;\n emitter.emit(\"buffered\", data);\n });\n\n engineInstance.on(\"ended\", () => {\n if (disposed) return;\n state = \"stopped\";\n stopTimer();\n emitter.emit(\"statechange\", { state: \"stopped\" });\n emitter.emit(\"ended\", undefined as never);\n });\n\n engineInstance.on(\"error\", (data) => {\n if (disposed) return;\n if (data.fatal) {\n state = \"stopped\";\n emitter.emit(\"statechange\", { state: \"stopped\" });\n emitter.emit(\"ended\", undefined as never);\n }\n });\n\n // Start engine and timeupdate timer\n engineInstance.start();\n 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 (state === \"paused\") {\n engineInstance.pause();\n } else if (state === \"stopped\") {\n engineInstance.stop();\n }\n });\n\n function startTimer() {\n if (timerId !== null) return;\n timerId = setInterval(() => {\n if (state !== \"playing\" || disposed) return;\n emitter.emit(\"timeupdate\", {\n position: getCurrentTime(),\n duration,\n });\n }, timeupdateInterval);\n }\n\n function stopTimer() {\n if (timerId !== null) {\n clearInterval(timerId);\n timerId = null;\n }\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 (state !== \"playing\" || disposed) return;\n state = \"paused\";\n engineInstance?.pause();\n stopTimer();\n emitter.emit(\"statechange\", { state: \"paused\" });\n emitter.emit(\"pause\", undefined as never);\n }\n\n function resume() {\n if (state !== \"paused\" || disposed) return;\n state = \"playing\";\n engineInstance?.resume();\n startTimer();\n emitter.emit(\"statechange\", { state: \"playing\" });\n emitter.emit(\"resume\", undefined as never);\n }\n\n function togglePlayPause() {\n if (state === \"playing\") pause();\n else if (state === \"paused\") resume();\n }\n\n function seek(position: number) {\n if (disposed) 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 (state === \"stopped\" || disposed) return;\n state = \"stopped\";\n engineInstance?.stop();\n stopTimer();\n emitter.emit(\"statechange\", { state: \"stopped\" });\n emitter.emit(\"stop\", undefined as never);\n }\n\n function setPlaybackRate(rate: number) {\n currentRate = rate;\n if (engineInstance) {\n engineInstance.setTempo(rate);\n }\n }\n\n function setLoop(_value: boolean) {\n // Loop is not supported in stretcher mode\n }\n\n function dispose() {\n if (disposed) return;\n disposed = true;\n stopTimer();\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: () => state,\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"]}