@smoove/core 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.
- package/LICENSE +21 -0
- package/README.md +80 -0
- package/dist/animation/color.d.ts +9 -0
- package/dist/animation/color.d.ts.map +1 -0
- package/dist/animation/color.js +224 -0
- package/dist/animation/color.js.map +1 -0
- package/dist/animation/easing.d.ts +22 -0
- package/dist/animation/easing.d.ts.map +1 -0
- package/dist/animation/easing.js +59 -0
- package/dist/animation/easing.js.map +1 -0
- package/dist/animation/interpolate-colors.d.ts +7 -0
- package/dist/animation/interpolate-colors.d.ts.map +1 -0
- package/dist/animation/interpolate-colors.js +19 -0
- package/dist/animation/interpolate-colors.js.map +1 -0
- package/dist/animation/interpolate.d.ts +12 -0
- package/dist/animation/interpolate.d.ts.map +1 -0
- package/dist/animation/interpolate.js +65 -0
- package/dist/animation/interpolate.js.map +1 -0
- package/dist/audio/asset.d.ts +21 -0
- package/dist/audio/asset.d.ts.map +1 -0
- package/dist/audio/asset.js +2 -0
- package/dist/audio/asset.js.map +1 -0
- package/dist/audio/audio-driver.d.ts +34 -0
- package/dist/audio/audio-driver.d.ts.map +1 -0
- package/dist/audio/audio-driver.js +2 -0
- package/dist/audio/audio-driver.js.map +1 -0
- package/dist/audio/audio-for-preview.d.ts +12 -0
- package/dist/audio/audio-for-preview.d.ts.map +1 -0
- package/dist/audio/audio-for-preview.js +70 -0
- package/dist/audio/audio-for-preview.js.map +1 -0
- package/dist/audio/audio-for-rendering.d.ts +16 -0
- package/dist/audio/audio-for-rendering.d.ts.map +1 -0
- package/dist/audio/audio-for-rendering.js +32 -0
- package/dist/audio/audio-for-rendering.js.map +1 -0
- package/dist/audio/audio-source-browser.d.ts +30 -0
- package/dist/audio/audio-source-browser.d.ts.map +1 -0
- package/dist/audio/audio-source-browser.js +139 -0
- package/dist/audio/audio-source-browser.js.map +1 -0
- package/dist/audio/audio-source.d.ts +38 -0
- package/dist/audio/audio-source.d.ts.map +1 -0
- package/dist/audio/audio-source.js +2 -0
- package/dist/audio/audio-source.js.map +1 -0
- package/dist/audio/index.d.ts +45 -0
- package/dist/audio/index.d.ts.map +1 -0
- package/dist/audio/index.js +143 -0
- package/dist/audio/index.js.map +1 -0
- package/dist/audio/mixer.d.ts +43 -0
- package/dist/audio/mixer.d.ts.map +1 -0
- package/dist/audio/mixer.js +51 -0
- package/dist/audio/mixer.js.map +1 -0
- package/dist/audio/types.d.ts +31 -0
- package/dist/audio/types.d.ts.map +1 -0
- package/dist/audio/types.js +2 -0
- package/dist/audio/types.js.map +1 -0
- package/dist/block.d.ts +16 -0
- package/dist/block.d.ts.map +1 -0
- package/dist/block.js +173 -0
- package/dist/block.js.map +1 -0
- package/dist/composition.d.ts.map +1 -0
- package/dist/composition.js.map +1 -0
- package/dist/easing.d.ts +22 -0
- package/dist/easing.d.ts.map +1 -0
- package/dist/easing.js +59 -0
- package/dist/easing.js.map +1 -0
- package/dist/emitter.d.ts +7 -0
- package/dist/emitter.d.ts.map +1 -0
- package/dist/emitter.js +29 -0
- package/dist/emitter.js.map +1 -0
- package/dist/engine/composition.d.ts +182 -0
- package/dist/engine/composition.d.ts.map +1 -0
- package/dist/engine/composition.js +527 -0
- package/dist/engine/composition.js.map +1 -0
- package/dist/engine/emitter.d.ts +7 -0
- package/dist/engine/emitter.d.ts.map +1 -0
- package/dist/engine/emitter.js +29 -0
- package/dist/engine/emitter.js.map +1 -0
- package/dist/engine/environment.d.ts +30 -0
- package/dist/engine/environment.d.ts.map +1 -0
- package/dist/engine/environment.js +27 -0
- package/dist/engine/environment.js.map +1 -0
- package/dist/engine/offsets.d.ts +39 -0
- package/dist/engine/offsets.d.ts.map +1 -0
- package/dist/engine/offsets.js +54 -0
- package/dist/engine/offsets.js.map +1 -0
- package/dist/engine/runtime-defaults.d.ts +50 -0
- package/dist/engine/runtime-defaults.d.ts.map +1 -0
- package/dist/engine/runtime-defaults.js +77 -0
- package/dist/engine/runtime-defaults.js.map +1 -0
- package/dist/engine/sequence.d.ts +44 -0
- package/dist/engine/sequence.d.ts.map +1 -0
- package/dist/engine/sequence.js +101 -0
- package/dist/engine/sequence.js.map +1 -0
- package/dist/engine/series.d.ts +40 -0
- package/dist/engine/series.d.ts.map +1 -0
- package/dist/engine/series.js +58 -0
- package/dist/engine/series.js.map +1 -0
- package/dist/engine/signal.d.ts +10 -0
- package/dist/engine/signal.d.ts.map +1 -0
- package/dist/engine/signal.js +39 -0
- package/dist/engine/signal.js.map +1 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js.map +1 -0
- package/dist/flex-engine.d.ts +31 -0
- package/dist/flex-engine.d.ts.map +1 -0
- package/dist/flex-engine.js +186 -0
- package/dist/flex-engine.js.map +1 -0
- package/dist/flex-types.d.ts +165 -0
- package/dist/flex-types.d.ts.map +1 -0
- package/dist/flex-types.js +2 -0
- package/dist/flex-types.js.map +1 -0
- package/dist/flex.d.ts +26 -0
- package/dist/flex.d.ts.map +1 -0
- package/dist/flex.js +179 -0
- package/dist/flex.js.map +1 -0
- package/dist/image.d.ts +10 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +156 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/interpolate-colors.d.ts +7 -0
- package/dist/interpolate-colors.d.ts.map +1 -0
- package/dist/interpolate-colors.js +240 -0
- package/dist/interpolate-colors.js.map +1 -0
- package/dist/interpolate.d.ts +12 -0
- package/dist/interpolate.d.ts.map +1 -0
- package/dist/interpolate.js +65 -0
- package/dist/interpolate.js.map +1 -0
- package/dist/layout/block.d.ts +54 -0
- package/dist/layout/block.d.ts.map +1 -0
- package/dist/layout/block.js +186 -0
- package/dist/layout/block.js.map +1 -0
- package/dist/layout/contract.d.ts +41 -0
- package/dist/layout/contract.d.ts.map +1 -0
- package/dist/layout/contract.js +8 -0
- package/dist/layout/contract.js.map +1 -0
- package/dist/layout/flex/engine.d.ts +31 -0
- package/dist/layout/flex/engine.d.ts.map +1 -0
- package/dist/layout/flex/engine.js +186 -0
- package/dist/layout/flex/engine.js.map +1 -0
- package/dist/layout/flex/flex.d.ts +31 -0
- package/dist/layout/flex/flex.d.ts.map +1 -0
- package/dist/layout/flex/flex.js +175 -0
- package/dist/layout/flex/flex.js.map +1 -0
- package/dist/layout/flex/mixin.d.ts +52 -0
- package/dist/layout/flex/mixin.d.ts.map +1 -0
- package/dist/layout/flex/mixin.js +114 -0
- package/dist/layout/flex/mixin.js.map +1 -0
- package/dist/layout/flex/types.d.ts +31 -0
- package/dist/layout/flex/types.d.ts.map +1 -0
- package/dist/layout/flex/types.js +2 -0
- package/dist/layout/flex/types.js.map +1 -0
- package/dist/layout/image.d.ts +46 -0
- package/dist/layout/image.d.ts.map +1 -0
- package/dist/layout/image.js +198 -0
- package/dist/layout/image.js.map +1 -0
- package/dist/layout/shapes.d.ts +72 -0
- package/dist/layout/shapes.d.ts.map +1 -0
- package/dist/layout/shapes.js +29 -0
- package/dist/layout/shapes.js.map +1 -0
- package/dist/layout/text/font.d.ts +92 -0
- package/dist/layout/text/font.d.ts.map +1 -0
- package/dist/layout/text/font.js +166 -0
- package/dist/layout/text/font.js.map +1 -0
- package/dist/layout/text/geometry.d.ts +31 -0
- package/dist/layout/text/geometry.d.ts.map +1 -0
- package/dist/layout/text/geometry.js +81 -0
- package/dist/layout/text/geometry.js.map +1 -0
- package/dist/layout/text/text.d.ts +98 -0
- package/dist/layout/text/text.d.ts.map +1 -0
- package/dist/layout/text/text.js +676 -0
- package/dist/layout/text/text.js.map +1 -0
- package/dist/layout/text/types.d.ts +103 -0
- package/dist/layout/text/types.d.ts.map +1 -0
- package/dist/layout/text/types.js +2 -0
- package/dist/layout/text/types.js.map +1 -0
- package/dist/media/audio/asset.d.ts +21 -0
- package/dist/media/audio/asset.d.ts.map +1 -0
- package/dist/media/audio/asset.js +2 -0
- package/dist/media/audio/asset.js.map +1 -0
- package/dist/media/audio/audio-driver.d.ts +34 -0
- package/dist/media/audio/audio-driver.d.ts.map +1 -0
- package/dist/media/audio/audio-driver.js +2 -0
- package/dist/media/audio/audio-driver.js.map +1 -0
- package/dist/media/audio/audio-for-preview.d.ts +47 -0
- package/dist/media/audio/audio-for-preview.d.ts.map +1 -0
- package/dist/media/audio/audio-for-preview.js +232 -0
- package/dist/media/audio/audio-for-preview.js.map +1 -0
- package/dist/media/audio/audio-for-rendering.d.ts +16 -0
- package/dist/media/audio/audio-for-rendering.d.ts.map +1 -0
- package/dist/media/audio/audio-for-rendering.js +32 -0
- package/dist/media/audio/audio-for-rendering.js.map +1 -0
- package/dist/media/audio/audio-source-browser.d.ts +30 -0
- package/dist/media/audio/audio-source-browser.d.ts.map +1 -0
- package/dist/media/audio/audio-source-browser.js +139 -0
- package/dist/media/audio/audio-source-browser.js.map +1 -0
- package/dist/media/audio/audio-source-mediabunny.d.ts +45 -0
- package/dist/media/audio/audio-source-mediabunny.d.ts.map +1 -0
- package/dist/media/audio/audio-source-mediabunny.js +79 -0
- package/dist/media/audio/audio-source-mediabunny.js.map +1 -0
- package/dist/media/audio/audio-source.d.ts +38 -0
- package/dist/media/audio/audio-source.d.ts.map +1 -0
- package/dist/media/audio/audio-source.js +2 -0
- package/dist/media/audio/audio-source.js.map +1 -0
- package/dist/media/audio/index.d.ts +57 -0
- package/dist/media/audio/index.d.ts.map +1 -0
- package/dist/media/audio/index.js +170 -0
- package/dist/media/audio/index.js.map +1 -0
- package/dist/media/audio/mixer.d.ts +43 -0
- package/dist/media/audio/mixer.d.ts.map +1 -0
- package/dist/media/audio/mixer.js +51 -0
- package/dist/media/audio/mixer.js.map +1 -0
- package/dist/media/audio/shared-audio-context.d.ts +30 -0
- package/dist/media/audio/shared-audio-context.d.ts.map +1 -0
- package/dist/media/audio/shared-audio-context.js +68 -0
- package/dist/media/audio/shared-audio-context.js.map +1 -0
- package/dist/media/audio/types.d.ts +31 -0
- package/dist/media/audio/types.d.ts.map +1 -0
- package/dist/media/audio/types.js +2 -0
- package/dist/media/audio/types.js.map +1 -0
- package/dist/media/media-marker.d.ts +26 -0
- package/dist/media/media-marker.d.ts.map +1 -0
- package/dist/media/media-marker.js +26 -0
- package/dist/media/media-marker.js.map +1 -0
- package/dist/media/media-time.d.ts +17 -0
- package/dist/media/media-time.d.ts.map +1 -0
- package/dist/media/media-time.js +17 -0
- package/dist/media/media-time.js.map +1 -0
- package/dist/media/video/driver.d.ts +31 -0
- package/dist/media/video/driver.d.ts.map +1 -0
- package/dist/media/video/driver.js +3 -0
- package/dist/media/video/driver.js.map +1 -0
- package/dist/media/video/index.d.ts +66 -0
- package/dist/media/video/index.d.ts.map +1 -0
- package/dist/media/video/index.js +372 -0
- package/dist/media/video/index.js.map +1 -0
- package/dist/media/video/types.d.ts +36 -0
- package/dist/media/video/types.d.ts.map +1 -0
- package/dist/media/video/types.js +2 -0
- package/dist/media/video/types.js.map +1 -0
- package/dist/media/video/video-for-preview.d.ts +17 -0
- package/dist/media/video/video-for-preview.d.ts.map +1 -0
- package/dist/media/video/video-for-preview.js +30 -0
- package/dist/media/video/video-for-preview.js.map +1 -0
- package/dist/media/video/video-for-rendering.d.ts +17 -0
- package/dist/media/video/video-for-rendering.d.ts.map +1 -0
- package/dist/media/video/video-for-rendering.js +59 -0
- package/dist/media/video/video-for-rendering.js.map +1 -0
- package/dist/media/video/video-source-browser.d.ts +37 -0
- package/dist/media/video/video-source-browser.d.ts.map +1 -0
- package/dist/media/video/video-source-browser.js +171 -0
- package/dist/media/video/video-source-browser.js.map +1 -0
- package/dist/media/video/video-source-mediabunny.d.ts +69 -0
- package/dist/media/video/video-source-mediabunny.d.ts.map +1 -0
- package/dist/media/video/video-source-mediabunny.js +236 -0
- package/dist/media/video/video-source-mediabunny.js.map +1 -0
- package/dist/media/video/video-source.d.ts +48 -0
- package/dist/media/video/video-source.d.ts.map +1 -0
- package/dist/media/video/video-source.js +2 -0
- package/dist/media/video/video-source.js.map +1 -0
- package/dist/media-marker.d.ts +19 -0
- package/dist/media-marker.d.ts.map +1 -0
- package/dist/media-marker.js +19 -0
- package/dist/media-marker.js.map +1 -0
- package/dist/media-time.d.ts +17 -0
- package/dist/media-time.d.ts.map +1 -0
- package/dist/media-time.js +17 -0
- package/dist/media-time.js.map +1 -0
- package/dist/signal.d.ts +10 -0
- package/dist/signal.d.ts.map +1 -0
- package/dist/signal.js +39 -0
- package/dist/signal.js.map +1 -0
- package/dist/text.d.ts +91 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +704 -0
- package/dist/text.js.map +1 -0
- package/dist/video/driver.d.ts +31 -0
- package/dist/video/driver.d.ts.map +1 -0
- package/dist/video/driver.js +3 -0
- package/dist/video/driver.js.map +1 -0
- package/dist/video/index.d.ts +45 -0
- package/dist/video/index.d.ts.map +1 -0
- package/dist/video/index.js +276 -0
- package/dist/video/index.js.map +1 -0
- package/dist/video/types.d.ts +35 -0
- package/dist/video/types.d.ts.map +1 -0
- package/dist/video/types.js +2 -0
- package/dist/video/types.js.map +1 -0
- package/dist/video/video-for-preview.d.ts +12 -0
- package/dist/video/video-for-preview.d.ts.map +1 -0
- package/dist/video/video-for-preview.js +73 -0
- package/dist/video/video-for-preview.js.map +1 -0
- package/dist/video/video-for-rendering.d.ts +16 -0
- package/dist/video/video-for-rendering.d.ts.map +1 -0
- package/dist/video/video-for-rendering.js +45 -0
- package/dist/video/video-for-rendering.js.map +1 -0
- package/dist/video/video-source-browser.d.ts +37 -0
- package/dist/video/video-source-browser.d.ts.map +1 -0
- package/dist/video/video-source-browser.js +171 -0
- package/dist/video/video-source-browser.js.map +1 -0
- package/dist/video/video-source.d.ts +48 -0
- package/dist/video/video-source.d.ts.map +1 -0
- package/dist/video/video-source.js +2 -0
- package/dist/video/video-source.js.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AudioSource, SeekMode } from "./audio-source.js";
|
|
2
|
+
/**
|
|
3
|
+
* Preview-mode {@link AudioSource}: wraps an `HTMLAudioElement` and drives it in
|
|
4
|
+
* realtime. Ported from {@link BrowserVideoSource} minus the drawable/rvfc bits
|
|
5
|
+
* (audio produces no frames). A server renderer injects a different source via
|
|
6
|
+
* `AudioConfig.sourceFactory`.
|
|
7
|
+
*/
|
|
8
|
+
export declare class BrowserAudioSource implements AudioSource {
|
|
9
|
+
private readonly _el;
|
|
10
|
+
private _ready;
|
|
11
|
+
private readonly _readyCbs;
|
|
12
|
+
private _seeking;
|
|
13
|
+
private _pending;
|
|
14
|
+
constructor();
|
|
15
|
+
load(src: string): Promise<void>;
|
|
16
|
+
get duration(): number;
|
|
17
|
+
get currentTime(): number;
|
|
18
|
+
get isReady(): boolean;
|
|
19
|
+
seek(timeSeconds: number, mode?: SeekMode): Promise<void>;
|
|
20
|
+
/** Issue the pending seek if none is in flight; chains the latest target after each completes. */
|
|
21
|
+
private _drainSeek;
|
|
22
|
+
play(): Promise<void>;
|
|
23
|
+
pause(): void;
|
|
24
|
+
setMuted(muted: boolean): void;
|
|
25
|
+
setVolume(volume: number): void;
|
|
26
|
+
setPlaybackRate(rate: number): void;
|
|
27
|
+
onReady(cb: () => void): () => void;
|
|
28
|
+
destroy(): void;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=audio-source-browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-source-browser.d.ts","sourceRoot":"","sources":["../../../src/media/audio/audio-source-browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAM/D;;;;;GAKG;AACH,qBAAa,kBAAmB,YAAW,WAAW;IACpD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyB;IAKnD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAA+E;;IAa/F,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBhC,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,GAAE,QAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBpE,kGAAkG;IAClG,OAAO,CAAC,UAAU;IA6BlB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,KAAK,IAAI,IAAI;IAIb,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAI9B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAInC,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IASnC,OAAO,IAAI,IAAI;CAQhB"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preview-mode {@link AudioSource}: wraps an `HTMLAudioElement` and drives it in
|
|
3
|
+
* realtime. Ported from {@link BrowserVideoSource} minus the drawable/rvfc bits
|
|
4
|
+
* (audio produces no frames). A server renderer injects a different source via
|
|
5
|
+
* `AudioConfig.sourceFactory`.
|
|
6
|
+
*/
|
|
7
|
+
export class BrowserAudioSource {
|
|
8
|
+
_el;
|
|
9
|
+
_ready = false;
|
|
10
|
+
_readyCbs = new Set();
|
|
11
|
+
// Seek coalescing: keep at most one seek in flight. Rapid scrubbing only ever
|
|
12
|
+
// lands the latest target — intermediate ones are dropped — so the backlog
|
|
13
|
+
// never grows (which is what makes scrubbing a large clip degrade over time).
|
|
14
|
+
_seeking = false;
|
|
15
|
+
_pending = null;
|
|
16
|
+
constructor() {
|
|
17
|
+
// createElement (not `new Audio()`) dodges the global `Audio` constructor,
|
|
18
|
+
// which collides with our exported `Audio` class.
|
|
19
|
+
const el = document.createElement("audio");
|
|
20
|
+
el.crossOrigin = "anonymous";
|
|
21
|
+
el.preload = "auto";
|
|
22
|
+
// Muted so the browser allows programmatic play()/seek before any user gesture.
|
|
23
|
+
el.muted = true;
|
|
24
|
+
this._el = el;
|
|
25
|
+
}
|
|
26
|
+
load(src) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const onReady = () => {
|
|
29
|
+
cleanup();
|
|
30
|
+
this._ready = true;
|
|
31
|
+
for (const cb of this._readyCbs)
|
|
32
|
+
cb();
|
|
33
|
+
resolve();
|
|
34
|
+
};
|
|
35
|
+
const onErr = () => {
|
|
36
|
+
cleanup();
|
|
37
|
+
reject(new Error(`Failed to load audio: ${src}`));
|
|
38
|
+
};
|
|
39
|
+
const cleanup = () => {
|
|
40
|
+
this._el.removeEventListener("loadeddata", onReady);
|
|
41
|
+
this._el.removeEventListener("error", onErr);
|
|
42
|
+
};
|
|
43
|
+
this._el.addEventListener("loadeddata", onReady);
|
|
44
|
+
this._el.addEventListener("error", onErr);
|
|
45
|
+
this._el.src = src;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
get duration() {
|
|
49
|
+
return Number.isFinite(this._el.duration) ? this._el.duration : 0;
|
|
50
|
+
}
|
|
51
|
+
get currentTime() {
|
|
52
|
+
return this._el.currentTime;
|
|
53
|
+
}
|
|
54
|
+
get isReady() {
|
|
55
|
+
return this._ready;
|
|
56
|
+
}
|
|
57
|
+
seek(timeSeconds, mode = "precise") {
|
|
58
|
+
const clamped = Math.max(0, this.duration > 0 ? Math.min(this.duration, timeSeconds) : timeSeconds);
|
|
59
|
+
// No-op seek with nothing in flight: already there, resolve immediately.
|
|
60
|
+
if (!this._seeking && Math.abs(this._el.currentTime - clamped) <= 1e-4) {
|
|
61
|
+
return Promise.resolve();
|
|
62
|
+
}
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
if (this._pending) {
|
|
65
|
+
// Supersede the queued target — only the latest scrub position matters.
|
|
66
|
+
this._pending.time = clamped;
|
|
67
|
+
this._pending.mode = mode;
|
|
68
|
+
this._pending.resolvers.push(resolve);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
this._pending = { time: clamped, mode, resolvers: [resolve] };
|
|
72
|
+
}
|
|
73
|
+
this._drainSeek();
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/** Issue the pending seek if none is in flight; chains the latest target after each completes. */
|
|
77
|
+
_drainSeek() {
|
|
78
|
+
if (this._seeking || !this._pending)
|
|
79
|
+
return;
|
|
80
|
+
const { time, mode, resolvers } = this._pending;
|
|
81
|
+
this._pending = null;
|
|
82
|
+
if (Math.abs(this._el.currentTime - time) <= 1e-4) {
|
|
83
|
+
for (const r of resolvers)
|
|
84
|
+
r();
|
|
85
|
+
this._drainSeek();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this._seeking = true;
|
|
89
|
+
const onSeeked = () => {
|
|
90
|
+
this._el.removeEventListener("seeked", onSeeked);
|
|
91
|
+
this._seeking = false;
|
|
92
|
+
for (const r of resolvers)
|
|
93
|
+
r();
|
|
94
|
+
// A newer target may have arrived mid-seek — land it now.
|
|
95
|
+
this._drainSeek();
|
|
96
|
+
};
|
|
97
|
+
this._el.addEventListener("seeked", onSeeked);
|
|
98
|
+
// fastSeek jumps to the nearest keyframe — much snappier for scrubbing a
|
|
99
|
+
// large clip. Fall back to a precise currentTime seek when unavailable.
|
|
100
|
+
if (mode === "fast" && typeof this._el.fastSeek === "function") {
|
|
101
|
+
this._el.fastSeek(time);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
this._el.currentTime = time;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
play() {
|
|
108
|
+
return this._el.play();
|
|
109
|
+
}
|
|
110
|
+
pause() {
|
|
111
|
+
this._el.pause();
|
|
112
|
+
}
|
|
113
|
+
setMuted(muted) {
|
|
114
|
+
this._el.muted = muted;
|
|
115
|
+
}
|
|
116
|
+
setVolume(volume) {
|
|
117
|
+
this._el.volume = Math.max(0, Math.min(1, volume));
|
|
118
|
+
}
|
|
119
|
+
setPlaybackRate(rate) {
|
|
120
|
+
this._el.playbackRate = rate;
|
|
121
|
+
}
|
|
122
|
+
onReady(cb) {
|
|
123
|
+
if (this._ready) {
|
|
124
|
+
cb();
|
|
125
|
+
return () => { };
|
|
126
|
+
}
|
|
127
|
+
this._readyCbs.add(cb);
|
|
128
|
+
return () => this._readyCbs.delete(cb);
|
|
129
|
+
}
|
|
130
|
+
destroy() {
|
|
131
|
+
this._el.pause();
|
|
132
|
+
this._el.removeAttribute("src");
|
|
133
|
+
this._el.load();
|
|
134
|
+
this._readyCbs.clear();
|
|
135
|
+
this._pending = null;
|
|
136
|
+
this._seeking = false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=audio-source-browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-source-browser.js","sourceRoot":"","sources":["../../../src/media/audio/audio-source-browser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AudioBufferSink } from "mediabunny";
|
|
2
|
+
import type { AudioSource, SeekMode } from "./audio-source.js";
|
|
3
|
+
/**
|
|
4
|
+
* A preview {@link AudioSource} that also exposes a Mediabunny
|
|
5
|
+
* {@link AudioBufferSink}. The {@link PreviewAudioDriver} owns the Web Audio
|
|
6
|
+
* scheduling, so all it needs from the source is decoded buffers on demand — the
|
|
7
|
+
* old self-playing `play()/pause()/seek()` surface is inert here.
|
|
8
|
+
*/
|
|
9
|
+
export interface SchedulableAudioSource extends AudioSource {
|
|
10
|
+
/** Decoded-buffer sink for the primary audio track, or null until ready. */
|
|
11
|
+
readonly sink: AudioBufferSink | null;
|
|
12
|
+
/** Track start time in seconds (videos don't always start at 0). */
|
|
13
|
+
readonly firstTimestamp: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function isSchedulable(s: AudioSource): s is SchedulableAudioSource;
|
|
16
|
+
/**
|
|
17
|
+
* Preview-mode {@link AudioSource} backed by Mediabunny + WebCodecs. Replaces the
|
|
18
|
+
* `HTMLAudioElement`-based {@link BrowserAudioSource}: it only decodes — playback,
|
|
19
|
+
* volume, mute and rate are driven by the {@link PreviewAudioDriver}'s Web Audio
|
|
20
|
+
* scheduler, which reads {@link sink}.
|
|
21
|
+
*/
|
|
22
|
+
export declare class MediabunnyAudioSource implements SchedulableAudioSource {
|
|
23
|
+
private _input;
|
|
24
|
+
private _sink;
|
|
25
|
+
private _ready;
|
|
26
|
+
private _disposed;
|
|
27
|
+
private _duration;
|
|
28
|
+
private _firstTimestamp;
|
|
29
|
+
private readonly _readyCbs;
|
|
30
|
+
load(src: string): Promise<void>;
|
|
31
|
+
get sink(): AudioBufferSink | null;
|
|
32
|
+
get firstTimestamp(): number;
|
|
33
|
+
get duration(): number;
|
|
34
|
+
get currentTime(): number;
|
|
35
|
+
get isReady(): boolean;
|
|
36
|
+
seek(_timeSeconds: number, _mode?: SeekMode): Promise<void>;
|
|
37
|
+
play(): Promise<void>;
|
|
38
|
+
pause(): void;
|
|
39
|
+
setMuted(_muted: boolean): void;
|
|
40
|
+
setVolume(_volume: number): void;
|
|
41
|
+
setPlaybackRate(_rate: number): void;
|
|
42
|
+
onReady(cb: () => void): () => void;
|
|
43
|
+
destroy(): void;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=audio-source-mediabunny.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-source-mediabunny.d.ts","sourceRoot":"","sources":["../../../src/media/audio/audio-source-mediabunny.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,eAAe,EAAoB,MAAM,YAAY,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,WAAW,sBAAuB,SAAQ,WAAW;IACzD,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IACtC,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,WAAW,GAAG,CAAC,IAAI,sBAAsB,CAEzE;AAED;;;;;GAKG;AACH,qBAAa,qBAAsB,YAAW,sBAAsB;IAClE,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyB;IAE7C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBtC,IAAI,IAAI,IAAI,eAAe,GAAG,IAAI,CAEjC;IAED,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAGD,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAG3D,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAGrB,KAAK,IAAI,IAAI;IACb,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAC/B,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAChC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAEpC,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IASnC,OAAO,IAAI,IAAI;CAOhB"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ALL_FORMATS, AudioBufferSink, Input, UrlSource } from "mediabunny";
|
|
2
|
+
export function isSchedulable(s) {
|
|
3
|
+
return "sink" in s && s.sink !== undefined;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Preview-mode {@link AudioSource} backed by Mediabunny + WebCodecs. Replaces the
|
|
7
|
+
* `HTMLAudioElement`-based {@link BrowserAudioSource}: it only decodes — playback,
|
|
8
|
+
* volume, mute and rate are driven by the {@link PreviewAudioDriver}'s Web Audio
|
|
9
|
+
* scheduler, which reads {@link sink}.
|
|
10
|
+
*/
|
|
11
|
+
export class MediabunnyAudioSource {
|
|
12
|
+
_input = null;
|
|
13
|
+
_sink = null;
|
|
14
|
+
_ready = false;
|
|
15
|
+
_disposed = false;
|
|
16
|
+
_duration = 0;
|
|
17
|
+
_firstTimestamp = 0;
|
|
18
|
+
_readyCbs = new Set();
|
|
19
|
+
async load(src) {
|
|
20
|
+
const input = new Input({ formats: ALL_FORMATS, source: new UrlSource(src) });
|
|
21
|
+
this._input = input;
|
|
22
|
+
const track = await input.getPrimaryAudioTrack();
|
|
23
|
+
if (!track)
|
|
24
|
+
throw new Error(`[smoove] no audio track in: ${src}`);
|
|
25
|
+
if (!(await track.canDecode())) {
|
|
26
|
+
throw new Error(`[smoove] cannot decode audio track in: ${src}`);
|
|
27
|
+
}
|
|
28
|
+
if (this._disposed)
|
|
29
|
+
return;
|
|
30
|
+
this._duration = await track.computeDuration();
|
|
31
|
+
this._firstTimestamp = await track.getFirstTimestamp();
|
|
32
|
+
this._sink = new AudioBufferSink(track);
|
|
33
|
+
this._ready = true;
|
|
34
|
+
for (const cb of this._readyCbs)
|
|
35
|
+
cb();
|
|
36
|
+
}
|
|
37
|
+
get sink() {
|
|
38
|
+
return this._sink;
|
|
39
|
+
}
|
|
40
|
+
get firstTimestamp() {
|
|
41
|
+
return this._firstTimestamp;
|
|
42
|
+
}
|
|
43
|
+
get duration() {
|
|
44
|
+
return this._duration;
|
|
45
|
+
}
|
|
46
|
+
get currentTime() {
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
get isReady() {
|
|
50
|
+
return this._ready;
|
|
51
|
+
}
|
|
52
|
+
// Inert: the scheduler in PreviewAudioDriver drives playback off `sink`.
|
|
53
|
+
seek(_timeSeconds, _mode) {
|
|
54
|
+
return Promise.resolve();
|
|
55
|
+
}
|
|
56
|
+
play() {
|
|
57
|
+
return Promise.resolve();
|
|
58
|
+
}
|
|
59
|
+
pause() { }
|
|
60
|
+
setMuted(_muted) { }
|
|
61
|
+
setVolume(_volume) { }
|
|
62
|
+
setPlaybackRate(_rate) { }
|
|
63
|
+
onReady(cb) {
|
|
64
|
+
if (this._ready) {
|
|
65
|
+
cb();
|
|
66
|
+
return () => { };
|
|
67
|
+
}
|
|
68
|
+
this._readyCbs.add(cb);
|
|
69
|
+
return () => this._readyCbs.delete(cb);
|
|
70
|
+
}
|
|
71
|
+
destroy() {
|
|
72
|
+
this._disposed = true;
|
|
73
|
+
this._input?.dispose();
|
|
74
|
+
this._input = null;
|
|
75
|
+
this._sink = null;
|
|
76
|
+
this._readyCbs.clear();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=audio-source-mediabunny.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-source-mediabunny.js","sourceRoot":"","sources":["../../../src/media/audio/audio-source-mediabunny.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Environment } from "../../engine/environment.js";
|
|
2
|
+
import type { SeekMode } from "../video/video-source.js";
|
|
3
|
+
export type { SeekMode };
|
|
4
|
+
/**
|
|
5
|
+
* Runtime-agnostic audio source — the dependency-injection seam between the
|
|
6
|
+
* engine and the actual media decoder. The browser implementation
|
|
7
|
+
* ({@link BrowserAudioSource}) wraps an `HTMLAudioElement`. Mirrors
|
|
8
|
+
* {@link VideoSource} minus the drawable/dimension members (audio has no pixels).
|
|
9
|
+
*/
|
|
10
|
+
export interface AudioSource {
|
|
11
|
+
load(src: string): Promise<void>;
|
|
12
|
+
/** Duration in seconds, or 0 if unknown. */
|
|
13
|
+
readonly duration: number;
|
|
14
|
+
/** Current media playback position in seconds. */
|
|
15
|
+
readonly currentTime: number;
|
|
16
|
+
readonly isReady: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Seek to a time in seconds. `mode: "fast"` requests an approximate, snappy
|
|
19
|
+
* seek (nearest keyframe) for scrubbing; the default `"precise"` lands on the
|
|
20
|
+
* exact time.
|
|
21
|
+
*/
|
|
22
|
+
seek(timeSeconds: number, mode?: SeekMode): Promise<void>;
|
|
23
|
+
play(): Promise<void>;
|
|
24
|
+
pause(): void;
|
|
25
|
+
setMuted(muted: boolean): void;
|
|
26
|
+
setVolume(volume: number): void;
|
|
27
|
+
setPlaybackRate(rate: number): void;
|
|
28
|
+
/** Fires once when metadata is available. */
|
|
29
|
+
onReady(cb: () => void): () => void;
|
|
30
|
+
destroy(): void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Factory for an {@link AudioSource}. Receives the resolved {@link Environment}
|
|
34
|
+
* so a single factory can pick an implementation based on whether we are
|
|
35
|
+
* rendering or previewing.
|
|
36
|
+
*/
|
|
37
|
+
export type AudioSourceFactory = (env: Environment) => AudioSource;
|
|
38
|
+
//# sourceMappingURL=audio-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-source.d.ts","sourceRoot":"","sources":["../../../src/media/audio/audio-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,YAAY,EAAE,QAAQ,EAAE,CAAC;AAEzB;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,IAAI,CAAC;IACd,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,6CAA6C;IAC7C,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IACpC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-source.js","sourceRoot":"","sources":["../../../src/media/audio/audio-source.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import Konva from "konva";
|
|
2
|
+
import { type ReadonlySignal } from "../../engine/signal.js";
|
|
3
|
+
import type { AudioChannel, AudioMixer } from "./mixer.js";
|
|
4
|
+
import type { AudioConfig } from "./types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Timeline-driven audio. API mirrors Remotion's `<Audio>`
|
|
7
|
+
* (`trimBefore`/`trimAfter`/`loop`/`muted`/`volume`/`playbackRate`). It is an
|
|
8
|
+
* invisible {@link Konva.Group} so it lives in the `Sequence` tree and is
|
|
9
|
+
* discovered, range-gated, ticked, and mixer-registered exactly like
|
|
10
|
+
* {@link Video} — but it produces no pixels. Picks a {@link RenderingAudioDriver}
|
|
11
|
+
* or {@link PreviewAudioDriver} based on the composition's {@link Environment}.
|
|
12
|
+
*/
|
|
13
|
+
export declare class Audio extends Konva.Group implements AudioChannel {
|
|
14
|
+
/** Human label for mixer UIs — `config.name`, falling back to `config.src`. */
|
|
15
|
+
readonly label: string;
|
|
16
|
+
/** Intrinsic audio level, 0..1 — scaled by the composition mixer's master. */
|
|
17
|
+
readonly volume: ReadonlySignal<number>;
|
|
18
|
+
readonly muted: ReadonlySignal<boolean>;
|
|
19
|
+
private readonly _src;
|
|
20
|
+
private _source;
|
|
21
|
+
private readonly _env;
|
|
22
|
+
private readonly _factory;
|
|
23
|
+
private _suspended;
|
|
24
|
+
private readonly _trimBefore;
|
|
25
|
+
private readonly _trimAfter?;
|
|
26
|
+
private readonly _loop;
|
|
27
|
+
private readonly _playbackRate;
|
|
28
|
+
private readonly _volume;
|
|
29
|
+
private readonly _muted;
|
|
30
|
+
private _mixer;
|
|
31
|
+
private _driver;
|
|
32
|
+
constructor(config: AudioConfig);
|
|
33
|
+
/**
|
|
34
|
+
* Create the media source and start loading it. Called once at construction
|
|
35
|
+
* and again by {@link _kmResume} after {@link _kmSuspend} dropped it.
|
|
36
|
+
*/
|
|
37
|
+
private _acquireSource;
|
|
38
|
+
/** @internal — {@link Composition.suspend}: drop the source to stop downloading/decoding. */
|
|
39
|
+
_kmSuspend(): void;
|
|
40
|
+
/** @internal — {@link Composition.resume}: re-acquire the dropped source. */
|
|
41
|
+
_kmResume(): void;
|
|
42
|
+
/** @internal — called by Sequence on each tick while this audio is on-stage. */
|
|
43
|
+
_kmTick(localFrame: number): void;
|
|
44
|
+
/** @internal — called by Sequence when it goes out of range. */
|
|
45
|
+
_kmDeactivate(): void;
|
|
46
|
+
private _ensureDriver;
|
|
47
|
+
setMuted(muted: boolean): void;
|
|
48
|
+
setVolume(volume: number): void;
|
|
49
|
+
/** @internal — {@link AudioMixer} hands us the bus (or null on unregister). */
|
|
50
|
+
_bindMixer(mixer: AudioMixer | null): void;
|
|
51
|
+
/** @internal — push effective level (master × intrinsic) to the source. */
|
|
52
|
+
_applyAudio(): void;
|
|
53
|
+
setPlaybackRate(rate: number): void;
|
|
54
|
+
destroy(): this;
|
|
55
|
+
}
|
|
56
|
+
export declare function isAudioNode(node: Konva.Node): node is Audio;
|
|
57
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/media/audio/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,EAAgB,KAAK,cAAc,EAAe,MAAM,wBAAwB,CAAC;AAOxF,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;GAOG;AACH,qBAAa,KAAM,SAAQ,KAAK,CAAC,KAAM,YAAW,YAAY;IAC5D,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAExC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAE9B,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,OAAO,CAA4B;gBAE/B,MAAM,EAAE,WAAW;IAwB/B;;;OAGG;IACH,OAAO,CAAC,cAAc;IAStB,6FAA6F;IAC7F,UAAU,IAAI,IAAI;IAQlB,6EAA6E;IAC7E,SAAS,IAAI,IAAI;IAMjB,gFAAgF;IAChF,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAKjC,gEAAgE;IAChE,aAAa,IAAI,IAAI;IAIrB,OAAO,CAAC,aAAa;IAwCrB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAK9B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK/B,+EAA+E;IAC/E,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI;IAK1C,2EAA2E;IAC3E,WAAW,IAAI,IAAI;IAQnB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI1B,OAAO,IAAI,IAAI;CAQzB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,GAAG,IAAI,IAAI,KAAK,CAE3D"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import Konva from "konva";
|
|
2
|
+
import { getComposition } from "../../engine/composition.js";
|
|
3
|
+
import { detectEnvironment, getEnvironment } from "../../engine/environment.js";
|
|
4
|
+
import { getDefaultAudioSourceFactory } from "../../engine/runtime-defaults.js";
|
|
5
|
+
import { createSignal } from "../../engine/signal.js";
|
|
6
|
+
import { AUDIO_MARK, MEDIA_MARK } from "../media-marker.js";
|
|
7
|
+
import { PreviewAudioDriver } from "./audio-for-preview.js";
|
|
8
|
+
import { RenderingAudioDriver } from "./audio-for-rendering.js";
|
|
9
|
+
/**
|
|
10
|
+
* Timeline-driven audio. API mirrors Remotion's `<Audio>`
|
|
11
|
+
* (`trimBefore`/`trimAfter`/`loop`/`muted`/`volume`/`playbackRate`). It is an
|
|
12
|
+
* invisible {@link Konva.Group} so it lives in the `Sequence` tree and is
|
|
13
|
+
* discovered, range-gated, ticked, and mixer-registered exactly like
|
|
14
|
+
* {@link Video} — but it produces no pixels. Picks a {@link RenderingAudioDriver}
|
|
15
|
+
* or {@link PreviewAudioDriver} based on the composition's {@link Environment}.
|
|
16
|
+
*/
|
|
17
|
+
export class Audio extends Konva.Group {
|
|
18
|
+
/** Human label for mixer UIs — `config.name`, falling back to `config.src`. */
|
|
19
|
+
label;
|
|
20
|
+
/** Intrinsic audio level, 0..1 — scaled by the composition mixer's master. */
|
|
21
|
+
volume;
|
|
22
|
+
muted;
|
|
23
|
+
_src;
|
|
24
|
+
// Reassigned on suspend/resume, so not readonly; `!` = set via _acquireSource().
|
|
25
|
+
_source;
|
|
26
|
+
_env;
|
|
27
|
+
_factory;
|
|
28
|
+
_suspended = false;
|
|
29
|
+
_trimBefore;
|
|
30
|
+
_trimAfter;
|
|
31
|
+
_loop;
|
|
32
|
+
_playbackRate;
|
|
33
|
+
_volume;
|
|
34
|
+
_muted;
|
|
35
|
+
_mixer = null;
|
|
36
|
+
_driver = null;
|
|
37
|
+
constructor(config) {
|
|
38
|
+
super({ listening: false, visible: false, id: config.id });
|
|
39
|
+
this.setAttr(MEDIA_MARK, true);
|
|
40
|
+
this.setAttr(AUDIO_MARK, true);
|
|
41
|
+
this._src = config.src;
|
|
42
|
+
this._trimBefore = config.trimBefore ?? config.startFrom ?? 0;
|
|
43
|
+
this._trimAfter = config.trimAfter ?? config.endAt;
|
|
44
|
+
this._loop = config.loop ?? false;
|
|
45
|
+
this._playbackRate = config.playbackRate ?? 1;
|
|
46
|
+
this.label = config.name ?? config.src;
|
|
47
|
+
this._volume = createSignal(config.volume ?? 1);
|
|
48
|
+
this._muted = createSignal(config.muted ?? false);
|
|
49
|
+
this.volume = this._volume;
|
|
50
|
+
this.muted = this._muted;
|
|
51
|
+
// Build the source eagerly so loading starts immediately. The driver (which
|
|
52
|
+
// needs the attached stage's composition) is resolved lazily on first tick.
|
|
53
|
+
this._env = detectEnvironment();
|
|
54
|
+
this._factory = config.sourceFactory ?? getDefaultAudioSourceFactory();
|
|
55
|
+
this._acquireSource();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Create the media source and start loading it. Called once at construction
|
|
59
|
+
* and again by {@link _kmResume} after {@link _kmSuspend} dropped it.
|
|
60
|
+
*/
|
|
61
|
+
_acquireSource() {
|
|
62
|
+
this._source = this._factory(this._env);
|
|
63
|
+
this._applyAudio(); // honor config/mixer volume+muted before playback
|
|
64
|
+
this._source.setPlaybackRate(this._playbackRate);
|
|
65
|
+
this._source.load(this._src).catch((err) => {
|
|
66
|
+
console.error("[smoove] Audio load failed:", err);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** @internal — {@link Composition.suspend}: drop the source to stop downloading/decoding. */
|
|
70
|
+
_kmSuspend() {
|
|
71
|
+
if (this._suspended)
|
|
72
|
+
return;
|
|
73
|
+
this._suspended = true;
|
|
74
|
+
this._driver?.dispose();
|
|
75
|
+
this._driver = null;
|
|
76
|
+
this._source.destroy();
|
|
77
|
+
}
|
|
78
|
+
/** @internal — {@link Composition.resume}: re-acquire the dropped source. */
|
|
79
|
+
_kmResume() {
|
|
80
|
+
if (!this._suspended)
|
|
81
|
+
return;
|
|
82
|
+
this._suspended = false;
|
|
83
|
+
this._acquireSource();
|
|
84
|
+
}
|
|
85
|
+
/** @internal — called by Sequence on each tick while this audio is on-stage. */
|
|
86
|
+
_kmTick(localFrame) {
|
|
87
|
+
const driver = this._ensureDriver();
|
|
88
|
+
driver?.tick(localFrame);
|
|
89
|
+
}
|
|
90
|
+
/** @internal — called by Sequence when it goes out of range. */
|
|
91
|
+
_kmDeactivate() {
|
|
92
|
+
this._driver?.deactivate();
|
|
93
|
+
}
|
|
94
|
+
_ensureDriver() {
|
|
95
|
+
if (this._driver)
|
|
96
|
+
return this._driver;
|
|
97
|
+
const stage = this.getStage();
|
|
98
|
+
if (!stage)
|
|
99
|
+
return null;
|
|
100
|
+
const comp = getComposition(stage);
|
|
101
|
+
if (!comp)
|
|
102
|
+
return null;
|
|
103
|
+
// Lazy fallback for audio added after its sequence was registered.
|
|
104
|
+
comp.mixer.register(this);
|
|
105
|
+
const timing = {
|
|
106
|
+
fps: comp.fps,
|
|
107
|
+
trimBefore: this._trimBefore,
|
|
108
|
+
trimAfter: this._trimAfter,
|
|
109
|
+
loop: this._loop,
|
|
110
|
+
playbackRate: this._playbackRate,
|
|
111
|
+
};
|
|
112
|
+
const ctx = {
|
|
113
|
+
source: this._source,
|
|
114
|
+
timing,
|
|
115
|
+
comp,
|
|
116
|
+
id: this.id() || `audio-${this._id}`,
|
|
117
|
+
src: this._src,
|
|
118
|
+
effectiveVolume: () => {
|
|
119
|
+
const m = this._mixer;
|
|
120
|
+
const master = m ? m.volume.get() : 1;
|
|
121
|
+
return master * this._volume.get();
|
|
122
|
+
},
|
|
123
|
+
effectiveMuted: () => {
|
|
124
|
+
const m = this._mixer;
|
|
125
|
+
const masterMuted = m ? m.muted.get() : false;
|
|
126
|
+
return masterMuted || this._muted.get();
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
this._driver = getEnvironment(stage).isRendering
|
|
130
|
+
? new RenderingAudioDriver(ctx)
|
|
131
|
+
: new PreviewAudioDriver(ctx);
|
|
132
|
+
return this._driver;
|
|
133
|
+
}
|
|
134
|
+
setMuted(muted) {
|
|
135
|
+
this._muted.set(muted);
|
|
136
|
+
this._applyAudio();
|
|
137
|
+
}
|
|
138
|
+
setVolume(volume) {
|
|
139
|
+
this._volume.set(Math.max(0, Math.min(1, volume)));
|
|
140
|
+
this._applyAudio();
|
|
141
|
+
}
|
|
142
|
+
/** @internal — {@link AudioMixer} hands us the bus (or null on unregister). */
|
|
143
|
+
_bindMixer(mixer) {
|
|
144
|
+
this._mixer = mixer;
|
|
145
|
+
this._applyAudio();
|
|
146
|
+
}
|
|
147
|
+
/** @internal — push effective level (master × intrinsic) to the source. */
|
|
148
|
+
_applyAudio() {
|
|
149
|
+
const m = this._mixer;
|
|
150
|
+
const master = m ? m.volume.get() : 1;
|
|
151
|
+
const masterMuted = m ? m.muted.get() : false;
|
|
152
|
+
this._source.setVolume(master * this._volume.get());
|
|
153
|
+
this._source.setMuted(masterMuted || this._muted.get());
|
|
154
|
+
}
|
|
155
|
+
setPlaybackRate(rate) {
|
|
156
|
+
this._source.setPlaybackRate(rate);
|
|
157
|
+
}
|
|
158
|
+
destroy() {
|
|
159
|
+
this._mixer?.unregister(this);
|
|
160
|
+
this._mixer = null;
|
|
161
|
+
this._driver?.dispose();
|
|
162
|
+
this._driver = null;
|
|
163
|
+
this._source.destroy();
|
|
164
|
+
return super.destroy();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
export function isAudioNode(node) {
|
|
168
|
+
return node.getAttr(AUDIO_MARK) === true;
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/media/audio/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type ReadonlySignal } from "../../engine/signal.js";
|
|
2
|
+
/**
|
|
3
|
+
* A registered audio source the mixer can re-apply. Implemented by {@link Video}.
|
|
4
|
+
* The channel owns its intrinsic level; the mixer owns the master that scales it.
|
|
5
|
+
*/
|
|
6
|
+
export interface AudioChannel {
|
|
7
|
+
/** Human label for mixer UIs. */
|
|
8
|
+
readonly label: string;
|
|
9
|
+
/** Intrinsic level, 0..1 — independent of the master. */
|
|
10
|
+
readonly volume: ReadonlySignal<number>;
|
|
11
|
+
readonly muted: ReadonlySignal<boolean>;
|
|
12
|
+
setVolume(v: number): void;
|
|
13
|
+
setMuted(m: boolean): void;
|
|
14
|
+
/** @internal — mixer hands the channel its bus (or null on unregister). */
|
|
15
|
+
_bindMixer(mixer: AudioMixer | null): void;
|
|
16
|
+
/** @internal — mixer calls this when the master volume/mute changes. */
|
|
17
|
+
_applyAudio(): void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Composition-level audio bus. Holds a master volume + mute that multiply into
|
|
21
|
+
* every registered {@link AudioChannel}: a channel's effective output is
|
|
22
|
+
* `master × channel.volume`, muted when `masterMuted || channel.muted`. The
|
|
23
|
+
* mixer never touches a source directly — on any change it asks each channel to
|
|
24
|
+
* re-apply its own audio.
|
|
25
|
+
*/
|
|
26
|
+
export declare class AudioMixer {
|
|
27
|
+
/** Master level, 0..1. */
|
|
28
|
+
readonly volume: ReadonlySignal<number>;
|
|
29
|
+
readonly muted: ReadonlySignal<boolean>;
|
|
30
|
+
private readonly _volume;
|
|
31
|
+
private readonly _muted;
|
|
32
|
+
private readonly _channels;
|
|
33
|
+
constructor();
|
|
34
|
+
setVolume(v: number): void;
|
|
35
|
+
setMuted(m: boolean): void;
|
|
36
|
+
/** Register a channel. Idempotent — the Set dedupes by instance. */
|
|
37
|
+
register(ch: AudioChannel): void;
|
|
38
|
+
unregister(ch: AudioChannel): void;
|
|
39
|
+
/** Snapshot of registered channels — for building per-track UIs. */
|
|
40
|
+
get channels(): AudioChannel[];
|
|
41
|
+
private _reapplyAll;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=mixer.d.ts.map
|